all-for-claudecode 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/README.md +15 -3
  4. package/agents/afc-architect.md +1 -1
  5. package/agents/afc-security.md +1 -1
  6. package/commands/analyze.md +1 -1
  7. package/commands/architect.md +1 -1
  8. package/commands/auto.md +2 -2
  9. package/commands/checkpoint.md +1 -1
  10. package/commands/clarify.md +1 -1
  11. package/commands/clean.md +126 -0
  12. package/commands/consult.md +1 -1
  13. package/commands/debug.md +1 -1
  14. package/commands/doctor.md +64 -23
  15. package/commands/ideate.md +1 -1
  16. package/commands/implement.md +1 -1
  17. package/commands/init.md +10 -6
  18. package/commands/launch.md +1 -1
  19. package/commands/plan.md +1 -1
  20. package/commands/pr-comment.md +1 -1
  21. package/commands/principles.md +1 -1
  22. package/commands/qa.md +191 -0
  23. package/commands/release-notes.md +1 -1
  24. package/commands/research.md +1 -1
  25. package/commands/resume.md +2 -2
  26. package/commands/review.md +1 -1
  27. package/commands/security.md +1 -1
  28. package/commands/spec.md +1 -1
  29. package/commands/tasks.md +1 -1
  30. package/commands/test.md +1 -1
  31. package/commands/triage.md +1 -1
  32. package/commands/validate.md +1 -1
  33. package/docs/phase-gate-protocol.md +1 -1
  34. package/hooks/hooks.json +1 -0
  35. package/package.json +5 -3
  36. package/schemas/hooks.schema.json +4 -0
  37. package/schemas/plugin.schema.json +5 -1
  38. package/scripts/afc-bash-guard.sh +3 -3
  39. package/scripts/afc-config-change.sh +8 -0
  40. package/scripts/afc-consistency-check.sh +58 -19
  41. package/scripts/afc-dag-validate.sh +1 -1
  42. package/scripts/afc-doctor.sh +445 -0
  43. package/scripts/afc-failure-hint.sh +24 -2
  44. package/scripts/afc-qa-audit.sh +536 -0
  45. package/scripts/afc-state.sh +3 -3
  46. package/scripts/afc-sync-cache.sh +49 -0
  47. package/scripts/afc-triage.sh +14 -3
  48. package/scripts/afc-user-prompt-submit.sh +98 -13
  49. package/scripts/pre-compact-checkpoint.sh +2 -2
  50. package/scripts/session-start-context.sh +39 -10
  51. package/scripts/track-afc-changes.sh +3 -3
@@ -0,0 +1,445 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # afc-doctor.sh — Automated health check for all-for-claudecode plugin
5
+ # Runs categories 1-8 deterministically. Categories 9-11 require LLM analysis.
6
+ # Output: human-readable text (no JSON), directly printable.
7
+ # Read-only: never modifies files.
8
+
9
+ # shellcheck source=afc-state.sh
10
+ . "$(dirname "$0")/afc-state.sh"
11
+
12
+ cleanup() { :; }
13
+ trap cleanup EXIT
14
+
15
+ # --- Globals ---
16
+ PASS=0
17
+ WARN=0
18
+ FAIL=0
19
+ VERBOSE=false
20
+
21
+ # Parse arguments
22
+ for arg in "$@"; do
23
+ case "$arg" in
24
+ --verbose) VERBOSE=true ;;
25
+ esac
26
+ done
27
+
28
+ # Derive paths
29
+ PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
30
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
31
+
32
+ # --- Helpers ---
33
+ pass() {
34
+ PASS=$((PASS + 1))
35
+ printf ' \xe2\x9c\x93 %s\n' "$1"
36
+ }
37
+
38
+ warn() {
39
+ WARN=$((WARN + 1))
40
+ printf ' \xe2\x9a\xa0 %s\n' "$1"
41
+ if [ -n "${2:-}" ]; then
42
+ printf ' Fix: %s\n' "$2"
43
+ fi
44
+ }
45
+
46
+ fail() {
47
+ FAIL=$((FAIL + 1))
48
+ printf ' \xe2\x9c\x97 %s\n' "$1"
49
+ if [ -n "${2:-}" ]; then
50
+ printf ' Fix: %s\n' "$2"
51
+ fi
52
+ }
53
+
54
+ section() {
55
+ printf '\n%s\n' "$1"
56
+ }
57
+
58
+ # --- Category 1: Environment ---
59
+ section "Environment"
60
+
61
+ if command -v git >/dev/null 2>&1; then
62
+ GIT_VER=$(git --version 2>/dev/null | sed 's/git version //')
63
+ pass "git installed ($GIT_VER)"
64
+ else
65
+ fail "git not found" "install git"
66
+ fi
67
+
68
+ if command -v jq >/dev/null 2>&1; then
69
+ pass "jq installed"
70
+ else
71
+ warn "jq not found — hook scripts will use grep/sed fallback" "brew install jq"
72
+ fi
73
+
74
+ # --- Category 2: Project Config ---
75
+ section "Project Config"
76
+
77
+ CONFIG_FILE="$PROJECT_DIR/.claude/afc.config.md"
78
+ if [ -f "$CONFIG_FILE" ]; then
79
+ pass ".claude/afc.config.md exists"
80
+
81
+ # Required sections
82
+ MISSING_SECTIONS=""
83
+ for sec in "## CI Commands" "## Architecture" "## Code Style"; do
84
+ if ! grep -q "$sec" "$CONFIG_FILE" 2>/dev/null; then
85
+ MISSING_SECTIONS="${MISSING_SECTIONS:+$MISSING_SECTIONS, }$sec"
86
+ fi
87
+ done
88
+ if [ -z "$MISSING_SECTIONS" ]; then
89
+ pass "Required sections present"
90
+ else
91
+ fail "Missing sections: $MISSING_SECTIONS" "add missing section to .claude/afc.config.md or re-run /afc:init"
92
+ fi
93
+
94
+ # Gate command
95
+ if grep -q 'gate:' "$CONFIG_FILE" 2>/dev/null; then
96
+ pass "Gate command defined"
97
+ else
98
+ fail "gate: field not found in CI Commands" "add gate: field to ## CI Commands section"
99
+ fi
100
+
101
+ # CI/gate command execution (verbose only)
102
+ if [ "$VERBOSE" = true ]; then
103
+ CI_CMD=$(grep -A1 '```yaml' "$CONFIG_FILE" 2>/dev/null | grep 'ci:' | head -1 | sed 's/ci:[[:space:]]*"//;s/"[[:space:]]*$//' || true)
104
+ if [ -n "$CI_CMD" ]; then
105
+ if (cd "$PROJECT_DIR" && eval "$CI_CMD" >/dev/null 2>&1); then
106
+ pass "CI command runnable ($CI_CMD)"
107
+ else
108
+ warn "CI command failed: $CI_CMD" "check ci: in afc.config.md"
109
+ fi
110
+ fi
111
+
112
+ GATE_CMD=$(grep -A5 '```yaml' "$CONFIG_FILE" 2>/dev/null | grep 'gate:' | head -1 | sed 's/gate:[[:space:]]*"//;s/"[[:space:]]*$//' || true)
113
+ if [ -n "$GATE_CMD" ]; then
114
+ if (cd "$PROJECT_DIR" && eval "$GATE_CMD" >/dev/null 2>&1); then
115
+ pass "Gate command runnable ($GATE_CMD)"
116
+ else
117
+ warn "Gate command failed: $GATE_CMD" "check gate: in afc.config.md"
118
+ fi
119
+ fi
120
+ fi
121
+ else
122
+ fail ".claude/afc.config.md not found" "run /afc:init"
123
+ fi
124
+
125
+ # --- Category 3: CLAUDE.md Integration ---
126
+ section "CLAUDE.md Integration"
127
+
128
+ GLOBAL_CLAUDE="$HOME/.claude/CLAUDE.md"
129
+ if [ -f "$GLOBAL_CLAUDE" ]; then
130
+ pass "Global ~/.claude/CLAUDE.md exists"
131
+
132
+ # AFC block
133
+ HAS_START=$(grep -c '<!-- AFC:START -->' "$GLOBAL_CLAUDE" 2>/dev/null || echo 0)
134
+ HAS_END=$(grep -c '<!-- AFC:END -->' "$GLOBAL_CLAUDE" 2>/dev/null || echo 0)
135
+ if [ "$HAS_START" -gt 0 ] && [ "$HAS_END" -gt 0 ]; then
136
+ pass "all-for-claudecode block present"
137
+
138
+ # Version check
139
+ BLOCK_VERSION=$(grep -o 'AFC:VERSION:[0-9][0-9.]*' "$GLOBAL_CLAUDE" 2>/dev/null | head -1 | sed 's/AFC:VERSION://' || true)
140
+ if [ -f "$PLUGIN_ROOT/package.json" ]; then
141
+ if command -v jq >/dev/null 2>&1; then
142
+ PLUGIN_VERSION=$(jq -r '.version // empty' "$PLUGIN_ROOT/package.json" 2>/dev/null || true)
143
+ else
144
+ PLUGIN_VERSION=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PLUGIN_ROOT/package.json" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"//') || true
145
+ fi
146
+
147
+ if [ -n "${BLOCK_VERSION:-}" ] && [ -n "${PLUGIN_VERSION:-}" ]; then
148
+ if [ "$BLOCK_VERSION" = "$PLUGIN_VERSION" ]; then
149
+ pass "Block version matches plugin ($PLUGIN_VERSION)"
150
+ else
151
+ warn "all-for-claudecode block outdated (block: $BLOCK_VERSION, plugin: $PLUGIN_VERSION)" "run /afc:init to update"
152
+ fi
153
+ fi
154
+ fi
155
+ else
156
+ fail "all-for-claudecode block not found" "run /afc:init to inject all-for-claudecode block"
157
+ fi
158
+ else
159
+ warn "No global ~/.claude/CLAUDE.md" "run /afc:init"
160
+ fi
161
+
162
+ # --- Category 4: Legacy Migration ---
163
+ section "Legacy Migration"
164
+
165
+ LEGACY_FOUND=false
166
+
167
+ # Legacy CLAUDE.md block
168
+ if [ -f "$GLOBAL_CLAUDE" ] && grep -q '<!-- SELFISH:START -->' "$GLOBAL_CLAUDE" 2>/dev/null; then
169
+ LEGACY_FOUND=true
170
+ warn "Legacy SELFISH:START block in ~/.claude/CLAUDE.md" "run /afc:init (will replace)"
171
+ fi
172
+
173
+ # Legacy config
174
+ if [ -f "$PROJECT_DIR/.claude/selfish.config.md" ]; then
175
+ LEGACY_FOUND=true
176
+ warn "Legacy config .claude/selfish.config.md found" "mv .claude/selfish.config.md .claude/afc.config.md"
177
+ fi
178
+
179
+ # Legacy state files
180
+ LEGACY_STATE=$(find "$PROJECT_DIR/.claude" -maxdepth 1 -name '.selfish-*' 2>/dev/null | head -1 || true)
181
+ if [ -n "$LEGACY_STATE" ]; then
182
+ LEGACY_FOUND=true
183
+ warn "Legacy state files .claude/.selfish-* found" "cd .claude && for f in .selfish-*; do mv \"\$f\" \"\${f/.selfish-/.afc-}\"; done"
184
+ fi
185
+
186
+ # Legacy artifact dir
187
+ if [ -d "$PROJECT_DIR/.claude/selfish" ]; then
188
+ LEGACY_FOUND=true
189
+ warn "Legacy artifact directory .claude/selfish/ found" "mv .claude/selfish .claude/afc"
190
+ fi
191
+
192
+ # Legacy git tags
193
+ LEGACY_TAGS=$(cd "$PROJECT_DIR" 2>/dev/null && git tag -l 'selfish/*' 2>/dev/null | head -1 || true)
194
+ if [ -n "$LEGACY_TAGS" ]; then
195
+ LEGACY_FOUND=true
196
+ warn "Legacy git tags selfish/* found" "git tag -l 'selfish/*' | xargs git tag -d"
197
+ fi
198
+
199
+ # Legacy plugin
200
+ SETTINGS_FILE="$HOME/.claude/settings.json"
201
+ if [ -f "$SETTINGS_FILE" ] && grep -q 'selfish-pipeline' "$SETTINGS_FILE" 2>/dev/null; then
202
+ LEGACY_FOUND=true
203
+ warn "Old selfish-pipeline plugin still installed" "claude plugin uninstall selfish@selfish-pipeline"
204
+ fi
205
+
206
+ if [ "$LEGACY_FOUND" = false ]; then
207
+ pass "No legacy artifacts"
208
+ fi
209
+
210
+ # --- Category 5: Pipeline State ---
211
+ section "Pipeline State"
212
+
213
+ if [ -f "$PROJECT_DIR/.claude/.afc-state.json" ]; then
214
+ if afc_state_is_active; then
215
+ FEAT=$(afc_state_read feature 2>/dev/null || echo "unknown")
216
+ PH=$(afc_state_read phase 2>/dev/null || echo "unknown")
217
+ warn "Active pipeline state (feature: $FEAT, phase: $PH)" "\"${PLUGIN_ROOT}/scripts/afc-pipeline-manage.sh\" end --force or /afc:resume"
218
+ else
219
+ warn "Zombie state file found (.afc-state.json exists but invalid)" "rm -f .claude/.afc-state.json"
220
+ fi
221
+ else
222
+ pass "No stale pipeline state"
223
+ fi
224
+
225
+ # Orphaned artifacts
226
+ ORPHAN_DIRS=$(find "$PROJECT_DIR/.claude/afc/specs" -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true)
227
+ if [ -n "$ORPHAN_DIRS" ]; then
228
+ # Check if any are from the active pipeline
229
+ ACTIVE_FEAT=""
230
+ if afc_state_is_active; then
231
+ ACTIVE_FEAT=$(afc_state_read feature 2>/dev/null || true)
232
+ fi
233
+ ORPHAN_FOUND=false
234
+ while IFS= read -r dir; do
235
+ [ -z "$dir" ] && continue
236
+ DIR_NAME=$(basename "$dir")
237
+ if [ "$DIR_NAME" != "$ACTIVE_FEAT" ]; then
238
+ ORPHAN_FOUND=true
239
+ warn "Orphaned spec directory: .claude/afc/specs/$DIR_NAME/" "rm -rf .claude/afc/specs/$DIR_NAME/"
240
+ fi
241
+ done <<< "$ORPHAN_DIRS"
242
+ if [ "$ORPHAN_FOUND" = false ]; then
243
+ pass "No orphaned artifacts"
244
+ fi
245
+ else
246
+ pass "No orphaned artifacts"
247
+ fi
248
+
249
+ # Safety tags
250
+ SAFETY_TAG=$(cd "$PROJECT_DIR" 2>/dev/null && git tag -l 'afc/pre-*' 2>/dev/null | head -1 || true)
251
+ if [ -n "$SAFETY_TAG" ]; then
252
+ if ! afc_state_is_active; then
253
+ warn "Lingering safety tag: $SAFETY_TAG" "git tag -d $SAFETY_TAG"
254
+ else
255
+ pass "Safety tag matches active pipeline"
256
+ fi
257
+ else
258
+ pass "No lingering safety tags"
259
+ fi
260
+
261
+ # Checkpoint
262
+ LOCAL_CP="$PROJECT_DIR/.claude/afc/memory/checkpoint.md"
263
+ if [ -f "$LOCAL_CP" ]; then
264
+ CP_DATE=$(grep 'Auto-generated:' "$LOCAL_CP" 2>/dev/null | head -1 | sed 's/.*Auto-generated: //' || true)
265
+ warn "Checkpoint from $CP_DATE" "run /afc:resume or delete .claude/afc/memory/checkpoint.md"
266
+ else
267
+ pass "No stale checkpoint"
268
+ fi
269
+
270
+ # --- Category 6: Memory Health ---
271
+ section "Memory Health"
272
+
273
+ MEMORY_DIR="$PROJECT_DIR/.claude/afc/memory"
274
+ if [ -d "$MEMORY_DIR" ]; then
275
+ check_dir_count() {
276
+ local dir="$1" name="$2" threshold="$3"
277
+ if [ -d "$dir" ]; then
278
+ local count
279
+ count=$(find "$dir" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ')
280
+ if [ "$count" -le "$threshold" ]; then
281
+ pass "$name: $count files"
282
+ else
283
+ warn "$name: $count files (threshold: $threshold)" "prune oldest files in $name/"
284
+ fi
285
+ fi
286
+ }
287
+
288
+ check_dir_count "$MEMORY_DIR/quality-history" "quality-history" 30
289
+ check_dir_count "$MEMORY_DIR/reviews" "reviews" 40
290
+ check_dir_count "$MEMORY_DIR/retrospectives" "retrospectives" 30
291
+ check_dir_count "$MEMORY_DIR/research" "research" 50
292
+ check_dir_count "$MEMORY_DIR/decisions" "decisions" 60
293
+
294
+ # Agent memory sizes
295
+ check_agent_memory() {
296
+ local agent="$1" limit="$2"
297
+ local mem_file="$PROJECT_DIR/.claude/agent-memory/$agent/MEMORY.md"
298
+ if [ -f "$mem_file" ]; then
299
+ local lines
300
+ lines=$(wc -l < "$mem_file" | tr -d ' ')
301
+ if [ "$lines" -le "$limit" ]; then
302
+ pass "$agent MEMORY.md: $lines lines"
303
+ else
304
+ warn "$agent MEMORY.md: $lines lines (limit: $limit)" "invoke /afc:${agent#afc-} to trigger self-pruning"
305
+ fi
306
+ fi
307
+ }
308
+
309
+ check_agent_memory "afc-architect" 100
310
+ check_agent_memory "afc-security" 100
311
+ else
312
+ pass "No memory directory"
313
+ fi
314
+
315
+ # --- Category 7: Hook Health ---
316
+ section "Hook Health"
317
+
318
+ HOOKS_FILE="$PLUGIN_ROOT/hooks/hooks.json"
319
+ if [ -f "$HOOKS_FILE" ]; then
320
+ HOOKS_VALID=false
321
+ if command -v jq >/dev/null 2>&1; then
322
+ if jq -e '.hooks' "$HOOKS_FILE" >/dev/null 2>&1; then
323
+ HOOKS_VALID=true
324
+ fi
325
+ else
326
+ if grep -q '"hooks"' "$HOOKS_FILE" 2>/dev/null; then
327
+ HOOKS_VALID=true
328
+ fi
329
+ fi
330
+
331
+ if [ "$HOOKS_VALID" = true ]; then
332
+ pass "hooks.json valid"
333
+ else
334
+ fail "hooks.json invalid" "reinstall plugin: claude plugin install afc@all-for-claudecode"
335
+ fi
336
+
337
+ # Check all referenced scripts exist
338
+ MISSING_SCRIPTS=""
339
+ if command -v jq >/dev/null 2>&1; then
340
+ while IFS= read -r cmd; do
341
+ [ -z "$cmd" ] && continue
342
+ # Extract script path from command string (strip quotes and CLAUDE_PLUGIN_ROOT)
343
+ SCRIPT_PATH=$(printf '%s\n' "$cmd" | sed 's|"||g; s|\${CLAUDE_PLUGIN_ROOT}|'"$PLUGIN_ROOT"'|g' | awk '{print $1}')
344
+ if [ ! -f "$SCRIPT_PATH" ]; then
345
+ MISSING_SCRIPTS="${MISSING_SCRIPTS:+$MISSING_SCRIPTS, }$(basename "$SCRIPT_PATH")"
346
+ fi
347
+ done < <(jq -r '.. | objects | select(.command?) | .command' "$HOOKS_FILE" 2>/dev/null)
348
+ fi
349
+
350
+ if [ -z "$MISSING_SCRIPTS" ]; then
351
+ pass "All hook scripts exist"
352
+ else
353
+ fail "Missing scripts: $MISSING_SCRIPTS" "reinstall plugin"
354
+ fi
355
+
356
+ # Check scripts executable
357
+ NON_EXEC=""
358
+ for script in "$PLUGIN_ROOT"/scripts/*.sh; do
359
+ [ -f "$script" ] || continue
360
+ if [ ! -x "$script" ]; then
361
+ NON_EXEC="${NON_EXEC:+$NON_EXEC, }$(basename "$script")"
362
+ fi
363
+ done
364
+ if [ -z "$NON_EXEC" ]; then
365
+ pass "All scripts executable"
366
+ else
367
+ warn "Non-executable scripts: $NON_EXEC" "chmod +x on the listed scripts"
368
+ fi
369
+ else
370
+ fail "hooks.json not found" "reinstall plugin: claude plugin install afc@all-for-claudecode"
371
+ fi
372
+
373
+ # --- Category 8: Version Sync (dev only) ---
374
+ IS_DEV=false
375
+ if [ -f "$PROJECT_DIR/package.json" ]; then
376
+ if command -v jq >/dev/null 2>&1; then
377
+ PKG_NAME=$(jq -r '.name // empty' "$PROJECT_DIR/package.json" 2>/dev/null || true)
378
+ else
379
+ PKG_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$PROJECT_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"//') || true
380
+ fi
381
+ if [ "$PKG_NAME" = "all-for-claudecode" ]; then
382
+ IS_DEV=true
383
+ fi
384
+ fi
385
+
386
+ if [ "$IS_DEV" = true ]; then
387
+ section "Version Sync (dev)"
388
+
389
+ # Read versions from all 3 files
390
+ if command -v jq >/dev/null 2>&1; then
391
+ V_PKG=$(jq -r '.version // empty' "$PROJECT_DIR/package.json" 2>/dev/null || true)
392
+ V_PLUGIN=$(jq -r '.version // empty' "$PROJECT_DIR/.claude-plugin/plugin.json" 2>/dev/null || true)
393
+ V_MKT_META=$(jq -r '.metadata.version // empty' "$PROJECT_DIR/.claude-plugin/marketplace.json" 2>/dev/null || true)
394
+ V_MKT_PLUG=$(jq -r '.plugins[0].version // empty' "$PROJECT_DIR/.claude-plugin/marketplace.json" 2>/dev/null || true)
395
+ else
396
+ V_PKG=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PROJECT_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"//') || true
397
+ V_PLUGIN=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PROJECT_DIR/.claude-plugin/plugin.json" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"//') || true
398
+ V_MKT_META=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PROJECT_DIR/.claude-plugin/marketplace.json" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"//') || true
399
+ V_MKT_PLUG=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PROJECT_DIR/.claude-plugin/marketplace.json" 2>/dev/null | sed -n '2p' | sed 's/.*: *"//;s/"//') || true
400
+ fi
401
+
402
+ if [ "$V_PKG" = "$V_PLUGIN" ] && [ "$V_PKG" = "$V_MKT_META" ] && [ "$V_PKG" = "$V_MKT_PLUG" ]; then
403
+ pass "Version triple match ($V_PKG)"
404
+ else
405
+ fail "Version mismatch — package.json: $V_PKG, plugin.json: $V_PLUGIN, marketplace meta: $V_MKT_META, marketplace plugin: $V_MKT_PLUG" "update mismatched files to the same version"
406
+ fi
407
+
408
+ # Cache sync check
409
+ CACHE_DIR="$HOME/.claude/plugins/cache/all-for-claudecode/afc/$V_PKG"
410
+ if [ -d "$CACHE_DIR" ]; then
411
+ CACHE_AUTO="$CACHE_DIR/commands/auto.md"
412
+ SOURCE_AUTO="$PROJECT_DIR/commands/auto.md"
413
+ if [ -f "$CACHE_AUTO" ] && [ -f "$SOURCE_AUTO" ]; then
414
+ if diff -q "$SOURCE_AUTO" "$CACHE_AUTO" >/dev/null 2>&1; then
415
+ pass "Cache in sync"
416
+ else
417
+ warn "Plugin cache is stale" "npm run sync:cache"
418
+ fi
419
+ else
420
+ warn "Cannot check cache sync (files missing)" "npm run sync:cache"
421
+ fi
422
+ else
423
+ warn "Plugin cache directory not found" "install plugin first, then npm run sync:cache"
424
+ fi
425
+ fi
426
+
427
+ # --- Summary ---
428
+ printf '\n'
429
+ printf '%s\n' "$(printf '\xe2\x94\x80%.0s' {1..25})"
430
+ printf 'Results: %d passed, %d warnings, %d failures\n' "$PASS" "$WARN" "$FAIL"
431
+
432
+ if [ "$FAIL" -eq 0 ] && [ "$WARN" -eq 0 ]; then
433
+ printf 'No issues found!\n'
434
+ elif [ "$FAIL" -eq 0 ]; then
435
+ printf '%d warnings found. Non-blocking but review recommended.\n' "$WARN"
436
+ else
437
+ printf '%d issues need attention. Run the Fix commands above.\n' "$FAIL"
438
+ fi
439
+
440
+ # Signal dev-only categories to caller
441
+ if [ "$IS_DEV" = true ]; then
442
+ printf '\nNote: Categories 9-11 (Command/Agent/Doc validation) require LLM analysis.\n'
443
+ fi
444
+
445
+ exit 0
@@ -33,6 +33,13 @@ ERROR="${ERROR:-}"
33
33
  # If pipeline is active, log failure (normalize error message to single line)
34
34
  if afc_state_is_active && [ -n "$ERROR" ]; then
35
35
  ERROR_ONELINE=$(printf '%s\n' "$ERROR" | head -1 | cut -c1-200)
36
+ # Auto-rotate if log exceeds 1 MB
37
+ if [ -f "$FAILURES_LOG" ]; then
38
+ LOG_SIZE=$(wc -c < "$FAILURES_LOG" | tr -d ' ')
39
+ if [ "$LOG_SIZE" -ge 1048576 ]; then
40
+ mv "$FAILURES_LOG" "${FAILURES_LOG}.1"
41
+ fi
42
+ fi
36
43
  printf '%s\n' "$(date +%s) $TOOL_NAME: $ERROR_ONELINE" >> "$FAILURES_LOG"
37
44
  fi
38
45
 
@@ -57,6 +64,21 @@ case "$ERROR" in
57
64
  *"ENOMEM"*|*"Cannot allocate"*)
58
65
  HINT="Out of memory. Terminate other processes or check resources."
59
66
  ;;
67
+ *"ETIMEDOUT"*|*"timed out"*|*"timeout"*)
68
+ HINT="Request timed out. Check network connectivity or increase timeout."
69
+ ;;
70
+ *"ENOSPC"*|*"No space left"*)
71
+ HINT="Disk full. Free up space and retry."
72
+ ;;
73
+ *"syntax error"*|*"SyntaxError"*)
74
+ HINT="Syntax error detected. Check recent changes for typos or missing brackets."
75
+ ;;
76
+ *"FAILED"*|*"failures"*|*"failed"*)
77
+ HINT="Test/build failures detected. Review the output above for specific errors."
78
+ ;;
79
+ *"Exit code"*)
80
+ HINT="Command exited with non-zero status. Check the output above for details."
81
+ ;;
60
82
  *)
61
83
  HINT=""
62
84
  ;;
@@ -67,13 +89,13 @@ if [ -n "$HINT" ]; then
67
89
  # Generate safe JSON with jq if available, otherwise strip special chars and use printf
68
90
  if command -v jq &> /dev/null; then
69
91
  jq -n --arg ctx "[afc:hint] $HINT (tool: $TOOL_NAME)" \
70
- '{"hookSpecificOutput":{"hookEventName":"PostToolUseFailure","additionalContext":$ctx}}' 2>/dev/null || true
92
+ '{"hookSpecificOutput":{"additionalContext":$ctx}}' 2>/dev/null || true
71
93
  else
72
94
  # shellcheck disable=SC1003
73
95
  SAFE_HINT=$(printf '%s' "$HINT" | tr -d '"' | tr -d '\\')
74
96
  # shellcheck disable=SC1003
75
97
  SAFE_TOOL=$(printf '%s' "$TOOL_NAME" | tr -d '"' | tr -d '\\')
76
- printf '{"hookSpecificOutput":{"hookEventName":"PostToolUseFailure","additionalContext":"[afc:hint] %s (tool: %s)"}}\n' "$SAFE_HINT" "$SAFE_TOOL"
98
+ printf '{"hookSpecificOutput":{"additionalContext":"[afc:hint] %s (tool: %s)"}}\n' "$SAFE_HINT" "$SAFE_TOOL"
77
99
  fi
78
100
  fi
79
101