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
|
@@ -3,6 +3,7 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
# Parallel Task Validator: Parse tasks.md and check for file path conflicts
|
|
5
5
|
# among [P]-marked (parallel) tasks within the same phase.
|
|
6
|
+
# Calls Node.js ESM version if available, falls back to bash implementation.
|
|
6
7
|
#
|
|
7
8
|
# Usage: afc-parallel-validate.sh <tasks_file_path>
|
|
8
9
|
# Exit 0: valid (no overlaps, or no [P] tasks found)
|
|
@@ -14,7 +15,6 @@ cleanup() {
|
|
|
14
15
|
}
|
|
15
16
|
trap cleanup EXIT
|
|
16
17
|
|
|
17
|
-
# PROJECT_DIR kept for convention consistency with other afc scripts
|
|
18
18
|
# shellcheck disable=SC2034
|
|
19
19
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
20
20
|
|
|
@@ -29,92 +29,85 @@ if [ ! -f "$TASKS_FILE" ]; then
|
|
|
29
29
|
exit 1
|
|
30
30
|
fi
|
|
31
31
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
# --- Node.js fast path ---
|
|
33
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
34
|
+
if command -v node >/dev/null 2>&1; then
|
|
35
|
+
node "$SCRIPT_DIR/afc-parallel-validate.mjs" "$TASKS_FILE"
|
|
36
|
+
exit $?
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# --- Bash fallback ---
|
|
37
40
|
|
|
38
41
|
current_phase=""
|
|
39
42
|
total_p_tasks=0
|
|
40
43
|
conflict_found=0
|
|
41
44
|
conflict_messages=""
|
|
42
45
|
|
|
43
|
-
# We process line by line using a while loop.
|
|
44
|
-
# Two associative arrays are used per phase to track:
|
|
45
|
-
# phase_files[file_path]="task_id" — first task that claimed the file
|
|
46
|
-
# phase_tasks[file_path]="task_id" — same map for conflict lookup
|
|
47
|
-
# Because bash 3 (macOS default) lacks associative arrays we use temp files.
|
|
48
|
-
|
|
49
46
|
TMPDIR_WORK="$(mktemp -d)"
|
|
50
47
|
# shellcheck disable=SC2064
|
|
51
48
|
trap "rm -rf '$TMPDIR_WORK'; :" EXIT
|
|
52
49
|
|
|
53
|
-
# File that accumulates seen paths for the current phase:
|
|
54
|
-
# format: <file_path><TAB><task_id>
|
|
55
50
|
phase_index="$TMPDIR_WORK/phase_index.tsv"
|
|
56
51
|
|
|
57
52
|
flush_phase() {
|
|
58
|
-
# Reset the per-phase index for a new phase
|
|
59
53
|
: > "$phase_index"
|
|
60
54
|
}
|
|
61
55
|
|
|
62
56
|
flush_phase
|
|
63
57
|
|
|
64
58
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
65
|
-
# Detect phase header: ## Phase N: ...
|
|
66
59
|
if printf '%s\n' "$line" | grep -qE '^## Phase [0-9]+'; then
|
|
67
|
-
# Extract phase number
|
|
68
60
|
current_phase="$(printf '%s\n' "$line" | sed 's/^## Phase \([0-9]*\).*/\1/')"
|
|
69
61
|
flush_phase
|
|
70
62
|
continue
|
|
71
63
|
fi
|
|
72
64
|
|
|
73
|
-
# Only process [P]-marked task lines when inside a phase
|
|
74
65
|
[ -z "$current_phase" ] && continue
|
|
75
66
|
|
|
76
|
-
# Match task lines containing [P] marker
|
|
77
67
|
if ! printf '%s\n' "$line" | grep -qE '^\s*-\s*\[[ xX]\]\s+T[0-9]+\s+\[P\]'; then
|
|
78
68
|
continue
|
|
79
69
|
fi
|
|
80
70
|
|
|
81
|
-
# Extract task ID: first T{NNN} token
|
|
82
71
|
task_id="$(printf '%s\n' "$line" | grep -oE 'T[0-9]+' | head -1)"
|
|
83
72
|
[ -z "$task_id" ] && continue
|
|
84
73
|
|
|
85
|
-
#
|
|
86
|
-
|
|
74
|
+
# shellcheck disable=SC2016
|
|
75
|
+
file_paths_raw="$(printf '%s\n' "$line" | grep -oE '`[^`]+`' | sed 's/`//g' || true)"
|
|
76
|
+
file_paths=""
|
|
77
|
+
if [ -n "$file_paths_raw" ]; then
|
|
78
|
+
file_paths="$(printf '%s\n' "$file_paths_raw" | grep -E '[/.]' || true)"
|
|
79
|
+
fi
|
|
87
80
|
|
|
88
|
-
|
|
89
|
-
if [ -z "$file_path" ] || [ "$file_path" = "$line" ]; then
|
|
81
|
+
if [ -z "$file_paths" ]; then
|
|
90
82
|
total_p_tasks=$((total_p_tasks + 1))
|
|
91
83
|
continue
|
|
92
84
|
fi
|
|
93
85
|
|
|
94
86
|
total_p_tasks=$((total_p_tasks + 1))
|
|
95
87
|
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
while IFS= read -r file_path; do
|
|
89
|
+
[ -z "$file_path" ] && continue
|
|
98
90
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
91
|
+
existing_task="$(grep -F "${file_path} " "$phase_index" | cut -f2 | head -1 || true)"
|
|
92
|
+
|
|
93
|
+
if [ -n "$existing_task" ]; then
|
|
94
|
+
conflict_found=1
|
|
95
|
+
msg="CONFLICT: Phase ${current_phase} — ${existing_task} and ${task_id} both target ${file_path}"
|
|
96
|
+
if [ -z "$conflict_messages" ]; then
|
|
97
|
+
conflict_messages="$msg"
|
|
98
|
+
else
|
|
99
|
+
conflict_messages="${conflict_messages}
|
|
107
100
|
${msg}"
|
|
101
|
+
fi
|
|
102
|
+
else
|
|
103
|
+
printf '%s\t%s\n' "$file_path" "$task_id" >> "$phase_index"
|
|
108
104
|
fi
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
fi
|
|
105
|
+
done <<EOF_PATHS
|
|
106
|
+
$file_paths
|
|
107
|
+
EOF_PATHS
|
|
113
108
|
|
|
114
109
|
done < "$TASKS_FILE"
|
|
115
110
|
|
|
116
|
-
# Count distinct phases that had [P] tasks by checking how many phase headers
|
|
117
|
-
# had at least one [P] task line (reparse for count only)
|
|
118
111
|
phases_with_p=0
|
|
119
112
|
current_phase_count=""
|
|
120
113
|
phase_had_p=0
|
|
@@ -134,15 +127,10 @@ while IFS= read -r line || [ -n "$line" ]; do
|
|
|
134
127
|
fi
|
|
135
128
|
done < "$TASKS_FILE"
|
|
136
129
|
|
|
137
|
-
# Flush last phase
|
|
138
130
|
if [ "$phase_had_p" -eq 1 ]; then
|
|
139
131
|
phases_with_p=$((phases_with_p + 1))
|
|
140
132
|
fi
|
|
141
133
|
|
|
142
|
-
# ------------------------------------------------------------------
|
|
143
|
-
# Output
|
|
144
|
-
# ------------------------------------------------------------------
|
|
145
|
-
|
|
146
134
|
if [ "$total_p_tasks" -eq 0 ]; then
|
|
147
135
|
printf 'Valid: no [P] tasks found, nothing to validate\n'
|
|
148
136
|
exit 0
|
|
@@ -4,6 +4,9 @@ set -euo pipefail
|
|
|
4
4
|
# PermissionRequest Hook: Auto-allow CI-related Bash commands during implement/review Phase
|
|
5
5
|
# Only exact whitelist matches allowed; commands with chaining (&&/;/|/$()) fall through to default behavior (user confirmation)
|
|
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,22 +14,17 @@ 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
|
-
PHASE_FLAG="$PROJECT_DIR/.claude/.afc-phase"
|
|
16
17
|
|
|
17
18
|
# Read hook data from stdin
|
|
18
19
|
INPUT=$(cat)
|
|
19
20
|
|
|
20
21
|
# Exit silently if pipeline is inactive
|
|
21
|
-
if
|
|
22
|
+
if ! afc_state_is_active; then
|
|
22
23
|
exit 0
|
|
23
24
|
fi
|
|
24
25
|
|
|
25
26
|
# Only active during implement/review Phase
|
|
26
|
-
PHASE=""
|
|
27
|
-
if [ -f "$PHASE_FLAG" ]; then
|
|
28
|
-
PHASE="$(head -1 "$PHASE_FLAG" | tr -d '\n\r')"
|
|
29
|
-
fi
|
|
27
|
+
PHASE="$(afc_state_read phase || echo '')"
|
|
30
28
|
case "${PHASE:-}" in
|
|
31
29
|
implement|review) ;;
|
|
32
30
|
*) exit 0 ;;
|
|
@@ -54,13 +52,60 @@ case "$COMMAND" in
|
|
|
54
52
|
*$'\n'*) exit 0 ;;
|
|
55
53
|
esac
|
|
56
54
|
|
|
55
|
+
# Build dynamic whitelist from afc.config.md (CI/gate/test commands)
|
|
56
|
+
DYNAMIC_WHITELIST=""
|
|
57
|
+
CONFIG_FILE="$PROJECT_DIR/.claude/afc.config.md"
|
|
58
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
59
|
+
# Extract ci, gate, test values from YAML code block
|
|
60
|
+
# Handles both quoted and unquoted: ci: "npm run lint" or ci: npm run lint
|
|
61
|
+
for key in ci gate test; do
|
|
62
|
+
val=$(grep -E "^\s*${key}:\s*\"[^\"]*\"" "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*'"${key}"': *"\([^"]*\)".*/\1/' || true)
|
|
63
|
+
if [ -n "$val" ] && [ "$val" != '""' ]; then
|
|
64
|
+
DYNAMIC_WHITELIST="${DYNAMIC_WHITELIST:+${DYNAMIC_WHITELIST}|}${val}"
|
|
65
|
+
# Generate PM-agnostic variants (npm → pnpm, yarn, bun)
|
|
66
|
+
case "$val" in
|
|
67
|
+
"npm run "*)
|
|
68
|
+
suffix="${val#npm run }"
|
|
69
|
+
DYNAMIC_WHITELIST="${DYNAMIC_WHITELIST}|pnpm run ${suffix}|yarn run ${suffix}|bun run ${suffix}"
|
|
70
|
+
;;
|
|
71
|
+
"npm test"*)
|
|
72
|
+
suffix="${val#npm test}"
|
|
73
|
+
DYNAMIC_WHITELIST="${DYNAMIC_WHITELIST}|pnpm test${suffix}|yarn test${suffix}|bun test${suffix}"
|
|
74
|
+
;;
|
|
75
|
+
esac
|
|
76
|
+
fi
|
|
77
|
+
done
|
|
78
|
+
fi
|
|
79
|
+
|
|
57
80
|
# Whitelist exact match (uses space + $ to prevent prefix matching)
|
|
58
81
|
ALLOWED=false
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
|
|
83
|
+
# Check dynamic whitelist first (from afc.config.md)
|
|
84
|
+
if [ -n "$DYNAMIC_WHITELIST" ]; then
|
|
85
|
+
# Use printf + grep for safe matching (no eval)
|
|
86
|
+
if printf '%s\n' "$DYNAMIC_WHITELIST" | tr '|' '\n' | grep -qxF "$COMMAND"; then
|
|
61
87
|
ALLOWED=true
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Hardcoded fallback whitelist (always active for backward compatibility)
|
|
92
|
+
if [ "$ALLOWED" = "false" ]; then
|
|
93
|
+
case "$COMMAND" in
|
|
94
|
+
"npm run lint"|"npm test"|"npm run test:all")
|
|
95
|
+
ALLOWED=true
|
|
96
|
+
;;
|
|
97
|
+
esac
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Plugin's own scripts (auto-allow during pipeline execution)
|
|
101
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-}"
|
|
102
|
+
if [ "$ALLOWED" = "false" ] && [ -n "$PLUGIN_ROOT" ]; then
|
|
103
|
+
case "$COMMAND" in
|
|
104
|
+
"\"${PLUGIN_ROOT}/scripts/"*|"${PLUGIN_ROOT}/scripts/"*)
|
|
105
|
+
ALLOWED=true
|
|
106
|
+
;;
|
|
107
|
+
esac
|
|
108
|
+
fi
|
|
64
109
|
|
|
65
110
|
# Prefix matching (allow paths after shellcheck, prettier, chmod +x)
|
|
66
111
|
if [ "$ALLOWED" = "false" ]; then
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
# Pipeline Management: Manage afc pipeline state
|
|
5
|
-
#
|
|
4
|
+
# Pipeline Management: Manage afc pipeline state
|
|
5
|
+
# Uses .afc-state.json for all state (replaces legacy flag files)
|
|
6
6
|
#
|
|
7
7
|
# Usage:
|
|
8
8
|
# afc-pipeline-manage.sh start <feature-name>
|
|
@@ -16,16 +16,17 @@ set -euo pipefail
|
|
|
16
16
|
|
|
17
17
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
18
18
|
FLAG_DIR="$PROJECT_DIR/.claude"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
|
|
20
|
+
# Source state library
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
22
|
+
# shellcheck source=afc-state.sh
|
|
23
|
+
. "$SCRIPT_DIR/afc-state.sh"
|
|
23
24
|
|
|
24
25
|
# shellcheck disable=SC2329
|
|
25
26
|
cleanup() {
|
|
26
27
|
local exit_code=$?
|
|
27
28
|
if [ "$exit_code" -ne 0 ]; then
|
|
28
|
-
echo "afc
|
|
29
|
+
echo "[afc:pipeline] Abnormal exit (code: $exit_code)" >&2
|
|
29
30
|
fi
|
|
30
31
|
exit "$exit_code"
|
|
31
32
|
}
|
|
@@ -35,34 +36,32 @@ mkdir -p "$FLAG_DIR"
|
|
|
35
36
|
|
|
36
37
|
COMMAND="${1:-}"
|
|
37
38
|
if [ -z "$COMMAND" ]; then
|
|
38
|
-
echo "Usage: $0 {start|phase|ci-pass|end|status} [args]" >&2
|
|
39
|
+
echo "[afc] Usage: $0 {start|phase|ci-pass|end|status} [args]" >&2
|
|
39
40
|
exit 1
|
|
40
41
|
fi
|
|
41
42
|
|
|
42
43
|
case "$COMMAND" in
|
|
43
44
|
start)
|
|
44
45
|
if [ -z "${2:-}" ]; then
|
|
45
|
-
echo "Feature name required" >&2
|
|
46
|
+
echo "[afc:pipeline] Feature name required" >&2
|
|
46
47
|
exit 1
|
|
47
48
|
fi
|
|
48
49
|
# Sanitize feature name (strip newlines, path traversal, limit length)
|
|
49
|
-
FEATURE=$(printf '%s' "$2" | tr -d '\n\r/' | cut -c1-100)
|
|
50
|
+
FEATURE=$(printf '%s' "$2" | tr -d '\n\r/"\\&' | cut -c1-100)
|
|
50
51
|
if [ -z "$FEATURE" ]; then
|
|
51
|
-
echo "Feature name invalid after sanitization" >&2
|
|
52
|
+
echo "[afc:pipeline] Feature name invalid after sanitization" >&2
|
|
52
53
|
exit 1
|
|
53
54
|
fi
|
|
54
55
|
|
|
55
56
|
# Prevent duplicate execution
|
|
56
|
-
if
|
|
57
|
-
EXISTING=$(
|
|
58
|
-
echo "WARNING: Pipeline already active: $EXISTING" >&2
|
|
59
|
-
echo "Use '$0 end --force' to clear, or '$0 status' to check
|
|
57
|
+
if afc_state_is_active; then
|
|
58
|
+
EXISTING=$(afc_state_read feature || echo "unknown")
|
|
59
|
+
echo "[afc] WARNING: Pipeline already active: $EXISTING" >&2
|
|
60
|
+
echo " → Use '$0 end --force' to clear, or '$0 status' to check" >&2
|
|
60
61
|
exit 1
|
|
61
62
|
fi
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
printf '%s\n' "spec" > "$PHASE_FLAG"
|
|
65
|
-
rm -f "$CI_FLAG" "$CHANGES_LOG"
|
|
64
|
+
afc_state_init "$FEATURE"
|
|
66
65
|
|
|
67
66
|
# Safety snapshot
|
|
68
67
|
if cd "$PROJECT_DIR" 2>/dev/null; then
|
|
@@ -74,35 +73,34 @@ case "$COMMAND" in
|
|
|
74
73
|
|
|
75
74
|
phase)
|
|
76
75
|
PHASE="${2:?Phase name required}"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
exit 1
|
|
86
|
-
;;
|
|
87
|
-
esac
|
|
76
|
+
if afc_is_valid_phase "$PHASE"; then
|
|
77
|
+
afc_state_write "phase" "$PHASE"
|
|
78
|
+
afc_state_invalidate_ci
|
|
79
|
+
echo "Phase: $PHASE"
|
|
80
|
+
else
|
|
81
|
+
printf "[afc:pipeline] Invalid phase: %s\n → Valid phases: %s\n" "$PHASE" "$AFC_VALID_PHASES" >&2
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
88
84
|
;;
|
|
89
85
|
|
|
90
86
|
ci-pass)
|
|
91
|
-
|
|
87
|
+
afc_state_ci_pass
|
|
92
88
|
echo "CI passed at $(date '+%H:%M:%S')"
|
|
93
89
|
;;
|
|
94
90
|
|
|
95
91
|
end)
|
|
96
92
|
FORCE="${2:-}"
|
|
97
93
|
FEATURE=""
|
|
98
|
-
if
|
|
99
|
-
FEATURE=$(
|
|
94
|
+
if afc_state_is_active; then
|
|
95
|
+
FEATURE=$(afc_state_read feature || echo "")
|
|
100
96
|
elif [ "$FORCE" != "--force" ]; then
|
|
101
|
-
echo "No active pipeline to end
|
|
97
|
+
echo "[afc:pipeline] No active pipeline to end" >&2
|
|
102
98
|
exit 0
|
|
103
99
|
fi
|
|
104
100
|
|
|
105
|
-
|
|
101
|
+
afc_state_delete
|
|
102
|
+
# Clean sidecar changes file if it exists (jq-less fallback)
|
|
103
|
+
rm -f "$FLAG_DIR/.afc-state.changes.log"
|
|
106
104
|
rm -f "$FLAG_DIR/.afc-failures.log" "$FLAG_DIR/.afc-task-results.log" "$FLAG_DIR/.afc-config-audit.log"
|
|
107
105
|
|
|
108
106
|
# Clean up safety tag and phase tags (on successful completion)
|
|
@@ -117,12 +115,15 @@ case "$COMMAND" in
|
|
|
117
115
|
;;
|
|
118
116
|
|
|
119
117
|
status)
|
|
120
|
-
if
|
|
121
|
-
echo "Active: $(
|
|
122
|
-
|
|
123
|
-
[ -
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
if afc_state_is_active; then
|
|
119
|
+
echo "Active: $(afc_state_read feature || echo 'unknown')"
|
|
120
|
+
PHASE=$(afc_state_read phase 2>/dev/null || true)
|
|
121
|
+
[ -n "$PHASE" ] && echo "Phase: $PHASE"
|
|
122
|
+
CI_TS=$(afc_state_read ciPassedAt 2>/dev/null || true)
|
|
123
|
+
[ -n "$CI_TS" ] && echo "CI: passed ($CI_TS)"
|
|
124
|
+
CHANGES=$(afc_state_read_changes 2>/dev/null || true)
|
|
125
|
+
if [ -n "$CHANGES" ]; then
|
|
126
|
+
CHANGE_COUNT=$(printf '%s\n' "$CHANGES" | wc -l | tr -d ' ')
|
|
126
127
|
echo "Changes: $CHANGE_COUNT files"
|
|
127
128
|
fi
|
|
128
129
|
else
|
|
@@ -134,10 +135,9 @@ case "$COMMAND" in
|
|
|
134
135
|
EVENT="${2:-}"
|
|
135
136
|
MSG="${3:-}"
|
|
136
137
|
if [ -z "$EVENT" ]; then
|
|
137
|
-
echo "Usage: $0 log <event_type> <message>" >&2
|
|
138
|
+
echo "[afc] Usage: $0 log <event_type> <message>" >&2
|
|
138
139
|
exit 1
|
|
139
140
|
fi
|
|
140
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
141
141
|
"$SCRIPT_DIR/afc-timeline-log.sh" "$EVENT" "$MSG"
|
|
142
142
|
;;
|
|
143
143
|
|
|
@@ -146,14 +146,14 @@ case "$COMMAND" in
|
|
|
146
146
|
# Sanitize to digits only
|
|
147
147
|
PHASE_NUM=$(printf '%s' "$PHASE_NUM" | tr -dc '0-9' | cut -c1-2)
|
|
148
148
|
if [ -z "$PHASE_NUM" ]; then
|
|
149
|
-
echo "Invalid phase number" >&2
|
|
149
|
+
echo "[afc:pipeline] Invalid phase number" >&2
|
|
150
150
|
exit 1
|
|
151
151
|
fi
|
|
152
152
|
if cd "$PROJECT_DIR" 2>/dev/null; then
|
|
153
153
|
git tag -f "afc/phase-${PHASE_NUM}" 2>/dev/null || true
|
|
154
154
|
echo "Phase tag created: afc/phase-${PHASE_NUM}"
|
|
155
155
|
else
|
|
156
|
-
echo "Cannot create tag: not a git repo" >&2
|
|
156
|
+
echo "[afc:pipeline] Cannot create tag: not a git repo" >&2
|
|
157
157
|
exit 1
|
|
158
158
|
fi
|
|
159
159
|
;;
|
|
@@ -172,13 +172,13 @@ case "$COMMAND" in
|
|
|
172
172
|
echo "No phase tags to remove"
|
|
173
173
|
fi
|
|
174
174
|
else
|
|
175
|
-
echo "Cannot clean tags: not a git repo" >&2
|
|
175
|
+
echo "[afc:pipeline] Cannot clean tags: not a git repo" >&2
|
|
176
176
|
exit 0
|
|
177
177
|
fi
|
|
178
178
|
;;
|
|
179
179
|
|
|
180
180
|
*)
|
|
181
|
-
echo "Usage: $0 {start|phase|ci-pass|end|status|log|phase-tag|phase-tag-clean} [args]" >&2
|
|
181
|
+
echo "[afc] Usage: $0 {start|phase|ci-pass|end|status|log|phase-tag|phase-tag-clean} [args]" >&2
|
|
182
182
|
exit 1
|
|
183
183
|
;;
|
|
184
184
|
esac
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
+
# shellcheck source=afc-state.sh
|
|
5
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
6
|
+
|
|
4
7
|
# shellcheck disable=SC2329
|
|
5
8
|
cleanup() {
|
|
6
9
|
:
|
|
@@ -165,9 +168,9 @@ fi
|
|
|
165
168
|
|
|
166
169
|
# ── Check 5: No active pipeline ──────────────────────────
|
|
167
170
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
ACTIVE_NAME=$(
|
|
171
|
+
if afc_state_is_active; then
|
|
172
|
+
ACTIVE_NAME=$(afc_state_read feature 2>/dev/null || printf 'unknown')
|
|
173
|
+
ACTIVE_NAME=$(printf '%s' "$ACTIVE_NAME" | cut -c1-100)
|
|
171
174
|
printf ' \xe2\x9c\x97 No active pipeline: pipeline already running (%s)\n' "$ACTIVE_NAME"
|
|
172
175
|
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
173
176
|
else
|