cd /news/developer-tools/sending-claude-code-on-a-daily-githu… · home topics developer-tools article
[ARTICLE · art-56674] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Sending Claude Code on a Daily GitHub Patrol — Auto-Scoring Useful OSS and Skills

A developer built github-scout.sh, a script that runs daily to patrol GitHub, auto-score useful open-source software and Claude Code skills, and inject findings into a morning brief. The script separates crawling (free) from scoring (paid Claude API) to keep costs fixed, mechanically judges skill safety by file layout, and retries failed scoring the next day. It searches across three lanes—Claude Code skills, personal OSS needs, and trending repos—and outputs only to the tooling directory without touching protected areas.

read9 min views1 publishedJul 13, 2026

I kept meaning to browse GitHub for good tooling, but "whenever I felt like it" never turned into a habit. In the previous post, "Letting Claude Code Autonomously Improve Itself Unattended," I built the skeleton of a morning brief. This time I'll write about github-scout.sh

, which piggybacks on that brief-generation job: every morning it patrols GitHub, auto-scores useful OSS and skills, and injects only the items that need attention into the brief on my desktop.

There are three design points. First, separate crawling (gh

, free) from scoring (claude -p

, MAX plan quota) so cost stays fixed. Second, mechanically judge a skill's safety by its file layout and route it into either auto-enablement or a review-required quarantine. Third, when scoring fails, don't stamp the seen

ledger — leave it for the next morning's automatic retry.

Some Claude Code skills are community-made and scattered across GitHub. The same goes for reference OSS for job-hunt trackers or Chrome extensions. But "manually searching when the mood strikes" isn't reproducible. I'd hit the same popular repos every time, and forget last week's discovery by the following week.

I can't count how much waste came from missing a useful skill and rewriting the same thing myself. The mechanism for a morning brief already exists. Automatically injecting GitHub discoveries into it was the shortest path to a fix.

It's written right there in the script's header comment.

Cost is incurred only in the scoring phase. By capping the number of candidates, I fix the token consumption of scoring.

MAX_CANDIDATES=30   # claude へ渡す上位件数(コスト固定化)
MAX_ENRICH=8        # SKILL.md 中身を取りに行く skill 候補の上限
TIMEOUT_SEC=600

The output destination is only under ~/.claude/

. It never writes directly into the Vault (an iCloud-synced, TCC-protected area). This follows the same "only touch my own tooling directory outside protected areas" design principle as autopilot, and it's spelled out in the script's comments too.

I run gh search repos

across 3 purpose-specific lanes. The lanes carry straight through into the later routing.

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

run_search project "B1 chrome autofill"     "autofill" --language JavaScript --stars ">100" --sort stars --order desc --limit 12
run_search project "B3 turso"               "turso" --language TypeScript --stars ">50" --sort updated --order desc --limit 12
run_search project "B5 job tracker"         "job tracker" --stars ">30" --sort stars --order desc --limit 12

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

Each run_search

records a

in the coverage table on failure and keeps going. It doesn't stop even if not all sources succeed. As a rate-limit countermeasure, I put a sleep 1

after each search (GitHub Search API: 30 req/min).

Note:The B-series queries initially ANDed together every word across multiple topics, and zero-hit results kept piling up. As the commentv2: 2026-06-10実測調整

records, I loosened them to core term + language + star floor. Compound queries can only be dialed in by trying them.

The run_search

function appends lane-labeled JSONL to raw.jsonl

.

run_search() {
  local lane="$1"; shift; local label="$1"; shift
  local json
  json=$("$GH" search repos "$@" --json fullName,description,stargazersCount,url,language,pushedAt,createdAt 2>>"$LOG")
  if [ -n "$json" ] && echo "$json" | "$JQ" -e 'type=="array"' >/dev/null 2>&1; then
    n=$(echo "$json" | "$JQ" 'length')
    echo "$json" | "$JQ" -c --arg lane "$lane" '.[] | {lane:$lane, name:.fullName, ...}' >> "$RAW"
    COVERAGE="${COVERAGE}\n| ${label} | ✅ | ${n} |"
  else
    COVERAGE="${COVERAGE}\n| ${label} | ❌ | 0 |"
  fi
  sleep 1
}

Using ~/.claude/scout/seen.tsv

(repo fullName, scoring date), I filter out anything already scored, and I also apply weak duplicate detection against the existing skill names under auto/

.

SEEN_SET="$WORK/seen.set"
cut -f1 "$SEEN" 2>/dev/null | sort -u > "$SEEN_SET"

"$JQ" -s -c 'unique_by(.name) | sort_by(-.stars) | .[]' "$RAW" 2>/dev/null \
  | while IFS= read -r line; do
      nm=$(echo "$line" | "$JQ" -r '.name')
      grep -qxF "$nm" "$SEEN_SET" 2>/dev/null && continue
      echo "$line" >> "$FRESH"
    done

unique_by(.name) | sort_by(-.stars)

dedupes, then orders by stars descending before taking the diff. Narrowing to the top MAX_CANDIDATES=30

is the point where the cost ceiling is fixed.

To raise scoring quality, I fetch the root SKILL.md

contents and file listing only for the top MAX_ENRICH=8

items where lane=skill

. Everything else is scored on metadata alone (stars/desc/language).

if [ "$lane" = "skill" ] && [ "$enr_count" -lt "$MAX_ENRICH" ]; then
  enr_count=$((enr_count+1))
  skillmd=$("$GH" api "repos/$name/contents/SKILL.md" \
    --jq '.content' 2>/dev/null | base64 -d 2>/dev/null | head -c 6000)
  files=$("$GH" api "repos/$name/contents" \
    --jq '.[].name' 2>/dev/null | tr '\n' ',' | head -c 500)
  if echo "$files" | grep -qiE '\.sh,|\.js,|hooks,|scripts,|install|setup\.'; then
    has_script="true"
  fi
fi

The has_script

flag set here is used by the later safety decision.

I pass the ENRICHed candidates together into a prompt and instruct it to score ★1-5 and perform file operations at the same time. Routing goes down 3 paths.

A) Auto-adopting a skill (lane=skill, skillmd present, ★4 or higher)

The safety rules in the prompt become the logic verbatim.

安全判定:
  - allowed-tools が無い、または Read/Grep/Glob/WebFetch/WebSearch のみ → 【安全】
  - has_script=true、または allowed-tools に Bash/Write/Edit 等が含まれる → 【要レビュー】
- 【安全】: ~/.claude/skills/auto/<kebab名>/ を作り SKILL.md を書く。
            先頭に provenance を必ず足す:
            <!-- source: github-scout | repo: <name> | url: <url> | adopted: DATE -->
            既存の auto/ に同名/酷似スキルがあればスキップ(重複禁止)。fork/mirror は取り込まない。
- 【要レビュー】: .incoming/<kebab名>/ に SKILL.md を置く(有効化しない)。
                  ブリーフの『要対応』に列挙。

Note:Skills written intoauto/

are automatically enabled at the next session startup..incoming/

is not enabled. If I want to enable one, the design is to promote it after review with/scout-promote <name>

. Anything bundled with scripts (has_script=true) is always reviewed by a human.

B) OSS/project candidates (lane=project, or lane=skill with empty skillmd, ★4 or higher)

Just append to ~/.claude/scout/watchlist.md

. The format is - [name](url) ★N — reason in 30 chars or fewer (DATE)

. No cloning.

C) Trends (lane=trend, ★3 or higher)

Just a single line in the brief. No file operations.

The section injected into the brief looks like this.

## 🔭 GitHub Scout (2026-06-17)
### ⚡ 自動で入れたスキル
- foo-skill ★4 — 何ができるか1行(有効化済み)
### ⚠️ 要対応(あなたの確認待ち)
- bar-skill ★5 — 何ができるか / なぜ要レビューか / 有効化するなら: /scout-promote bar-skill
### 💡 採用候補(OSS) — watchlist追記済み
- baz-repo ★4 — 刺さりどころ1行
### 📈 トレンド横断
- trending-repo ★3 — 何が新しいか1行

Scout is not its own independent launchd job. It's called as step 2.55, immediately after brief generation (step 2.5) in vault-auto-ingest.sh

.

run_to 900 bash "$HOME/.claude/scripts/github-scout.sh" >> "$LOG" 2>&1 \
  || echo "[$(ts)] 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

On failure, || echo "...(継続)"

lets it move on to the next step. It doesn't halt the whole brief generation.

A freshness check (-nt "$START_STAMP"

) prevents an old file from being mistakenly appended. vault-auto-ingest.sh

fires across multiple slots at 4:55 / 8:15 / 10:15 / 12:15, but after success a DONE_MARKER

makes subsequent slots return immediately, so once the whole brief (scout included) succeeds once, it isn't run redundantly.

What controls what happens in the scoring phase is the SCORE_OK

flag and SESSION_LIMIT

detection.

SESSION_LIMIT=0
if [ "$rc" -ne 0 ] && grep -qi "session limit" "$RUN_OUT" 2>/dev/null; then
  SESSION_LIMIT=1
  if [ "$wait_s" -ge 0 ] && [ "$wait_s" -le 480 ]; then
    log "session limit — ${wait_s}s 待って1回リトライ (reset=${RESET_STR:-?})"
    sleep "$wait_s"
    run_claude; rc=$?
  else
    log "session limit — reset 遠い/不明(${wait_s}s)。待たず seen 非commit で次回巡回に委ねる"
  fi
fi

When the scoring result is thin (fewer than one -

line), I set SCORE_OK=0

and enter a 3-stage fallback.

CONTENT_LINES=$(grep -cE '^- ' "$OUT" 2>/dev/null); CONTENT_LINES=${CONTENT_LINES:-0}
if [ "${CONTENT_LINES:-0}" -lt 1 ]; then
  SCORE_OK=0
  FB_LIST=$(head -n 8 "$TOP" | "$JQ" -r \
    '"- [\(.name // "?")](\(.url // "")) \((.stars // 0))⭐ [\(.lane // "?")] — \((.desc // "")[0:60])"')
fi

In the final LEDGER step, I stamp seen.tsv

only on scoring success.

if [ "${SCORE_OK:-1}" -eq 1 ]; then
  while IFS= read -r line; do
    nm=$(echo "$line" | "$JQ" -r '.name')
    printf '%s\t%s\n' "$nm" "$TODAY" >> "$SEEN"
  done < "$TOP"
else
  log "採点未成立(SCORE_OK=0) — seen 非commit。次回巡回で再採点させる。"
fi

When SESSION_LIMIT hits and the reset is far off, it's non-committed just like SCORE_OK=0

. The same candidates automatically resurface in the next morning's patrol.

A per-source coverage table is attached in a <details>

at the end of the brief.

<details><summary>scout カバレッジ</summary>

| ソース | 状態 | 件数 |
|---|---|---|
| A1 topic:claude-code | ✅ | 25 |
| B1 chrome autofill | ✅ | 12 |
| C1 breakout 90d | ❌ | 0 |
...

_raw 234 → fresh 41 → 採点 30。seen累計 187。_
</details>

Following the "Multi-source Coverage" policy in CLAUDE.md, failed sources are surfaced with

rather than hidden. "採点 30" (scored 30) is proof the MAX_CANDIDATES

cap is in effect, letting you read off that cost is fixed.

v2: 2026-06-10実測調整

).gh

/jq

--created

/--updated

into the query string for gh search

gets them ignoredgh search repos

native flags like --created ">$D90"

(the C-series needs this).-s

checkgrep -cE '^- '

(implemented in the pre-dawn of 2026-06-11 after a socket death).grep -c

returns exit 1 on zero matches|| echo 0

produces 0\n0

and breaks the integer comparison. The script comment explicitly says "footgun。付けない" (footgun. don't add it).has_script

flag from the file listing.gh

, free) from scoring (claude -p

, MAX plan quota), and fix cost with MAX_CANDIDATES=30

.seen.tsv

to prevent next-day resurfacing..incoming/

, and immediately enable in auto/

only the ones with read-only permissions.The parts I've built across this series (4-layer memory / self-propagating skills / context reduction / launchd / autopilot / long-term memory) all connect into one here. GitHub discoveries ride into the brief, and good skills become usable automatically from the next day. A loop where the environment "reads and grows" is now wired end to end.

Next time, I wrote about how context injection ballooned as these daily-accumulating skills and briefs grew — The Context Audit That Cut It From 228KB to 48KB.

*Written by Lily — I ship iOS apps and automate my content stack with Claude Code.

Follow along: Portfolio · X · GitHub*

── more in #developer-tools 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/sending-claude-code-…] indexed:0 read:9min 2026-07-13 ·