all-for-claudecode 2.4.0 → 2.6.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 +1 -1
- package/README.md +15 -3
- package/agents/afc-impl-worker.md +2 -0
- package/agents/afc-pr-analyst.md +57 -0
- package/agents/afc-security.md +5 -0
- package/commands/auto.md +1 -1
- package/commands/doctor.md +63 -22
- package/commands/implement.md +14 -10
- package/commands/init.md +42 -21
- package/commands/pr-comment.md +181 -0
- package/commands/qa.md +191 -0
- package/commands/release-notes.md +219 -0
- package/commands/resume.md +1 -1
- package/commands/triage.md +222 -0
- package/docs/phase-gate-protocol.md +1 -1
- package/package.json +6 -4
- package/scripts/afc-bash-guard.sh +3 -3
- package/scripts/afc-blast-radius.sh +41 -119
- 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-subagent-context.sh +1 -1
- package/scripts/afc-sync-cache.sh +49 -0
- package/scripts/afc-triage.sh +142 -0
- package/scripts/afc-user-prompt-submit.sh +9 -4
- package/scripts/pre-compact-checkpoint.sh +2 -2
- package/scripts/session-start-context.sh +27 -1
- package/scripts/track-afc-changes.sh +3 -3
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Triage Metadata Collector: Gathers PR and issue metadata via gh CLI
|
|
5
|
+
#
|
|
6
|
+
# Usage: afc-triage.sh [--pr|--issue|--all|#N #M ...]
|
|
7
|
+
# --pr Collect PRs only
|
|
8
|
+
# --issue Collect issues only
|
|
9
|
+
# --all Collect both (default)
|
|
10
|
+
# #N #M Collect specific items by number
|
|
11
|
+
#
|
|
12
|
+
# Output: JSON object with "prs" and "issues" arrays to stdout
|
|
13
|
+
# Exit 0: success
|
|
14
|
+
# Exit 1: gh CLI not available or API error
|
|
15
|
+
|
|
16
|
+
# shellcheck disable=SC2329
|
|
17
|
+
cleanup() {
|
|
18
|
+
:
|
|
19
|
+
}
|
|
20
|
+
trap cleanup EXIT
|
|
21
|
+
|
|
22
|
+
# ── Check prerequisites ──────────────────────────────────
|
|
23
|
+
|
|
24
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
25
|
+
printf '[afc:triage] Error: gh CLI not found. Install from https://cli.github.com/\n' >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if ! gh auth status >/dev/null 2>&1; then
|
|
30
|
+
printf '[afc:triage] Error: gh not authenticated. Run: gh auth login\n' >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# ── Parse arguments ──────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
MODE="all"
|
|
37
|
+
SPECIFIC_NUMBERS=()
|
|
38
|
+
DEEP_FLAG="false"
|
|
39
|
+
|
|
40
|
+
for arg in "$@"; do
|
|
41
|
+
case "$arg" in
|
|
42
|
+
--pr) MODE="pr" ;;
|
|
43
|
+
--issue) MODE="issue" ;;
|
|
44
|
+
--all) MODE="all" ;;
|
|
45
|
+
--deep) DEEP_FLAG="true" ;;
|
|
46
|
+
\#*)
|
|
47
|
+
# Strip leading # and add number
|
|
48
|
+
num="${arg#\#}"
|
|
49
|
+
if [[ "$num" =~ ^[0-9]+$ ]]; then
|
|
50
|
+
SPECIFIC_NUMBERS+=("$num")
|
|
51
|
+
fi
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
# Try as plain number
|
|
55
|
+
if [[ "$arg" =~ ^[0-9]+$ ]]; then
|
|
56
|
+
SPECIFIC_NUMBERS+=("$arg")
|
|
57
|
+
fi
|
|
58
|
+
;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
# ── Collect metadata ─────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
PR_JSON="[]"
|
|
65
|
+
ISSUE_JSON="[]"
|
|
66
|
+
|
|
67
|
+
if [ ${#SPECIFIC_NUMBERS[@]} -gt 0 ]; then
|
|
68
|
+
# Specific items: try each as PR first, then as issue
|
|
69
|
+
PR_ITEMS="[]"
|
|
70
|
+
ISSUE_ITEMS="[]"
|
|
71
|
+
|
|
72
|
+
for num in "${SPECIFIC_NUMBERS[@]}"; do
|
|
73
|
+
# Try as PR
|
|
74
|
+
pr_data=""
|
|
75
|
+
pr_data=$(gh pr view "$num" --json number,title,headRefName,author,labels,additions,deletions,changedFiles,createdAt,updatedAt,reviewDecision,isDraft 2>/dev/null || true)
|
|
76
|
+
|
|
77
|
+
if [ -n "$pr_data" ]; then
|
|
78
|
+
if command -v jq >/dev/null 2>&1; then
|
|
79
|
+
PR_ITEMS=$(printf '%s\n' "$PR_ITEMS" | jq --argjson item "$pr_data" '. + [$item]')
|
|
80
|
+
else
|
|
81
|
+
# Fallback: accumulate newline-delimited JSON objects
|
|
82
|
+
if [ "$PR_ITEMS" = "[]" ]; then
|
|
83
|
+
PR_ITEMS="$pr_data"
|
|
84
|
+
else
|
|
85
|
+
PR_ITEMS="$PR_ITEMS
|
|
86
|
+
$pr_data"
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
else
|
|
90
|
+
# Try as issue
|
|
91
|
+
issue_data=""
|
|
92
|
+
issue_data=$(gh issue view "$num" --json number,title,labels,author,createdAt,updatedAt,comments 2>/dev/null || true)
|
|
93
|
+
|
|
94
|
+
if [ -n "$issue_data" ]; then
|
|
95
|
+
if command -v jq >/dev/null 2>&1; then
|
|
96
|
+
ISSUE_ITEMS=$(printf '%s\n' "$ISSUE_ITEMS" | jq --argjson item "$issue_data" '. + [$item]')
|
|
97
|
+
else
|
|
98
|
+
# Fallback: accumulate newline-delimited JSON objects
|
|
99
|
+
if [ "$ISSUE_ITEMS" = "[]" ]; then
|
|
100
|
+
ISSUE_ITEMS="$issue_data"
|
|
101
|
+
else
|
|
102
|
+
ISSUE_ITEMS="$ISSUE_ITEMS
|
|
103
|
+
$issue_data"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
else
|
|
107
|
+
printf '[afc:triage] Warning: #%s not found as PR or issue\n' "$num" >&2
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
|
|
112
|
+
PR_JSON="$PR_ITEMS"
|
|
113
|
+
ISSUE_JSON="$ISSUE_ITEMS"
|
|
114
|
+
else
|
|
115
|
+
# Bulk collection by mode
|
|
116
|
+
if [ "$MODE" = "pr" ] || [ "$MODE" = "all" ]; then
|
|
117
|
+
PR_JSON=$(gh pr list --json number,title,headRefName,author,labels,additions,deletions,changedFiles,createdAt,updatedAt,reviewDecision,isDraft --limit 50 2>/dev/null || printf '[]')
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if [ "$MODE" = "issue" ] || [ "$MODE" = "all" ]; then
|
|
121
|
+
ISSUE_JSON=$(gh issue list --json number,title,labels,author,createdAt,updatedAt,comments --limit 50 2>/dev/null || printf '[]')
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# ── Build output ─────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
if command -v jq >/dev/null 2>&1; then
|
|
128
|
+
jq -n \
|
|
129
|
+
--argjson prs "$PR_JSON" \
|
|
130
|
+
--argjson issues "$ISSUE_JSON" \
|
|
131
|
+
--arg deep "$DEEP_FLAG" \
|
|
132
|
+
'{prs: $prs, issues: $issues, deep: ($deep == "true"), collectedAt: now | todate}'
|
|
133
|
+
else
|
|
134
|
+
# Fallback: construct JSON manually
|
|
135
|
+
printf '{"prs":%s,"issues":%s,"deep":%s,"collectedAt":"%s"}\n' \
|
|
136
|
+
"$PR_JSON" \
|
|
137
|
+
"$ISSUE_JSON" \
|
|
138
|
+
"$DEEP_FLAG" \
|
|
139
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
exit 0
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
# UserPromptSubmit Hook:
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# UserPromptSubmit Hook: Two modes of operation:
|
|
5
|
+
# 1. Pipeline INACTIVE: No action (routing handled by CLAUDE.md intent-based skill table)
|
|
6
|
+
# 2. Pipeline ACTIVE: Inject Phase/Feature context + drift checkpoint at thresholds
|
|
7
|
+
# Exit 0 immediately if no action needed (minimize overhead)
|
|
7
8
|
|
|
8
9
|
# shellcheck source=afc-state.sh
|
|
9
10
|
. "$(dirname "$0")/afc-state.sh"
|
|
@@ -17,11 +18,15 @@ trap cleanup EXIT
|
|
|
17
18
|
# Consume stdin (required -- pipe breaks if not consumed)
|
|
18
19
|
cat > /dev/null
|
|
19
20
|
|
|
20
|
-
#
|
|
21
|
+
# --- Branch: Pipeline INACTIVE → no action needed ---
|
|
22
|
+
# Routing is handled by CLAUDE.md intent-based skill routing table.
|
|
23
|
+
# The main model classifies user intent natively — no hook-level keyword matching.
|
|
21
24
|
if ! afc_state_is_active; then
|
|
22
25
|
exit 0
|
|
23
26
|
fi
|
|
24
27
|
|
|
28
|
+
# --- Branch: Pipeline ACTIVE → existing Phase/Feature context ---
|
|
29
|
+
|
|
25
30
|
# Read Feature/Phase + JSON-safe processing (strip special characters)
|
|
26
31
|
FEATURE="$(afc_state_read feature || echo '')"
|
|
27
32
|
FEATURE="$(printf '%s' "$FEATURE" | tr -d '"' | cut -c1-100)"
|
|
@@ -79,14 +79,14 @@ fi
|
|
|
79
79
|
# Guard against empty lists
|
|
80
80
|
if [ -n "$MODIFIED" ]; then
|
|
81
81
|
# shellcheck disable=SC2001
|
|
82
|
-
MODIFIED_LIST=$(
|
|
82
|
+
MODIFIED_LIST=$(printf '%s\n' "$MODIFIED" | sed 's/^/ - /')
|
|
83
83
|
else
|
|
84
84
|
MODIFIED_LIST=" (none)"
|
|
85
85
|
fi
|
|
86
86
|
|
|
87
87
|
if [ -n "$STAGED" ]; then
|
|
88
88
|
# shellcheck disable=SC2001
|
|
89
|
-
STAGED_LIST=$(
|
|
89
|
+
STAGED_LIST=$(printf '%s\n' "$STAGED" | sed 's/^/ - /')
|
|
90
90
|
else
|
|
91
91
|
STAGED_LIST=" (none)"
|
|
92
92
|
fi
|
|
@@ -49,6 +49,32 @@ if afc_state_is_active; then
|
|
|
49
49
|
if [ -n "$CI_TIMESTAMP" ]; then
|
|
50
50
|
OUTPUT="$OUTPUT | Last CI: PASSED ($CI_TIMESTAMP)"
|
|
51
51
|
fi
|
|
52
|
+
elif [ -f "$PROJECT_DIR/.claude/.afc-state.json" ]; then
|
|
53
|
+
# 1a. Zombie state cleanup — file exists but afc_state_is_active returned false
|
|
54
|
+
rm -f "$PROJECT_DIR/.claude/.afc-state.json"
|
|
55
|
+
OUTPUT="${OUTPUT:+$OUTPUT | }[ZOMBIE STATE CLEANED] Removed invalid .afc-state.json"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# 1b. Version mismatch detection
|
|
59
|
+
PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
60
|
+
if [ -f "$PLUGIN_ROOT/package.json" ]; then
|
|
61
|
+
# Read plugin version
|
|
62
|
+
if command -v jq >/dev/null 2>&1; then
|
|
63
|
+
PLUGIN_VERSION=$(jq -r '.version // empty' "$PLUGIN_ROOT/package.json" 2>/dev/null || true)
|
|
64
|
+
else
|
|
65
|
+
PLUGIN_VERSION=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$PLUGIN_ROOT/package.json" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"//') || true
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [ -n "${PLUGIN_VERSION:-}" ]; then
|
|
69
|
+
# Read AFC block version from global CLAUDE.md
|
|
70
|
+
GLOBAL_CLAUDE="$HOME/.claude/CLAUDE.md"
|
|
71
|
+
if [ -f "$GLOBAL_CLAUDE" ]; then
|
|
72
|
+
BLOCK_VERSION=$(grep -o 'AFC:VERSION:[0-9][0-9.]*' "$GLOBAL_CLAUDE" 2>/dev/null | head -1 | sed 's/AFC:VERSION://' || true)
|
|
73
|
+
if [ -n "${BLOCK_VERSION:-}" ] && [ "$BLOCK_VERSION" != "$PLUGIN_VERSION" ]; then
|
|
74
|
+
OUTPUT="${OUTPUT:+$OUTPUT | }[AFC VERSION MISMATCH] v$PLUGIN_VERSION installed but CLAUDE.md block is v$BLOCK_VERSION. Run /afc:init to update."
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
52
78
|
fi
|
|
53
79
|
|
|
54
80
|
# 2. Check if checkpoint exists (project-local first, fallback to auto-memory)
|
|
@@ -62,7 +88,7 @@ fi
|
|
|
62
88
|
|
|
63
89
|
if [ -n "$CHECKPOINT_FILE" ]; then
|
|
64
90
|
RAW_LINE=$(grep 'Auto-generated:' "$CHECKPOINT_FILE" 2>/dev/null || echo "")
|
|
65
|
-
FIRST_LINE=$(
|
|
91
|
+
FIRST_LINE=$(printf '%s\n' "$RAW_LINE" | head -1)
|
|
66
92
|
CHECKPOINT_DATE="${FIRST_LINE##*Auto-generated: }"
|
|
67
93
|
if [ -n "$CHECKPOINT_DATE" ]; then
|
|
68
94
|
if [ -n "$OUTPUT" ]; then
|
|
@@ -14,14 +14,14 @@ cleanup() {
|
|
|
14
14
|
}
|
|
15
15
|
trap cleanup EXIT
|
|
16
16
|
|
|
17
|
+
# Consume stdin immediately (prevents SIGPIPE if exiting early)
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
17
20
|
# If pipeline is inactive -> skip
|
|
18
21
|
if ! afc_state_is_active; then
|
|
19
22
|
exit 0
|
|
20
23
|
fi
|
|
21
24
|
|
|
22
|
-
# Parse tool input from stdin
|
|
23
|
-
INPUT=$(cat)
|
|
24
|
-
|
|
25
25
|
# Skip if stdin is empty
|
|
26
26
|
if [ -z "$INPUT" ]; then
|
|
27
27
|
exit 0
|