all-for-claudecode 2.0.0 → 2.2.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 +4 -4
- package/.claude-plugin/plugin.json +3 -4
- package/MIGRATION.md +10 -7
- package/README.md +68 -119
- package/agents/afc-architect.md +16 -0
- package/agents/afc-impl-worker.md +40 -0
- package/agents/afc-security.md +11 -0
- package/bin/cli.mjs +1 -1
- package/commands/analyze.md +6 -7
- package/commands/architect.md +5 -7
- package/commands/auto.md +355 -102
- package/commands/checkpoint.md +3 -4
- package/commands/clarify.md +8 -1
- package/commands/debug.md +40 -3
- package/commands/doctor.md +12 -13
- package/commands/ideate.md +191 -0
- package/commands/implement.md +211 -66
- package/commands/init.md +76 -61
- package/commands/launch.md +181 -0
- package/commands/plan.md +86 -22
- package/commands/principles.md +6 -2
- package/commands/resume.md +1 -2
- package/commands/review.md +68 -18
- package/commands/security.md +10 -13
- package/commands/spec.md +60 -3
- package/commands/tasks.md +19 -4
- package/commands/test.md +24 -6
- package/docs/phase-gate-protocol.md +6 -6
- package/hooks/hooks.json +29 -3
- package/package.json +19 -11
- package/schemas/hooks.schema.json +75 -0
- package/schemas/marketplace.schema.json +52 -0
- package/schemas/plugin.schema.json +53 -0
- package/scripts/afc-bash-guard.sh +6 -6
- package/scripts/afc-blast-radius.sh +418 -0
- package/scripts/afc-config-change.sh +6 -4
- package/scripts/afc-consistency-check.sh +261 -0
- package/scripts/afc-dag-validate.mjs +94 -0
- package/scripts/afc-dag-validate.sh +142 -0
- package/scripts/afc-failure-hint.sh +6 -4
- package/scripts/afc-parallel-validate.mjs +81 -0
- package/scripts/afc-parallel-validate.sh +33 -45
- package/scripts/afc-permission-request.sh +56 -11
- package/scripts/afc-pipeline-manage.sh +46 -46
- package/scripts/afc-preflight-check.sh +6 -3
- package/scripts/afc-schema-validate.sh +225 -0
- package/scripts/afc-session-end.sh +5 -5
- package/scripts/afc-state.sh +256 -0
- package/scripts/afc-stop-gate.sh +32 -24
- package/scripts/afc-subagent-context.sh +15 -6
- package/scripts/afc-subagent-stop.sh +4 -2
- package/scripts/afc-task-completed-gate.sh +19 -25
- package/scripts/afc-teammate-idle.sh +9 -14
- package/scripts/afc-test-pre-gen.sh +141 -0
- package/scripts/afc-timeline-log.sh +9 -6
- package/scripts/afc-user-prompt-submit.sh +8 -10
- package/scripts/afc-worktree-create.sh +56 -0
- package/scripts/afc-worktree-remove.sh +47 -0
- package/scripts/install-shellspec.sh +38 -0
- package/scripts/pre-compact-checkpoint.sh +6 -4
- package/scripts/session-start-context.sh +9 -8
- package/scripts/track-afc-changes.sh +6 -9
- package/templates/afc.config.template.md +12 -76
- package/templates/afc.config.express-api.md +0 -99
- package/templates/afc.config.monorepo.md +0 -98
- package/templates/afc.config.nextjs-fsd.md +0 -107
- package/templates/afc.config.react-spa.md +0 -96
|
@@ -4,6 +4,9 @@ set -euo pipefail
|
|
|
4
4
|
# SubagentStop Hook: Log subagent completion/failure results to pipeline log
|
|
5
5
|
# Enables pipeline orchestrator to track task progress
|
|
6
6
|
|
|
7
|
+
# shellcheck source=afc-state.sh
|
|
8
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
9
|
+
|
|
7
10
|
# shellcheck disable=SC2329
|
|
8
11
|
cleanup() {
|
|
9
12
|
:
|
|
@@ -11,7 +14,6 @@ cleanup() {
|
|
|
11
14
|
trap cleanup EXIT
|
|
12
15
|
|
|
13
16
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
14
|
-
PIPELINE_FLAG="$PROJECT_DIR/.claude/.afc-active"
|
|
15
17
|
RESULTS_LOG="$PROJECT_DIR/.claude/.afc-task-results.log"
|
|
16
18
|
|
|
17
19
|
# Read hook data from stdin
|
|
@@ -34,7 +36,7 @@ if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
|
|
|
34
36
|
fi
|
|
35
37
|
|
|
36
38
|
# Exit silently if pipeline is inactive
|
|
37
|
-
if
|
|
39
|
+
if ! afc_state_is_active; then
|
|
38
40
|
exit 0
|
|
39
41
|
fi
|
|
40
42
|
|
|
@@ -5,60 +5,54 @@ set -euo pipefail
|
|
|
5
5
|
#
|
|
6
6
|
# Gap fix: "Prompts are not enforcement" -> Physical enforcement via exit 2
|
|
7
7
|
|
|
8
|
+
# shellcheck source=afc-state.sh
|
|
9
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
10
|
+
|
|
8
11
|
# trap: Preserve exit code on abnormal termination + stderr message
|
|
9
12
|
# shellcheck disable=SC2329
|
|
10
13
|
cleanup() {
|
|
11
14
|
local exit_code=$?
|
|
12
15
|
if [ "$exit_code" -ne 0 ] && [ "$exit_code" -ne 2 ]; then
|
|
13
|
-
echo "
|
|
16
|
+
echo "[afc:task-gate] Abnormal exit (code: $exit_code)" >&2
|
|
14
17
|
fi
|
|
15
18
|
exit "$exit_code"
|
|
16
19
|
}
|
|
17
20
|
trap cleanup EXIT
|
|
18
21
|
|
|
19
|
-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
20
|
-
PIPELINE_FLAG="${PROJECT_DIR}/.claude/.afc-active"
|
|
21
|
-
CI_FLAG="${PROJECT_DIR}/.claude/.afc-ci-passed"
|
|
22
|
-
PHASE_FLAG="${PROJECT_DIR}/.claude/.afc-phase"
|
|
23
|
-
|
|
24
22
|
# Consume stdin (required -- pipe breaks if not consumed)
|
|
25
23
|
cat > /dev/null
|
|
26
24
|
|
|
27
25
|
# If pipeline is not active -> pass through
|
|
28
|
-
if
|
|
26
|
+
if ! afc_state_is_active; then
|
|
29
27
|
exit 0
|
|
30
28
|
fi
|
|
31
29
|
|
|
32
|
-
FEATURE="$(
|
|
30
|
+
FEATURE="$(afc_state_read feature || echo '')"
|
|
33
31
|
|
|
34
|
-
# Check current Phase
|
|
35
|
-
CURRENT_PHASE=""
|
|
36
|
-
if [ -f "$PHASE_FLAG" ]; then
|
|
37
|
-
CURRENT_PHASE="$(head -1 "$PHASE_FLAG" | tr -d '\n\r')"
|
|
38
|
-
fi
|
|
39
|
-
CURRENT_PHASE="${CURRENT_PHASE:-}"
|
|
32
|
+
# Check current Phase
|
|
33
|
+
CURRENT_PHASE="$(afc_state_read phase || echo '')"
|
|
40
34
|
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
;;
|
|
46
|
-
esac
|
|
35
|
+
# Preparatory phases do not require CI -> pass through
|
|
36
|
+
if afc_is_ci_exempt "${CURRENT_PHASE:-}"; then
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
47
39
|
|
|
48
40
|
# Implement/Review/Clean Phase (4-6) require CI to pass
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
CI_TIME="$(afc_state_read ciPassedAt 2>/dev/null || echo '')"
|
|
42
|
+
CI_TIME="$(printf '%s' "$CI_TIME" | tr -dc '0-9')"
|
|
43
|
+
CI_TIME="${CI_TIME:-0}"
|
|
44
|
+
|
|
45
|
+
if [ "$CI_TIME" -eq 0 ]; then
|
|
46
|
+
printf "[afc:task-gate] CI has not been run. Pipeline '%s' Phase '%s' requires CI gate.\n → Run your CI command to pass the gate\n" "${FEATURE:-unknown}" "${CURRENT_PHASE:-unknown}" >&2
|
|
51
47
|
exit 2
|
|
52
48
|
fi
|
|
53
49
|
|
|
54
50
|
# Verify CI passed within the last 10 minutes (prevent stale results)
|
|
55
|
-
CI_TIME="$(cat "$CI_FLAG" 2>/dev/null | head -1 | tr -dc '0-9' || true)"
|
|
56
|
-
CI_TIME="${CI_TIME:-0}"
|
|
57
51
|
NOW="$(date +%s)"
|
|
58
52
|
if [ "$CI_TIME" -gt 0 ]; then
|
|
59
53
|
DIFF=$(( NOW - CI_TIME ))
|
|
60
54
|
if [ "$DIFF" -gt 600 ]; then
|
|
61
|
-
|
|
55
|
+
printf "[afc:task-gate] CI results are stale (%ss ago).\n → Run your CI command again\n" "$DIFF" >&2
|
|
62
56
|
exit 2
|
|
63
57
|
fi
|
|
64
58
|
fi
|
|
@@ -5,42 +5,37 @@ set -euo pipefail
|
|
|
5
5
|
#
|
|
6
6
|
# Gap fix: "Prompts are not enforcement" -> Physical enforcement via exit 2
|
|
7
7
|
|
|
8
|
+
# shellcheck source=afc-state.sh
|
|
9
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
10
|
+
|
|
8
11
|
# trap: Preserve exit code on abnormal termination + stderr message
|
|
9
12
|
# shellcheck disable=SC2329
|
|
10
13
|
cleanup() {
|
|
11
14
|
local exit_code=$?
|
|
12
15
|
if [ "$exit_code" -ne 0 ] && [ "$exit_code" -ne 2 ]; then
|
|
13
|
-
echo "
|
|
16
|
+
echo "[afc:teammate] Abnormal exit (code: $exit_code)" >&2
|
|
14
17
|
fi
|
|
15
18
|
exit "$exit_code"
|
|
16
19
|
}
|
|
17
20
|
trap cleanup EXIT
|
|
18
21
|
|
|
19
|
-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
20
|
-
PIPELINE_FLAG="${PROJECT_DIR}/.claude/.afc-active"
|
|
21
|
-
PHASE_FLAG="${PROJECT_DIR}/.claude/.afc-phase"
|
|
22
|
-
|
|
23
22
|
# Consume stdin (required -- pipe breaks if not consumed)
|
|
24
23
|
cat > /dev/null
|
|
25
24
|
|
|
26
25
|
# If pipeline is not active -> pass through
|
|
27
|
-
if
|
|
26
|
+
if ! afc_state_is_active; then
|
|
28
27
|
exit 0
|
|
29
28
|
fi
|
|
30
29
|
|
|
31
|
-
FEATURE="$(
|
|
30
|
+
FEATURE="$(afc_state_read feature || echo '')"
|
|
32
31
|
|
|
33
|
-
# Check current Phase
|
|
34
|
-
CURRENT_PHASE=""
|
|
35
|
-
if [ -f "$PHASE_FLAG" ]; then
|
|
36
|
-
CURRENT_PHASE="$(head -1 "$PHASE_FLAG" | tr -d '\n\r')"
|
|
37
|
-
fi
|
|
38
|
-
CURRENT_PHASE="${CURRENT_PHASE:-}"
|
|
32
|
+
# Check current Phase
|
|
33
|
+
CURRENT_PHASE="$(afc_state_read phase || echo '')"
|
|
39
34
|
|
|
40
35
|
# Block idle during implement/review Phase -> force work to continue
|
|
41
36
|
case "${CURRENT_PHASE:-}" in
|
|
42
37
|
implement|review)
|
|
43
|
-
|
|
38
|
+
printf "[afc:teammate] Pipeline '%s' Phase '%s' is active.\n → Complete the current task before going idle\n" "${FEATURE:-unknown}" "${CURRENT_PHASE:-unknown}" >&2
|
|
44
39
|
exit 2
|
|
45
40
|
;;
|
|
46
41
|
*)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# afc-test-pre-gen.sh — Generate ShellSpec test skeletons for testable tasks
|
|
5
|
+
# Reads a tasks.md file, identifies tasks targeting .sh scripts, and generates
|
|
6
|
+
# pending ShellSpec spec files for scripts that lack test coverage.
|
|
7
|
+
#
|
|
8
|
+
# Usage: afc-test-pre-gen.sh <tasks_file> [output_dir]
|
|
9
|
+
# tasks_file : path to a tasks.md file
|
|
10
|
+
# output_dir : directory for generated spec files (default: spec/ relative to project root)
|
|
11
|
+
# Exit: 0 = success, 1 = error
|
|
12
|
+
|
|
13
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
14
|
+
|
|
15
|
+
# shellcheck disable=SC2329
|
|
16
|
+
cleanup() {
|
|
17
|
+
:
|
|
18
|
+
}
|
|
19
|
+
trap cleanup EXIT
|
|
20
|
+
|
|
21
|
+
# --- Argument validation ---
|
|
22
|
+
TASKS_FILE="${1:-}"
|
|
23
|
+
if [ -z "$TASKS_FILE" ]; then
|
|
24
|
+
printf '[afc:test-pre-gen] Usage: %s <tasks_file> [output_dir]\n' "$0" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if [ ! -f "$TASKS_FILE" ]; then
|
|
29
|
+
printf '[afc:test-pre-gen] Error: file not found: %s\n' "$TASKS_FILE" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
OUTPUT_DIR="${2:-${PROJECT_DIR}/spec}"
|
|
34
|
+
mkdir -p "$OUTPUT_DIR"
|
|
35
|
+
|
|
36
|
+
# --- Counters ---
|
|
37
|
+
TASKS_ANALYZED=0
|
|
38
|
+
TESTABLE_SH=0
|
|
39
|
+
SKIPPED_NON_SH=0
|
|
40
|
+
ALREADY_EXISTS=0
|
|
41
|
+
GENERATED=0
|
|
42
|
+
GENERATED_FILES=""
|
|
43
|
+
|
|
44
|
+
# --- Parse tasks ---
|
|
45
|
+
# Match lines like: - [ ] T001 ... `scripts/some-name.sh`
|
|
46
|
+
# Also handles: - [x] T001 ... (checked tasks are still analyzed)
|
|
47
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
48
|
+
# Only match task lines: - [ ] TNNN or - [x] TNNN
|
|
49
|
+
if ! printf '%s\n' "$line" | grep -qE '^\s*-\s*\[[ xX]\]\s+T[0-9]+'; then
|
|
50
|
+
continue
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
TASKS_ANALYZED=$((TASKS_ANALYZED + 1))
|
|
54
|
+
|
|
55
|
+
# Extract all backtick-quoted file paths from the line
|
|
56
|
+
# shellcheck disable=SC2207,SC2016
|
|
57
|
+
PATHS=($(printf '%s\n' "$line" | grep -oE '`[^`]+`' | tr -d '`'))
|
|
58
|
+
|
|
59
|
+
if [ ${#PATHS[@]} -eq 0 ]; then
|
|
60
|
+
continue
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
HAS_SH_TARGET=false
|
|
64
|
+
for fpath in "${PATHS[@]}"; do
|
|
65
|
+
# Only process .sh files under scripts/
|
|
66
|
+
case "$fpath" in
|
|
67
|
+
scripts/*.sh)
|
|
68
|
+
HAS_SH_TARGET=true
|
|
69
|
+
TESTABLE_SH=$((TESTABLE_SH + 1))
|
|
70
|
+
|
|
71
|
+
# Extract script name from path (e.g., scripts/afc-blast-radius.sh -> afc-blast-radius.sh)
|
|
72
|
+
SCRIPT_NAME="${fpath##*/}"
|
|
73
|
+
# Derive spec name (e.g., afc-blast-radius.sh -> afc-blast-radius_spec.sh)
|
|
74
|
+
SPEC_NAME="${SCRIPT_NAME%.sh}_spec.sh"
|
|
75
|
+
SPEC_PATH="${OUTPUT_DIR}/${SPEC_NAME}"
|
|
76
|
+
|
|
77
|
+
# Check if spec already exists
|
|
78
|
+
if [ -f "$SPEC_PATH" ]; then
|
|
79
|
+
ALREADY_EXISTS=$((ALREADY_EXISTS + 1))
|
|
80
|
+
printf '[afc:test-pre-gen] Skip (exists): %s\n' "$SPEC_NAME" >&2
|
|
81
|
+
continue
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Generate ShellSpec skeleton
|
|
85
|
+
cat > "$SPEC_PATH" << SKELETON
|
|
86
|
+
#!/bin/bash
|
|
87
|
+
# shellcheck shell=bash
|
|
88
|
+
# Auto-generated test skeleton for ${SCRIPT_NAME}
|
|
89
|
+
# TODO: Replace Pending examples with real tests
|
|
90
|
+
|
|
91
|
+
Describe "${SCRIPT_NAME}"
|
|
92
|
+
setup() {
|
|
93
|
+
setup_tmpdir TEST_DIR
|
|
94
|
+
}
|
|
95
|
+
cleanup() { cleanup_tmpdir "\$TEST_DIR"; }
|
|
96
|
+
Before "setup"
|
|
97
|
+
After "cleanup"
|
|
98
|
+
|
|
99
|
+
Context "basic usage"
|
|
100
|
+
It "exits 0 on valid input"
|
|
101
|
+
Pending "implement test"
|
|
102
|
+
End
|
|
103
|
+
|
|
104
|
+
It "exits 1 on missing arguments"
|
|
105
|
+
Pending "implement test"
|
|
106
|
+
End
|
|
107
|
+
End
|
|
108
|
+
End
|
|
109
|
+
SKELETON
|
|
110
|
+
|
|
111
|
+
GENERATED=$((GENERATED + 1))
|
|
112
|
+
if [ -n "$GENERATED_FILES" ]; then
|
|
113
|
+
GENERATED_FILES="${GENERATED_FILES}, ${SPEC_NAME}"
|
|
114
|
+
else
|
|
115
|
+
GENERATED_FILES="${SPEC_NAME}"
|
|
116
|
+
fi
|
|
117
|
+
printf '[afc:test-pre-gen] Generated: %s\n' "$SPEC_NAME" >&2
|
|
118
|
+
;;
|
|
119
|
+
*)
|
|
120
|
+
# Non-.sh file — counted once per task below
|
|
121
|
+
;;
|
|
122
|
+
esac
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
if [ "$HAS_SH_TARGET" = false ]; then
|
|
126
|
+
SKIPPED_NON_SH=$((SKIPPED_NON_SH + 1))
|
|
127
|
+
fi
|
|
128
|
+
done < "$TASKS_FILE"
|
|
129
|
+
|
|
130
|
+
# --- Summary report ---
|
|
131
|
+
printf 'Test pre-generation:\n'
|
|
132
|
+
printf ' Tasks analyzed: %d\n' "$TASKS_ANALYZED"
|
|
133
|
+
printf ' Testable (.sh): %d\n' "$TESTABLE_SH"
|
|
134
|
+
printf ' Skipped (non-.sh): %d\n' "$SKIPPED_NON_SH"
|
|
135
|
+
printf ' Already exists: %d\n' "$ALREADY_EXISTS"
|
|
136
|
+
printf ' Generated: %d skeletons\n' "$GENERATED"
|
|
137
|
+
if [ -n "$GENERATED_FILES" ]; then
|
|
138
|
+
printf ' Files: %s\n' "$GENERATED_FILES"
|
|
139
|
+
else
|
|
140
|
+
printf ' Files: (none)\n'
|
|
141
|
+
fi
|
|
@@ -10,6 +10,9 @@ set -euo pipefail
|
|
|
10
10
|
# message: human-readable description
|
|
11
11
|
# extra_json_fields: optional JSON object string to merge (e.g. '{"tool":"bash"}')
|
|
12
12
|
|
|
13
|
+
# shellcheck source=afc-state.sh
|
|
14
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
15
|
+
|
|
13
16
|
# shellcheck disable=SC2329
|
|
14
17
|
cleanup() {
|
|
15
18
|
:
|
|
@@ -36,14 +39,14 @@ fi
|
|
|
36
39
|
EVENT_TYPE=$(printf '%s' "$EVENT_TYPE" | tr -d '\n\r' | cut -c1-64)
|
|
37
40
|
MESSAGE=$(printf '%s' "$MESSAGE" | tr -d '\n\r' | cut -c1-500)
|
|
38
41
|
|
|
39
|
-
# Read pipeline state (gracefully handle missing
|
|
42
|
+
# Read pipeline state (gracefully handle missing state)
|
|
40
43
|
FEATURE="none"
|
|
41
44
|
PHASE="none"
|
|
42
|
-
if
|
|
43
|
-
FEATURE=$(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
PHASE=$(
|
|
45
|
+
if afc_state_is_active; then
|
|
46
|
+
FEATURE=$(afc_state_read feature || echo "none")
|
|
47
|
+
FEATURE=$(printf '%s' "$FEATURE" | cut -c1-100)
|
|
48
|
+
PHASE=$(afc_state_read phase || echo "none")
|
|
49
|
+
PHASE=$(printf '%s' "$PHASE" | cut -c1-64)
|
|
47
50
|
fi
|
|
48
51
|
|
|
49
52
|
# Timestamp (no jq dependency)
|
|
@@ -4,30 +4,28 @@ set -euo pipefail
|
|
|
4
4
|
# UserPromptSubmit Hook: Inject pipeline Phase/Feature context on every prompt
|
|
5
5
|
# Exit 0 immediately if pipeline is inactive (minimize overhead)
|
|
6
6
|
|
|
7
|
+
# shellcheck source=afc-state.sh
|
|
8
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
9
|
+
|
|
7
10
|
# shellcheck disable=SC2329
|
|
8
11
|
cleanup() {
|
|
9
12
|
:
|
|
10
13
|
}
|
|
11
14
|
trap cleanup EXIT
|
|
12
15
|
|
|
13
|
-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
14
|
-
PIPELINE_FLAG="$PROJECT_DIR/.claude/.afc-active"
|
|
15
|
-
PHASE_FLAG="$PROJECT_DIR/.claude/.afc-phase"
|
|
16
|
-
|
|
17
16
|
# Consume stdin (required -- pipe breaks if not consumed)
|
|
18
17
|
cat > /dev/null
|
|
19
18
|
|
|
20
19
|
# Exit silently if pipeline is inactive
|
|
21
|
-
if
|
|
20
|
+
if ! afc_state_is_active; then
|
|
22
21
|
exit 0
|
|
23
22
|
fi
|
|
24
23
|
|
|
25
24
|
# Read Feature/Phase + JSON-safe processing (strip special characters)
|
|
26
|
-
FEATURE="$(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
fi
|
|
25
|
+
FEATURE="$(afc_state_read feature || echo '')"
|
|
26
|
+
FEATURE="$(printf '%s' "$FEATURE" | tr -d '"' | cut -c1-100)"
|
|
27
|
+
PHASE="$(afc_state_read phase || echo 'unknown')"
|
|
28
|
+
PHASE="$(printf '%s' "$PHASE" | tr -d '"' | cut -c1-100)"
|
|
31
29
|
|
|
32
30
|
# Output additionalContext to stdout (injected into Claude context)
|
|
33
31
|
printf '{"hookSpecificOutput":{"additionalContext":"[Pipeline: %s] [Phase: %s]"}}\n' "$FEATURE" "$PHASE"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# WorktreeCreate Hook: Inject pipeline context into new worktrees
|
|
5
|
+
# Ensures worker worktrees have access to pipeline state and config
|
|
6
|
+
|
|
7
|
+
# shellcheck source=afc-state.sh
|
|
8
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
9
|
+
|
|
10
|
+
# shellcheck disable=SC2329
|
|
11
|
+
cleanup() {
|
|
12
|
+
:
|
|
13
|
+
}
|
|
14
|
+
trap cleanup EXIT
|
|
15
|
+
|
|
16
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
17
|
+
|
|
18
|
+
# Read hook data from stdin
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
|
|
21
|
+
# Exit silently if pipeline is inactive
|
|
22
|
+
if ! afc_state_is_active; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Parse worktree_path from stdin JSON
|
|
27
|
+
WORKTREE_PATH=""
|
|
28
|
+
if command -v jq &>/dev/null; then
|
|
29
|
+
WORKTREE_PATH=$(printf '%s\n' "$INPUT" | jq -r '.worktree_path // empty' 2>/dev/null || true)
|
|
30
|
+
else
|
|
31
|
+
WORKTREE_PATH=$(printf '%s\n' "$INPUT" | grep -o '"worktree_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:[[:space:]]*"//;s/"$//' 2>/dev/null || true)
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Exit if no worktree path provided
|
|
35
|
+
if [ -z "$WORKTREE_PATH" ]; then
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Read pipeline state
|
|
40
|
+
FEATURE=$(afc_state_read feature || echo "unknown")
|
|
41
|
+
PHASE=$(afc_state_read phase || echo "unknown")
|
|
42
|
+
|
|
43
|
+
# Inject pipeline context into worktree
|
|
44
|
+
CONTEXT="[AFC WORKTREE] Feature: $FEATURE | Phase: ${PHASE:-unknown} | Source: $PROJECT_DIR"
|
|
45
|
+
|
|
46
|
+
# Output as hookSpecificOutput JSON
|
|
47
|
+
if command -v jq &>/dev/null; then
|
|
48
|
+
jq -n --arg ctx "$CONTEXT" \
|
|
49
|
+
'{"hookSpecificOutput":{"additionalContext":$ctx}}' 2>/dev/null || true
|
|
50
|
+
else
|
|
51
|
+
# shellcheck disable=SC1003
|
|
52
|
+
SAFE_CONTEXT=$(printf '%s' "$CONTEXT" | tr -d '"' | tr -d '\\' | cut -c1-2000)
|
|
53
|
+
printf '{"hookSpecificOutput":{"additionalContext":"%s"}}\n' "$SAFE_CONTEXT"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# WorktreeRemove Hook: Archive worker results when worktree is removed
|
|
5
|
+
# Ensures task results are preserved in the main project log
|
|
6
|
+
|
|
7
|
+
# shellcheck source=afc-state.sh
|
|
8
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
9
|
+
|
|
10
|
+
# shellcheck disable=SC2329
|
|
11
|
+
cleanup() {
|
|
12
|
+
:
|
|
13
|
+
}
|
|
14
|
+
trap cleanup EXIT
|
|
15
|
+
|
|
16
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
17
|
+
RESULTS_LOG="$PROJECT_DIR/.claude/.afc-task-results.log"
|
|
18
|
+
|
|
19
|
+
# Read hook data from stdin
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
|
|
22
|
+
# Exit silently if pipeline is inactive (also handles race condition on pipeline end)
|
|
23
|
+
if ! afc_state_is_active; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Parse worktree_path from stdin JSON
|
|
28
|
+
WORKTREE_PATH=""
|
|
29
|
+
if command -v jq &>/dev/null; then
|
|
30
|
+
WORKTREE_PATH=$(printf '%s\n' "$INPUT" | jq -r '.worktree_path // empty' 2>/dev/null || true)
|
|
31
|
+
else
|
|
32
|
+
WORKTREE_PATH=$(printf '%s\n' "$INPUT" | grep -o '"worktree_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:[[:space:]]*"//;s/"$//' 2>/dev/null || true)
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Exit if no worktree path provided
|
|
36
|
+
if [ -z "$WORKTREE_PATH" ]; then
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Archive worktree task results to main project
|
|
41
|
+
WORKTREE_RESULTS="$WORKTREE_PATH/.claude/.afc-task-results.log"
|
|
42
|
+
if [ -f "$WORKTREE_RESULTS" ]; then
|
|
43
|
+
printf '%s\n' "--- worktree: $WORKTREE_PATH ---" >> "$RESULTS_LOG"
|
|
44
|
+
cat "$WORKTREE_RESULTS" >> "$RESULTS_LOG" 2>/dev/null || true
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Install ShellSpec locally to vendor/shellspec/
|
|
3
|
+
# Usage: bash scripts/install-shellspec.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
trap cleanup EXIT
|
|
7
|
+
cleanup() { :; }
|
|
8
|
+
|
|
9
|
+
SHELLSPEC_VERSION="0.28.1"
|
|
10
|
+
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
11
|
+
VENDOR_DIR="$PROJECT_ROOT/vendor"
|
|
12
|
+
SHELLSPEC_DIR="$VENDOR_DIR/shellspec"
|
|
13
|
+
|
|
14
|
+
if [ -f "$SHELLSPEC_DIR/shellspec" ]; then
|
|
15
|
+
printf 'ShellSpec %s already installed at %s\n' "$SHELLSPEC_VERSION" "$SHELLSPEC_DIR"
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
printf 'Installing ShellSpec %s to %s ...\n' "$SHELLSPEC_VERSION" "$SHELLSPEC_DIR"
|
|
20
|
+
|
|
21
|
+
mkdir -p "$VENDOR_DIR"
|
|
22
|
+
|
|
23
|
+
TARBALL_URL="https://github.com/shellspec/shellspec/archive/refs/tags/${SHELLSPEC_VERSION}.tar.gz"
|
|
24
|
+
|
|
25
|
+
if command -v curl >/dev/null 2>&1; then
|
|
26
|
+
curl -fsSL "$TARBALL_URL" | tar -xzf - -C "$VENDOR_DIR"
|
|
27
|
+
elif command -v wget >/dev/null 2>&1; then
|
|
28
|
+
wget -qO - "$TARBALL_URL" | tar -xzf - -C "$VENDOR_DIR"
|
|
29
|
+
else
|
|
30
|
+
printf '[afc] Error: curl or wget is required to install ShellSpec\n' >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
mv "$VENDOR_DIR/shellspec-${SHELLSPEC_VERSION}" "$SHELLSPEC_DIR"
|
|
35
|
+
chmod +x "$SHELLSPEC_DIR/shellspec"
|
|
36
|
+
|
|
37
|
+
printf 'ShellSpec %s installed successfully at %s\n' "$SHELLSPEC_VERSION" "$SHELLSPEC_DIR"
|
|
38
|
+
printf 'Run tests with: npm test\n'
|
|
@@ -5,6 +5,9 @@ set -euo pipefail
|
|
|
5
5
|
#
|
|
6
6
|
# Gap fix: Enforces OMC auto-state-save via physical script
|
|
7
7
|
|
|
8
|
+
# shellcheck source=afc-state.sh
|
|
9
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
10
|
+
|
|
8
11
|
# shellcheck disable=SC2329
|
|
9
12
|
cleanup() {
|
|
10
13
|
# Extend here if temporary file cleanup is needed
|
|
@@ -50,10 +53,9 @@ if [ -n "$ALL_STAGED" ]; then
|
|
|
50
53
|
fi
|
|
51
54
|
|
|
52
55
|
# Check afc pipeline active status
|
|
53
|
-
PIPELINE_FLAG="$PROJECT_DIR/.claude/.afc-active"
|
|
54
56
|
PIPELINE_FEATURE=""
|
|
55
|
-
if
|
|
56
|
-
PIPELINE_FEATURE=$(
|
|
57
|
+
if afc_state_is_active; then
|
|
58
|
+
PIPELINE_FEATURE=$(afc_state_read feature || true)
|
|
57
59
|
fi
|
|
58
60
|
|
|
59
61
|
# Check tasks.md progress status
|
|
@@ -97,7 +99,7 @@ $MODIFIED_LIST
|
|
|
97
99
|
$STAGED_LIST
|
|
98
100
|
|
|
99
101
|
## Pipeline Status
|
|
100
|
-
- Active: $([ -
|
|
102
|
+
- Active: $([ -n "$PIPELINE_FEATURE" ] && echo "Yes ($PIPELINE_FEATURE)" || echo "No")
|
|
101
103
|
- Task progress: $TASKS_DONE/$TASKS_TOTAL
|
|
102
104
|
|
|
103
105
|
## Restore Command
|
|
@@ -6,11 +6,14 @@ set -euo pipefail
|
|
|
6
6
|
#
|
|
7
7
|
# Gap fix: Enforces OMC session continuity via physical script
|
|
8
8
|
|
|
9
|
+
# shellcheck source=afc-state.sh
|
|
10
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
11
|
+
|
|
9
12
|
# shellcheck disable=SC2329
|
|
10
13
|
cleanup() {
|
|
11
14
|
local exit_code=$?
|
|
12
15
|
if [ "$exit_code" -ne 0 ]; then
|
|
13
|
-
echo "[
|
|
16
|
+
echo "[afc:session] session-start-context.sh exited abnormally" >&2
|
|
14
17
|
fi
|
|
15
18
|
exit "$exit_code"
|
|
16
19
|
}
|
|
@@ -26,13 +29,11 @@ PROJECT_PATH=$(cd "$PROJECT_DIR" 2>/dev/null && pwd || echo "$PROJECT_DIR")
|
|
|
26
29
|
ENCODED_PATH="${PROJECT_PATH//\//-}"
|
|
27
30
|
MEMORY_DIR="$HOME/.claude/projects/$ENCODED_PATH/memory"
|
|
28
31
|
CHECKPOINT="$MEMORY_DIR/checkpoint.md"
|
|
29
|
-
PIPELINE_FLAG="$PROJECT_DIR/.claude/.afc-active"
|
|
30
|
-
|
|
31
32
|
OUTPUT=""
|
|
32
33
|
|
|
33
34
|
# 1. Check for active pipeline
|
|
34
|
-
if
|
|
35
|
-
FEATURE=$(
|
|
35
|
+
if afc_state_is_active; then
|
|
36
|
+
FEATURE=$(afc_state_read feature || true)
|
|
36
37
|
OUTPUT="[AFC PIPELINE ACTIVE] Feature: $FEATURE"
|
|
37
38
|
|
|
38
39
|
# tasks.md progress
|
|
@@ -44,9 +45,9 @@ if [ -f "$PIPELINE_FLAG" ]; then
|
|
|
44
45
|
fi
|
|
45
46
|
|
|
46
47
|
# CI pass status
|
|
47
|
-
|
|
48
|
-
if [ -
|
|
49
|
-
OUTPUT="$OUTPUT | Last CI: PASSED ($
|
|
48
|
+
CI_TIMESTAMP=$(afc_state_read ciPassedAt 2>/dev/null || true)
|
|
49
|
+
if [ -n "$CI_TIMESTAMP" ]; then
|
|
50
|
+
OUTPUT="$OUTPUT | Last CI: PASSED ($CI_TIMESTAMP)"
|
|
50
51
|
fi
|
|
51
52
|
fi
|
|
52
53
|
|
|
@@ -4,10 +4,8 @@ set -euo pipefail
|
|
|
4
4
|
# Record changed files after Edit/Write tool usage
|
|
5
5
|
# Track which files have changed for the CI gate
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
CHANGES_LOG="$PROJECT_DIR/.claude/.afc-changes.log"
|
|
10
|
-
CI_FLAG="$PROJECT_DIR/.claude/.afc-ci-passed"
|
|
7
|
+
# shellcheck source=afc-state.sh
|
|
8
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
11
9
|
|
|
12
10
|
# shellcheck disable=SC2329
|
|
13
11
|
cleanup() {
|
|
@@ -17,7 +15,7 @@ cleanup() {
|
|
|
17
15
|
trap cleanup EXIT
|
|
18
16
|
|
|
19
17
|
# If pipeline is inactive -> skip
|
|
20
|
-
if
|
|
18
|
+
if ! afc_state_is_active; then
|
|
21
19
|
exit 0
|
|
22
20
|
fi
|
|
23
21
|
|
|
@@ -37,12 +35,11 @@ else
|
|
|
37
35
|
fi
|
|
38
36
|
|
|
39
37
|
if [ -n "$FILE_PATH" ]; then
|
|
40
|
-
# Append to change log (deduplicate)
|
|
41
|
-
|
|
42
|
-
sort -u -o "$CHANGES_LOG" "$CHANGES_LOG"
|
|
38
|
+
# Append to change log (deduplicate handled by afc_state_append_change)
|
|
39
|
+
afc_state_append_change "$FILE_PATH"
|
|
43
40
|
|
|
44
41
|
# Invalidate CI results since a file was changed
|
|
45
|
-
|
|
42
|
+
afc_state_invalidate_ci
|
|
46
43
|
fi
|
|
47
44
|
|
|
48
45
|
exit 0
|