Claude Codeに毎朝GitHubを巡回させる ― 有用なOSSとスキルを自動採点
前回、コスト枠で無人ループの暴走を止めた話を書きました。今回はそのautopilotが毎朝走らせているジョブの1つ ―― GitHubを巡回してスキル/OSS/トレンドを発見し、Claude自身に採点させて、要対応だけをブリーフに差し込む github-scout.sh の話です。
困りごと:見るべきGitHubを見る時間がない
有用なOSSやClaude Codeスキルは日々GitHubに増えていますが、自分で topic:claude-code を巡回して stars を見て README を読んで……という作業は、続けようとすると必ずサボります。かといって毎朝の巡回を新しいジョブとして立てると、launchd のエントリがまた1つ増え、監視対象も1つ増えます。
すでに毎朝 vault-auto-ingest.sh が動いていたので、新しいジョブを作らず、既存の朝のパイプラインに1ステップ相乗りさせることにしました。
全体像:CRAWL → DEDUP → ENRICH → SCORE+ROUTE+INGEST → LEDGER
github-scout.sh 冒頭のコメントがそのままパイプライン設計です。
# 設計:
# CRAWL(gh=無料) -> DEDUP(shell) -> ENRICH(gh contents) -> SCORE+ROUTE+INGEST(claude -p=MAX枠) -> LEDGER
# 出力:
# - ブリーフ差し込み用セクション: ~/.claude/logs/github-scout-latest.md
# (vault-auto-ingest.sh が today-brief.md に追記し Desktop ブリーフに載せる)
# - 安全なスキル(md のみ/read系権限): ~/.claude/skills/auto/<name>/ に自動有効化
# - Bash/script 同梱スキル: ~/.claude/skills/auto/.incoming/<name>/ に隔離 + 要対応フラグ
# - OSS/トレンド候補: ~/.claude/scout/watchlist.md に追記(cloneしない)
# コスト: crawl/dedup/ledger は ¥0。採点のみ claude -p(MAX 定額)。候補は上位 N にcap。
肝は「課金が発生するのは採点の1ステップだけ」に閉じ込めていることです。gh search repos によるクロールと重複除外はすべて無料のshellで済ませ、Claudeを呼ぶのは絞り込んだ後の1回だけにしています。
1. CRAWL — 3系統・12クエリ
検索は lane ごとに3系統に分けています。
# A: Claude Code スキル/プラグイン(本丸)
run_search skill "A1 topic:claude-code" --topic claude-code --sort stars --order desc --limit 25
run_search skill "A2 claude code skills" "claude code skills" --sort stars --order desc --limit 20
run_search skill "A3 claude agent skill" "claude agent skill SKILL.md" --sort updated --order desc --limit 20
# B: 自分の開発に直結する OSS。全語ANDだと0件になるのでコア語+言語+star下限に緩める(v2: 実測調整)
run_search project "B1 chrome autofill" "autofill" --language JavaScript --stars ">100" --sort stars --order desc --limit 12
...
# C: トレンド横断 — created/updated はクエリ文字列だと無視されるので gh ネイティブflagを使う
run_search trend "C1 breakout 90d" --created ">$D90" --stars ">800" --sort stars --order desc --limit 20
run_search trend "C2 hot now" --updated ">$D3" --stars ">3000" --sort updated --order desc --limit 20
Bレーンは「全語AND」で組むとほぼ0件になったので、コア語1つ+言語+star下限まで緩めています。Cレーンの --created / --updated は最初クエリ文字列に混ぜていて無視されていました。gh のネイティブフラグに切り替えて初めて効くようになった経緯があります。
2〜3. DEDUP / ENRICH — Claudeに渡す前に絞る
seen.tsv(過去に見た repo full name の台帳)と既存の auto/ .incoming/ の名前でまず重複を弾き、stars 降順で MAX_CANDIDATES=30 件に切ります。skillレーンだけ、上位 MAX_ENRICH=8 件に絞って SKILL.md の中身とルートのファイル一覧を追加取得します。
if echo "$files" | grep -qiE '\.sh,|\.js,|hooks,|scripts,|install|setup\.'; then has_script="true"; fi
ここで has_script フラグを立てておくのが、次の採点ステップでの安全ルーティングの下準備になります。
4. SCORE + ROUTE + INGEST — ここだけがclaude -p
絞り込んだJSONLをまとめて claude -p に渡し、採点と同時にファイル操作まで行わせます。安全判定はこうプロンプトに書いています。
## A) スキルの自動取り込み (lane=skill で skillmd が空でない & ★4以上)
安全判定:
- SKILL.md frontmatter の allowed-tools が無い、または Read/Grep/Glob/WebFetch/WebSearch のみ → 【安全】
- has_script=true、または allowed-tools に Bash/Write/Edit/Execute 等が含まれる → 【要レビュー】
- 【安全】: ディレクトリ $AUTO/<kebab名>/ を作り SKILL.md を書く。先頭に必ず以下の provenance を足す:
- 【要レビュー】: $INCOMING/<kebab名>/ に SKILL.md を置く(有効化しない)。
Read/Grep/Glob/WebFetch/WebSearchだけのスキルは即有効化、Bashやスクリプト同梱は .incoming/ に隔離して自分の確認待ち、という2段構えです。取り込むスキルには必ず出典コメントを先頭に埋めるので、後から「これどこから来た」を追える。
出力は各セクション最大5件、★3未満は載せません。ノイズを増やさないための一番効くルールがこの2つでした。
5. LEDGER — 採点成功時だけ台帳に刻む
if [ "${SCORE_OK:-1}" -eq 1 ]; then
while IFS= read -r line; do
nm=$(echo "$line" | "$JQ" -r '.name')
grep -qxF "$nm" "$SEEN_SET" 2>/dev/null || printf '%s\t%s\n' "$nm" "$TODAY" >> "$SEEN"
done < "$TOP"
else
log "採点未成立(SCORE_OK=0) — seen 非commit。次回巡回で再採点させる。"
fi
採点が失敗(Claude MAXの枠切れ含む)したら台帳に書きません。そうすると同じ候補が翌朝また流れてきて再採点される ―― 失敗時のリトライを、リトライ専用コードなしで台帳の未更新だけで実現しています。
相乗りの仕組み:新しいlaunchdエントリを作らない
github-scout.sh は単独のジョブではなく、既存の vault-auto-ingest.sh(4:55/8:15/10:15/12:15にlaunchdから起動)の中の1ステップとして呼ばれています。
# 2.55 GitHub Scout 巡回 → 発見/要対応をブリーフに差し込む。scout本体は ~/.claude(保護外)のみ書く。
# ここで today-brief.md に追記 → 2.6 の Desktop コピーに載る。失敗してもブリーフ生成は継続。
run_to 900 bash "$HOME/.claude/scripts/github-scout.sh" >> "$LOG" 2>&1 || echo "[$(date '+%F %T')] github-scout 失敗(継続)" >> "$LOG"
SCOUT_SEC="$HOME/.claude/logs/github-scout-latest.md"
BRIEF_SRC="$VAULT/wiki/today-brief.md"
if [ -s "$SCOUT_SEC" ] && [ "$SCOUT_SEC" -nt "$START_STAMP" ] && [ -s "$BRIEF_SRC" ] && [ "$BRIEF_SRC" -nt "$START_STAMP" ]; then
{ echo ""; echo "---"; echo ""; cat "$SCOUT_SEC"; } >> "$BRIEF_SRC"
fi
ポイントは2つです。
run_to 900でタイムアウトを切り、失敗しても||で継続。ブリーフ生成全体をscoutの不調で止めない。- 追記前に鮮度チェック(
-nt "$START_STAMP")。今回のランより古いファイルなら追記しない ―― 前回実行分の使い回しをブリーフに混ぜないためです。
なぜscout本体は ~/.claude 配下しか書かないのか
Vault(claude-obsidian)はiCloud同期下にあり、launchd からそこに書き込むには /bin/bash に「フルディスクアクセス」をGUIで1回付与する必要があります。vault-auto-ingest.sh はすでにその権限下で動いているので、Vaultへの追記はそちら(cat "$SCOUT_SEC" >> "$BRIEF_SRC")に任せ、github-scout.sh 自身は ~/.claude/logs ~/.claude/skills ~/.claude/scout という保護境界の外側だけしか触りません。
scoutをVault直書きにしなかったのは権限の節約が目的ではなく、**「保護領域に触れる権限を持つコードを1箇所に閉じる」**ためです。scoutが独立して動く限り、権限昇格を気にせず単体でテスト・再実行できます。
踏んだ落とし穴
claude -pが空briefを返し、ブリーフのScout欄が無言で空白化(ヘッダ35バイトのみ)→ TOP候補→watchlist直近→明示失敗メッセージの3段フォールバックを実装grep -cは0件でも文字列"0"を返しつつexit 1する →grep -c ... || echo 0は"0\n0"という2行を生んで整数比較を壊すfootgun。|| echo 0を付けないのが正解- Claude MAXの枠切れを採点失敗と区別できず🚨誤報→
session limit文字列を専用検出し、reset時刻が8分以内なら1回だけ待ってリトライ、遠ければ台帳を更新せず次回巡回に自動的に委ねる --created/--updatedをクエリ文字列に混ぜても無視される →gh search reposのネイティブフラグとして渡す- 全語ANDの検索クエリはほぼ0件 → コア語1つ+言語+star下限まで緩めて実測調整
まとめ
- クロール・重複除外・台帳更新は無料shellで、課金が発生するのはClaude採点の1ステップだけに閉じ込める
- 安全なスキル(Read/Grep/Glob/WebFetch/WebSearchのみ)は即自動有効化、Bash同梱は隔離ディレクトリで人間の確認待ち
- ★3未満は非表示・各セクション最大5件で、ノイズは採点ルールで絞る
- 新しい
launchdエントリを増やさず、既存の朝バッチに1ステップ相乗りさせる - 保護領域(iCloud Vault)に書く権限は1箇所に閉じ、scout本体は保護外だけを触る設計にする
- 採点失敗時は台帳を更新しない、それだけで専用のリトライコードなしに再試行が成立する
次は、このscoutが拾ってくる候補の「質」をどう測っているか ―― 採点そのものの精度チェックについて書く予定です。
Lily(@bokuwalily)― 個人開発者。Claude Code で自動化基盤を組みながら、iOSアプリやWebサービスを量産しています
- AIで「寝てても回る仕組み」を作って月120万にした話は noteの有料記事 に💰
- OSS: github.com/bokuwalily 🐙
- 最新情報・お問い合わせは X @bokuwalily へ🌍
- AI導入・自動化の相談と実装テンプレ7本の配布は 公式LINE から💬
皆さんの ❤️ やシェアが励みになります!