ååã® autopilot èšäºã§ launchd ãã Claude ãç¡äººã§èµ°ãããæ§æã玹ä»ããŸãããä»åã¯ãã®è£åŽ ââ ç¡äººãžã§ããé»ã£ãŠæ¢ãŸã£ãŠããäºå®ããèª°ãæ°ã¥ããªããŸãŸæŸçœ®ããŠããŸãåé¡ãšãã®å¯ŸåŠã®è©±ã§ãã
ç¡äººèªååã®æ¬åœã®æãã¯ãã°ã§ã¯ãªãããµã€ã¬ã³ã倱æã§ããlaunchd ã®ãžã§ãã exit 78 ã§ã¯ã©ãã·ã¥ããŠããŠããhooks ã®ã¹ã¯ãªãããå®è¡æš©éã倱ã£ãŠããŠããagentmemory ãããŒããéãããŸãŸ worker äžåšã®ãåçç¶æ ãã§åããŠããŠãã衚é¢äžã¯äœãèµ·ããŠããªãããã«èŠããŸãããã®ç¶æ ã6æ¥éç¶ããŠèª°ã«ãæ°ã¥ãããªãã£ãå®äŸã `automation-health.sh` ã®ã³ã¡ã³ãã«æ®ã£ãŠããŸãã
å°ãããšïŒé»ã£ãŠæ»ã¬
èªåŸã«ãŒããçµããšç£èŠå¯Ÿè±¡ãå¢ããŸããç§ã®æ§æã ãã§ãïŒ
- launchd ãžã§ãïŒskill-harvest / skill-curate / agentmemory ãªã©ïŒ
- Stop hook / PreToolUse hook ã®å®äœã¹ã¯ãªãã
- äŒè©±ãã°ãé·æèšæ¶ã«å€æãã Stop hook
- Obsidian Vault ã®èªåæŽæ°
- 鱿¬¡ã»ææ¬¡ãããïŒenv-audit / plugin-purge / dotfiles-snapshot ãªã©ïŒ
ããããå šéšæ£åžžãã©ãããæã§ç¢ºèªããæ°ã«ã¯ãªããŸãããããšãã£ãŠåå¥ã« `launchctl list` ãå©ããŠããå šäœåããèŠããªãã
å¿ èŠãªã®ã¯ å šèªååããŸãšããŠç¹æ€ããŠãGREEN / WARN / FAIL ã®3å€ã§æããŠããã1ã³ãã³ãã§ãã
èšèšïŒ3å€å€å®ãš OK/WN/NG ãã«ã
`~/.claude/scripts/automation-health.sh` ã®ã³ã¢ã¯ã·ã³ãã«ã§ãã
fail=0; warn=0
ok() { printf " ${GRN}â${RST} %s\n" "$1"; }
wn() { printf " ${YEL}â ${RST} %s\n" "$1"; warn=$((warn+1)); }
ng() { printf " ${RED}â${RST} %s\n" "$1"; fail=$((fail+1)); }ãã§ãã¯é ç®ã `ok` / `wn` / `ng` ã®ããããã§è©äŸ¡ããŠãããæåŸã«ããå€å®ããŸãã
if [ "$fail" -gt 0 ]; then
printf " ${RED}â FAIL${RST} red=%d warn=%d\n\n" "$fail" "$warn"; exit 1
elif [ "$warn" -gt 0 ]; then
printf " ${YEL}â WARN${RST} warn=%d (èŽåœçåé¡ãªã)\n\n" "$warn"; exit 0
else
printf " ${GRN}â ALL GREEN${RST} å
šèªååãæ£åžžçšŒå\n\n"; exit 0
fi- ALL GREENïŒå šé ç® OKãexit 0
- WARNïŒèŽåœçã§ã¯ãªããèŠæ³šæãexit 0
- FAILïŒRED ã1ã€ã§ãããã° exit 1
exit code ãåããããšã§ã`daily-brief.sh` ã CI ãã `||` ã§åŸç¶åŠçãç¹ããããŸãã
ãã§ãã¯é ç®ïŒäœãèŠãŠããã
ã¹ã¯ãªããã¯9ç¯ã«åãããŠããŸããå®ã³ãŒãããäž»èŠãªå€å®ãæç²ããŸãã
1. launchd ãžã§ãã®çæ»
for job in com.shun.skill-harvest com.shun.skill-curate; do
line=$(launchctl list 2>/dev/null | grep -E "\b${job}\b")
if [ -z "$line" ]; then
ng "$job: æªããŒã (launchctl load ãçŽããå¿
èŠ)"
else
exitc=$(echo "$line" | awk '{print $2}')
if [ "$exitc" = "0" ] || [ "$exitc" = "-" ]; then
ok "$job: ããŒãæž / last exit=$exitc"
else
ng "$job: last exit=$exitc (åå倱æ)"
fi
fi
done`launchctl list` ã®2åç®ã last exit code ã§ãã`-` ã¯ããŸã èµ·åããŠããªãïŒæ£åžžïŒãã0 ã¯æåããã以å€ã¯å€±æã§ãã
2. hooks ã¹ã¯ãªããã®å®åšãšå®è¡æš©é
hooks=(pre_git_guard.sh pre_secrets_check.sh pre_env_guard.sh \
post_audit_log.sh post_format.sh post_tsc_check.sh \
stop_notify.sh user_prompt_submit.sh)
for h in "${hooks[@]}"; do
f="$CLAUDE/hooks/$h"
if [ ! -f "$f" ]; then ng "$h: äžåš"
elif [ ! -x "$f" ]; then wn "$h: å®è¡æš©éãªã (chmod +x æšå¥š)"
else ok "$h"
fi
donesettings.json ã« hook ãæžããŠããå®äœã¹ã¯ãªããã `chmod +x` ãããŠããªããš silently skip ãããŸããããã WARN ã§æ€åºããŸãã
3. skill-harvest ã®ãã°é®®åºŠ
hlog="$CLAUDE/skills/auto/.harvest.log"
a=$(age_h "$hlog") # (now - mtime) / 3600 ãè¿ããã«ã
if [ "$a" -ge 0 ] && [ "$a" -le 48 ]; then
ok "æçµçšŒå ${a}hå: ${last##*] }"
else
wn "æçµçšŒå ${a}hå (>48h: cron忢ã®çã): ${last##*] }"
fi
nskills=$(find "$CLAUDE/skills/auto" -maxdepth 2 -name SKILL.md | wc -l | tr -d ' ')
ok "çææž auto-skill: ${nskills} å"ãã°ã® mtime ã 48h 以äžå€ããã° WARNãããã°ãããããšãæè¿èµ°ã£ããã¯å¥ã®è©±ãªã®ã§ mtime ã§èŠãŸãã
4. äŒè©±ãã°ã®çåãš INDEX.md ã®é®®åºŠ
cnt=$(find "$CONV" -name '*.md' ! -name 'INDEX.md' | wc -l | tr -d ' ')
ok "äŒè©±ãã° ${cnt} ä»¶ / ææ° ${la}hå"
ia=$(age_h "$idx")
if [ "$ia" -le 24 ]; then ok "INDEX.md 鮮床 ${ia}hå"
else wn "INDEX.md ã ${ia}hå (extract ãæŽæ°ããŠããªãå¯èœæ§)"; fi5. Obsidian Vault ã®èªåæŽæ°ããŒã«ãŒ
if [ -f "$hot" ] && grep -q "recent:start auto-updated" "$hot"; then
ha=$(age_h "$hot"); ok "hot.md èªåæŽæ°ããŒã«ãŒæ / ${ha}hå"
else wn "hot.md ã®èªåæŽæ°ããŒã«ãŒãèŠã€ãããªã"; fi
# index.md ã«ãã¬ããž: å®ãã¡ã€ã«æ° vs èšèŒããŒãžæ°
real=$(find "$VAULT" -name '*.md' -not -path '*/.*' | wc -l | tr -d ' ')
stated=$(grep -oE 'ç·ããŒãžæ°ïŒ[0-9]+' "$vidx" | grep -oE '[0-9]+' | head -1)
if [ "$stated" = "$real" ]; then ok "index.md ã«ãã¬ããžäžèŽ (${real}p)"
else wn "index.md èšèŒ ${stated:-?}p â å® ${real}p"; fi6. remember èšæ¶å±€ã®éè€ããŒã¹ãæ€ç¥
for f in now.md recent.md archive.md; do
[ -f "$REMEMBER/$f" ] && ok "$f ååš" || wn "$f äžåš (å¿
é å±€)"
done
dup=$(grep -vE '^\s*$|^##|^#' "$nowf" | sort | uniq -c | sort -rn | head -1 | awk '{print $1}')
if [ "${dup:-0}" -ge 3 ]; then
wn "now.md ã«åäžèŠçŽã ${dup} å (consolidate é
å»¶ã®å
å)"
else
ok "now.md éè€ãªã (consolidate å¥å
š)"
fi`now.md` 㯠consolidate ãé ãããšåãèŠçŽãç¹°ãè¿ã远èšãããŸãã3å以äžã®éè€ã WARN ã§æ©ææ€ç¥ããŸãã
7. ãã£ã¹ã¯äœ¿çšéãšæ®éªžãã¡ã€ã«
cdir_mb=$(( ${cdir_total_mb:-0} - ${disabled_mb:-0} ))
if [ "${cdir_mb:-0}" -lt 2000 ]; then ok "~/.claude å®å¹ ${cdir_mb}MB"
elif [ "${cdir_mb:-0}" -lt 5000 ]; then wn "~/.claude å®å¹ ${cdir_mb}MB (>2GB: 倧ãã)"
else ng "~/.claude å®å¹ ${cdir_mb}MB (>5GB: ç°åžžè¥å€§)"; fi
orphan=$(find "$CLAUDE" -maxdepth 1 -name 'security_warnings_state_*.json' -mtime +7 | wc -l | tr -d ' ')
if [ "${orphan:-0}" -ge 5 ]; then
wn "security_warnings_state_*.json ã ${orphan} å (>7æ¥å: backups/ ãžéé¿æšå¥š)"
fireversible cacheïŒ`.disabled-cache`ïŒã¯å®å¹å®¹éããé€å€ããŠå€å®ããŸãã
8. 鱿¬¡ã»ææ¬¡ãããã®ãã° mtime
declare -a CRON_JOBS=(
"weekly cleanup-misc:$CLAUDE/logs/cleanup-misc.log:192"
"weekly env-audit:$CLAUDE/logs/env-audit-latest.md:192"
"monthly plugin-purge:$CLAUDE/logs/plugin-purge.log:744"
"weekly plugin-auto-disable:$CLAUDE/logs/plugin-auto-disable.log:192"
"weekly dotfiles-snapshot:$CLAUDE/logs/dotfiles-snapshot.log:192"
"weekly agents-index:$CLAUDE/logs/agents-index.log:192"
"daily plugin-usage:$CLAUDE/scripts/plugin-audit-latest.md:48"
)
for entry in "${CRON_JOBS[@]}"; do
IFS=":" read -r name path budget <<< "$entry"
a=$(age_h "$path")
if [ "$a" -le "$budget" ]; then ok "$name: ${a}hå (budget ${budget}h)"
else wn "$name: ${a}hå (budget ${budget}h è¶
é â launchd é
é倱æã®å¯èœæ§)"; fi
done鱿¬¡ãžã§ããªã 192hïŒ8æ¥ïŒãææ¬¡ãªã 744hïŒ31æ¥ïŒã budget ãšããŠããããè¶ ããã WARN ã§ãã
agentmemory ã®ãåçç¶æ ãåé¡
agentmemory ãµãŒãã®ãã§ãã¯ã ããå€å®ã2段éã«ãªã£ãŠããŸãã
am_line=$(launchctl list 2>/dev/null | awk '$3=="com.shun.agentmemory"{print $1" "$2}')
am_pid=${am_line%% *}; am_exit=${am_line##* }
if [ -z "$am_line" ]; then
wn "com.shun.agentmemory ã launchd ã«æªããŒã"
elif [ "$am_pid" = "-" ] && [ "$am_exit" != "0" ]; then
ng "com.shun.agentmemory åæ¢äž (last exit=$am_exit) â ã¯ã©ãã·ã¥ã«ãŒãã®å¯èœæ§"
else
am_code=$(curl -s -o /dev/null -w '%{http_code}' -m 3 \
http://localhost:3111/agentmemory/health 2>/dev/null || echo 000)
if [ "$am_code" = "200" ]; then ok "皌åäž (pid=$am_pid / health 200)"
elif [ "$am_code" = "000" ]; then ng "ããã»ã¹ã¯å±
ãã port 3111 ç¡å¿ç"
else ng "port 3111 ã¯éãã /agentmemory/health=$am_code â worker äžåšã®åçç¶æ
"; fi
fiã³ã¡ã³ãã«çç±ãæžããŠãããŸãã
launchd ã® exit 78 ã¯ã©ãã·ã¥ã«ãŒãã6æ¥é誰ã«ãæ°ã¥ããããããã«ãããŒãã¯éãã worker äžåšã§å šAPI 404ãã®åçç¶æ ã¯æ»æŽ»ç£èŠã§ã¯èŠããªãã
launchd ã®ç¶æ ãã§ãã¯ã ãã§ã¯ãããã»ã¹ããããå®éã«ã¯åããŠããªããã±ãŒã¹ãæ€åºã§ããŸããã`curl` ã§ HTTP 200 ã確èªããçç±ã¯ããã«ãããŸãã
launchd ã® PID ãã§ãã¯ãš HTTP ãã«ã¹ãšã³ããã€ã³ãã®äž¡æ¹ãèŠãªããšããèµ·åããŠããããã«èŠããŠå š API ã 404ããšããåçç¶æ ãèŠéããŸããMCP ãµãŒãã API ãµãŒãã launchd ã§åãããªããå¿ ã `/health` ãšã³ããã€ã³ããå©ãäžæéãè¶³ããŠãã ããã
cron â launchd äºéå®è¡ã®æ€ç¥
èŠèœãšããã¡ãªé ç®ã9çªç®ã®äºéå®è¡ãã§ãã¯ã§ãã
cron_sh=$(crontab -l 2>/dev/null | grep -vE '^[[:space:]]*#' | \
grep -oE '/[^ ]+\.sh' | xargs -n1 basename | sort -u)
launchd_sh=$(grep -hoE '/[^<> ]+\.sh' \
~/Library/LaunchAgents/com.shun.*.plist | xargs -n1 basename | sort -u)
dup=$(comm -12 <(printf '%s\n' "$cron_sh") <(printf '%s\n' "$launchd_sh") | \
grep -vE '^[[:space:]]*$')
if [ -n "$dup" ]; then
ng "cron ãš launchd ã®äž¡æ¹ã«ç»é²ããäºéå®è¡ãããã¹ã¯ãªãã: $(printf '%s' "$dup" | tr '\n' ' ')"
fiã³ã¡ã³ãã«ã¯ã2026-06-01: cronâlaunchd ç§»è¡ã§ cron ãæ¶ãå¿ããŠäž¡æ¹ã«ç»é²ïŒæ¯ãµã€ã¯ã«äºéå®è¡ãçºçããšãããŸããç§»è¡åŸã«å€ã crontab ãæ¶ãå¿ãããã¿ãŒã³ã§ãã
daily-brief ãšã®é£æº
`daily-brief.sh` ã¯æ¯æ 8:00 ã« launchd ããèµ·åããåé ã®ç°å¢ãã«ã¹ã«1è¡ã ãçµã¿èŸŒã¿ãŸãã
echo "## ð¥ ç°å¢ãã«ã¹"
run_to 60 ~/.claude/scripts/automation-health.sh 2>&1 | tail -3 | head -1`tail -3 | head -1` ã¯æçµãµããªè¡ïŒ`â ALL GREEN` / `â WARN` / `â FAIL` ã®è¡ïŒã ããåãåºããŠããŸãã
ããã« `daily-brief.sh` åŽã§ã¯ãã©ã€ãçéãããŒãã§ RED ãæ€åºãããšãã ã `## ðš èŠå¯Ÿå¿` ã»ã¯ã·ã§ã³ãçŸããŸãã
RED_FLAGS=""
flag_red() { RED_FLAGS="${RED_FLAGS}\n- â $1"; }
if [ -n "$RED_FLAGS" ]; then
echo "## ðš èŠå¯Ÿå¿ïŒã©ã€ãçéã§REDïŒ"
printf '%b\n' "$RED_FLAGS"
fiåé¡ããªããšããã®ã»ã¯ã·ã§ã³ã¯ååšããŸããã**ã倱æäžã ãçŸããç¯ã**ãšããŠèšèšãããŠããŸããbrief 㯠`~/Desktop/Daily Brief/today-brief-YYYYMMDD.md` ã«æžãåºãããã®ã§ããã¹ã¯ããããéãããšãã« RED ãç®ã«å ¥ã圢ã«ãªããŸãã
`automation-health.sh` ã® exit 1 ã䜿ãã°ãlaunchd ã®ã©ãããŒã§åæ§ã®ãã¿ãŒã³ã远å ã§ããŸãã
bash ~/.claude/scripts/automation-health.sh || touch ~/Desktop/AUTOMATION_FAILED.md
bash ~/.claude/scripts/automation-health.sh && rm -f ~/Desktop/AUTOMATION_FAILED.md倱æäžã ããã¡ã€ã«ãååšããçŽã£ããæ¶ãã ââ ããã·ã¥ããŒããèŠãªããŠããã¹ã¯ããããç¶æ ãæããŠãããŸãã
èžãã èœãšã穎
- `launchctl list` ã® last exit code `0` ãš `-` ãåãã«æ±ããªããšèª€ WARN â ã©ã¡ããæ£åžžãšã㊠OR æ¡ä»¶ã§åŒŸã
- hooks ã¹ã¯ãªããã `chmod +x` ãããŠããªããŠã settings.json ã¯ãšã©ãŒãåºããªã â WARN ã§æ€åºãããŸã§æ°ã¥ããªãã£ã
- agentmemory ã¯ããŒããéããš launchd ããèµ·åäžããšèŠãªã â HTTP `/health` ãå©ããŸã§åçç¶æ ãæ€åºã§ããªã
- env-audit ã®åºåå ã `scripts/` ãã `logs/` ã«ç§»èšãããšãããã«ã¹ãã§ãã¯åŽã®ãã¹ãæŽæ°ãå¿ èŠïŒã¹ã¯ãªããå ã³ã¡ã³ãã2026-06-11: åºåå ã scripts/âlogs/ ã«ç§»èšãããïŒ
- **index.md** ã«ãã¬ããžãã§ãã¯ã§ `.backup` çã®é ããã£ã¬ã¯ããªãé€å€ããªããšæä¹ WARN ã«ãªã â `-not -path '/.'` ã§é ããã£ã¬ã¯ããªãé€å€
- cronâlaunchd ç§»è¡åŸã« crontab ãæ¶ãå¿ãããšäºéå®è¡ â `comm -12` ã§äž¡ç³»çµ±ã®ã¹ã¯ãªããåãæ¯èŒããŠæ€åº
ãŸãšã
- èªåŸåã®æ»æŽ»ç¢ºèªã¯1ã³ãã³ãã§ ALL GREEN / WARN / FAIL ã®3å€ã«éçŽãã
- `exit 1` ãæ©æ¢°å€å®ã®ã·ã°ãã«ãšããŠäœ¿ããdaily-brief ãžã®1è¡åã蟌ã¿ã FAILED ãã¡ã€ã«ã®çæã«ç¹ãã
- **ãåé¡äžã ãçŸããç¯ããã¡ã€ã«ã**ãèšèšããããšã§ãããã·ã¥ããŒããèŠãªããŠãç°åžžãç®ã«é£ã³èŸŒã
- launchd ã® PID ãã§ãã¯ã ãã§ã¯ãåçç¶æ ããèŠéããHTTP ãã«ã¹ãšã³ããã€ã³ããå¿ ãäžç·ã«å©ã
- ãããã®æ»æŽ»ã¯ log ã® mtime ã budget æéãšæ¯èŒããã ãã§æ©æ¢°å€å®ã§ãã
次åã¯ããã®ãã«ã¹ãã§ãã¯ãš autopilot ãçæããæ¹åãã°ãçµ±åããŠã鱿¬¡ã§äœãå€ãã£ããããŸãšãã ââ 鱿¬¡ã¬ããŒãèªåçæã®ä»çµã¿ãæžãäºå®ã§ãã
ãã®èšäºãè¯ãã£ãã
ããããããªã¯ãšã¹ããã§èè ã«ãããã®åãåãèšå®ããé¡ãã§ããŸã
