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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +4 -2
- package/README.md +15 -3
- package/agents/afc-architect.md +1 -1
- package/agents/afc-security.md +1 -1
- package/commands/analyze.md +1 -1
- package/commands/architect.md +1 -1
- package/commands/auto.md +2 -2
- package/commands/checkpoint.md +1 -1
- package/commands/clarify.md +1 -1
- package/commands/clean.md +126 -0
- package/commands/consult.md +1 -1
- package/commands/debug.md +1 -1
- package/commands/doctor.md +64 -23
- package/commands/ideate.md +1 -1
- package/commands/implement.md +1 -1
- package/commands/init.md +10 -6
- package/commands/launch.md +1 -1
- package/commands/plan.md +1 -1
- package/commands/pr-comment.md +1 -1
- package/commands/principles.md +1 -1
- package/commands/qa.md +191 -0
- package/commands/release-notes.md +1 -1
- package/commands/research.md +1 -1
- package/commands/resume.md +2 -2
- package/commands/review.md +1 -1
- package/commands/security.md +1 -1
- package/commands/spec.md +1 -1
- package/commands/tasks.md +1 -1
- package/commands/test.md +1 -1
- package/commands/triage.md +1 -1
- package/commands/validate.md +1 -1
- package/docs/phase-gate-protocol.md +1 -1
- package/hooks/hooks.json +1 -0
- package/package.json +5 -3
- package/schemas/hooks.schema.json +4 -0
- package/schemas/plugin.schema.json +5 -1
- package/scripts/afc-bash-guard.sh +3 -3
- package/scripts/afc-config-change.sh +8 -0
- package/scripts/afc-consistency-check.sh +58 -19
- package/scripts/afc-dag-validate.sh +1 -1
- package/scripts/afc-doctor.sh +445 -0
- package/scripts/afc-failure-hint.sh +24 -2
- package/scripts/afc-qa-audit.sh +536 -0
- package/scripts/afc-state.sh +3 -3
- package/scripts/afc-sync-cache.sh +49 -0
- package/scripts/afc-triage.sh +14 -3
- package/scripts/afc-user-prompt-submit.sh +98 -13
- package/scripts/pre-compact-checkpoint.sh +2 -2
- package/scripts/session-start-context.sh +39 -10
- 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":{"
|
|
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":{"
|
|
98
|
+
printf '{"hookSpecificOutput":{"additionalContext":"[afc:hint] %s (tool: %s)"}}\n' "$SAFE_HINT" "$SAFE_TOOL"
|
|
77
99
|
fi
|
|
78
100
|
fi
|
|
79
101
|
|