all-for-claudecode 2.2.0 → 2.2.1
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 +81 -14
- package/agents/afc-architect.md +2 -0
- package/commands/architect.md +1 -1
- package/commands/auto.md +17 -13
- package/commands/implement.md +1 -1
- package/commands/init.md +4 -3
- package/commands/resume.md +6 -3
- package/commands/spec.md +2 -1
- package/docs/phase-gate-protocol.md +17 -5
- package/hooks/hooks.json +2 -4
- package/package.json +1 -1
- package/scripts/afc-consistency-check.sh +32 -7
- package/scripts/afc-dag-validate.sh +3 -2
- package/scripts/afc-notify.sh +5 -6
- package/scripts/afc-parallel-validate.sh +3 -5
- package/scripts/afc-permission-request.sh +16 -2
- package/scripts/afc-pipeline-manage.sh +16 -0
- package/scripts/afc-state.sh +22 -26
- package/scripts/afc-stop-todo-check.sh +83 -0
- package/scripts/pre-compact-checkpoint.sh +19 -4
- package/scripts/session-start-context.sh +11 -3
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Automated pipeline for Claude Code — spec → plan → implement → review → clean",
|
|
9
|
-
"version": "2.2.
|
|
9
|
+
"version": "2.2.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "afc",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Automated pipeline for Claude Code. Automates the full development cycle: spec → plan → implement → review → clean.",
|
|
16
|
-
"version": "2.2.
|
|
16
|
+
"version": "2.2.1",
|
|
17
17
|
"category": "automation",
|
|
18
18
|
"tags": ["pipeline", "automation", "spec", "plan", "implement", "review", "critic-loop"]
|
|
19
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "afc",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Automated pipeline for Claude Code. Automates the full development cycle: spec → plan → implement → review → clean.",
|
|
5
5
|
"author": { "name": "jhlee0409", "email": "relee6203@gmail.com" },
|
|
6
6
|
"homepage": "https://github.com/jhlee0409/all-for-claudecode",
|
package/README.md
CHANGED
|
@@ -56,6 +56,47 @@ CI failure → debug-based RCA (not blind retry).
|
|
|
56
56
|
Critic Loops verify quality at each gate until convergence.
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
## Walkthrough: What a Pipeline Run Looks Like
|
|
60
|
+
|
|
61
|
+
Running `/afc:auto "Add password reset flow"` produces this (abbreviated):
|
|
62
|
+
|
|
63
|
+
**Spec (1/5)** — Generates `spec.md` with requirements and acceptance criteria:
|
|
64
|
+
```
|
|
65
|
+
FR-001: POST /auth/reset sends email with token
|
|
66
|
+
FR-002: GET /auth/reset/:token validates and shows form
|
|
67
|
+
FR-003: Token expires after 1 hour
|
|
68
|
+
Acceptance: Given expired token, When user submits, Then show error
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Plan (2/5)** — Creates `plan.md` with file change map and architecture decisions:
|
|
72
|
+
```
|
|
73
|
+
File Change Map:
|
|
74
|
+
src/routes/auth.ts — ADD reset endpoint handlers
|
|
75
|
+
src/services/email.ts — ADD sendResetEmail()
|
|
76
|
+
src/middleware/validate.ts — MODIFY add token validation
|
|
77
|
+
tests/auth.test.ts — ADD reset flow tests
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Implement (3/5)** — Auto-decomposes into tasks, executes with CI gates:
|
|
81
|
+
```
|
|
82
|
+
Tasks: 4 total (2 parallel)
|
|
83
|
+
[1] Add reset endpoint ✓
|
|
84
|
+
[2] Add email service ✓ ← parallel with [1]
|
|
85
|
+
[3] Add token validation ✓ ← depends on [1]
|
|
86
|
+
[4] Add tests ✓
|
|
87
|
+
CI: npm test → passed
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Review (4/5)** — 8-perspective review + specialist agent analysis:
|
|
91
|
+
```
|
|
92
|
+
Architecture (afc-architect): ✓ layer boundaries respected
|
|
93
|
+
Security (afc-security): ⚠ rate-limit reset endpoint
|
|
94
|
+
Performance: ✓ no N+1 queries
|
|
95
|
+
→ Auto-fixed: added rate limiter middleware
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Clean (5/5)** — Removes pipeline artifacts, final CI check.
|
|
99
|
+
|
|
59
100
|
## Slash Commands
|
|
60
101
|
|
|
61
102
|
| Command | Description |
|
|
@@ -76,9 +117,30 @@ Critic Loops verify quality at each gate until convergence.
|
|
|
76
117
|
| `/afc:checkpoint` | Save session state |
|
|
77
118
|
| `/afc:resume` | Restore session state |
|
|
78
119
|
| `/afc:tasks` | Task decomposition (auto-generated by implement) |
|
|
120
|
+
| `/afc:ideate` | Explore and structure a product idea |
|
|
121
|
+
| `/afc:launch` | Generate release artifacts (changelog, tag, publish) |
|
|
79
122
|
| `/afc:analyze` | Verify artifact consistency |
|
|
80
123
|
| `/afc:clarify` | Resolve spec ambiguities |
|
|
81
124
|
|
|
125
|
+
### Individual Command Examples
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Write a spec for a specific feature
|
|
129
|
+
/afc:spec "Add dark mode toggle"
|
|
130
|
+
|
|
131
|
+
# Design a plan from an existing spec
|
|
132
|
+
/afc:plan
|
|
133
|
+
|
|
134
|
+
# Debug a specific error
|
|
135
|
+
/afc:debug "TypeError: Cannot read property 'user' of undefined"
|
|
136
|
+
|
|
137
|
+
# Run code review on current changes
|
|
138
|
+
/afc:review
|
|
139
|
+
|
|
140
|
+
# Explore and structure a product idea
|
|
141
|
+
/afc:ideate "real-time collaboration feature"
|
|
142
|
+
```
|
|
143
|
+
|
|
82
144
|
## Hook Events
|
|
83
145
|
|
|
84
146
|
Every hook fires automatically — no configuration needed after install.
|
|
@@ -125,31 +187,21 @@ The implement phase automatically selects execution strategy:
|
|
|
125
187
|
|
|
126
188
|
Dependencies are tracked via DAG. CI gate + Mini-Review + Auto-Checkpoint run at each phase boundary.
|
|
127
189
|
|
|
128
|
-
## Project Presets
|
|
129
|
-
|
|
130
|
-
| Preset | Stack |
|
|
131
|
-
|---|---|
|
|
132
|
-
| `template` | Generic (manual config) |
|
|
133
|
-
| `nextjs-fsd` | Next.js + FSD + Zustand + React Query |
|
|
134
|
-
| `react-spa` | Vite + React 18 + Zustand + Tailwind |
|
|
135
|
-
| `express-api` | Express + TypeScript + Prisma + Jest |
|
|
136
|
-
| `monorepo` | Turborepo + pnpm workspace |
|
|
137
|
-
|
|
138
190
|
## Configuration
|
|
139
191
|
|
|
140
192
|
```
|
|
141
193
|
/afc:init
|
|
142
194
|
```
|
|
143
195
|
|
|
144
|
-
|
|
196
|
+
Auto-detects your tech stack (package manager, framework, architecture, testing, linting) and generates `.claude/afc.config.md` with CI commands, architecture rules, and code style conventions. No manual preset selection needed — the init command analyzes your project structure directly.
|
|
145
197
|
|
|
146
198
|
## FAQ
|
|
147
199
|
|
|
148
200
|
### Does it work with any project?
|
|
149
|
-
Yes. Run `/afc:init` to auto-detect your stack,
|
|
201
|
+
Yes. Run `/afc:init` to auto-detect your stack. Works with JavaScript/TypeScript, Python, Rust, Go, and any project with a CI command.
|
|
150
202
|
|
|
151
203
|
### Does it require any dependencies?
|
|
152
|
-
No. Pure markdown commands + bash hook scripts.
|
|
204
|
+
No. Pure markdown commands + bash hook scripts. No npm packages are imported at runtime.
|
|
153
205
|
|
|
154
206
|
### What happens if CI fails during the pipeline?
|
|
155
207
|
Debug-based RCA: traces the error, forms a hypothesis, applies a targeted fix. Halts after 3 failed attempts with full diagnosis.
|
|
@@ -158,7 +210,22 @@ Debug-based RCA: traces the error, forms a hypothesis, applies a targeted fix. H
|
|
|
158
210
|
Yes. Each phase has its own command (`/afc:spec`, `/afc:plan`, `/afc:implement`, `/afc:review`). `/afc:auto` runs them all.
|
|
159
211
|
|
|
160
212
|
### What are Critic Loops?
|
|
161
|
-
Convergence-based quality checks after each phase. They evaluate output against criteria
|
|
213
|
+
Convergence-based quality checks after each phase. They evaluate output against criteria and auto-fix issues until stable. 4 verdicts: PASS, FAIL, ESCALATE (asks user), DEFER.
|
|
214
|
+
|
|
215
|
+
### How many tokens does a pipeline run use?
|
|
216
|
+
Depends on project size and feature complexity. A typical `/afc:auto` run for a medium feature uses roughly the same as a detailed manual implementation session — the pipeline adds structure, not overhead.
|
|
217
|
+
|
|
218
|
+
### Can I customize the pipeline behavior?
|
|
219
|
+
Yes. Edit `.claude/afc.config.md` to change CI commands, architecture rules, and code style conventions. The pipeline reads this config at every phase.
|
|
220
|
+
|
|
221
|
+
### Does it work with monorepos?
|
|
222
|
+
Yes. Run `/afc:init` in the monorepo root. The init command detects workspace structure and configures accordingly.
|
|
223
|
+
|
|
224
|
+
### Can multiple team members use it on the same repo?
|
|
225
|
+
Yes. Each developer runs their own pipeline independently. The `.claude/afc.config.md` config is shared (commit it to the repo), but pipeline state is local and session-scoped.
|
|
226
|
+
|
|
227
|
+
### How is this different from Cursor / Copilot / other AI tools?
|
|
228
|
+
All-for-claudecode is not a code completion tool — it is a structured development pipeline. It enforces spec → plan → implement → review flow with quality gates, persistent memory agents, and CI verification at every step.
|
|
162
229
|
|
|
163
230
|
## License
|
|
164
231
|
|
package/agents/afc-architect.md
CHANGED
|
@@ -11,6 +11,8 @@ tools:
|
|
|
11
11
|
- WebSearch
|
|
12
12
|
model: sonnet
|
|
13
13
|
memory: project
|
|
14
|
+
# Note: no `isolation: worktree` — architect writes ADR files to project memory
|
|
15
|
+
# which must persist in the main worktree (unlike afc-security which is read-only)
|
|
14
16
|
skills:
|
|
15
17
|
- docs/critic-loop-rules.md
|
|
16
18
|
- docs/phase-gate-protocol.md
|
package/commands/architect.md
CHANGED
|
@@ -92,7 +92,7 @@ Structure analysis results and **print to console**:
|
|
|
92
92
|
|
|
93
93
|
> **Always** read `${CLAUDE_PLUGIN_ROOT}/docs/critic-loop-rules.md` first and follow it.
|
|
94
94
|
|
|
95
|
-
Run the critic loop until convergence. Safety cap: 7 passes.
|
|
95
|
+
Run the critic loop until convergence. Safety cap: 7 passes (higher than the standard 5 because architecture analysis involves broader exploration across modules and layers).
|
|
96
96
|
|
|
97
97
|
| Criterion | Validation |
|
|
98
98
|
|-----------|------------|
|
package/commands/auto.md
CHANGED
|
@@ -231,14 +231,18 @@ Execute `/afc:plan` logic inline:
|
|
|
231
231
|
```
|
|
232
232
|
- If architect returns conflicts → **ESCALATE** to user with conflict details
|
|
233
233
|
- If no conflicts → proceed (ADR recorded for future reference)
|
|
234
|
-
8. **Session context preservation**:
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
234
|
+
8. **Session context preservation**: Write key decisions to `.claude/afc/specs/{feature}/context.md` for compaction resilience:
|
|
235
|
+
```markdown
|
|
236
|
+
# Session Context: {feature}
|
|
237
|
+
## Goal
|
|
238
|
+
- Original request: $ARGUMENTS
|
|
239
|
+
- Current objective: Implement {feature}
|
|
240
|
+
## Key Decisions
|
|
241
|
+
- {what}: {rationale}
|
|
242
|
+
## Discoveries
|
|
243
|
+
- {file path}: {finding}
|
|
241
244
|
```
|
|
245
|
+
This file is read at Implement start to restore context after compaction.
|
|
242
246
|
9. **Checkpoint**: phase transition already recorded by `afc-pipeline-manage.sh phase plan` at phase start
|
|
243
247
|
10. Progress: `✓ 2/5 Plan complete (Critic: converged ({N} passes, {M} fixes, {E} escalations), files: {N}, ADR: {N} recorded, Implementation Context: {W} words)`
|
|
244
248
|
|
|
@@ -246,7 +250,7 @@ Execute `/afc:plan` logic inline:
|
|
|
246
250
|
|
|
247
251
|
`"${CLAUDE_PLUGIN_ROOT}/scripts/afc-pipeline-manage.sh" phase implement`
|
|
248
252
|
|
|
249
|
-
**Session context reload**: At implement start,
|
|
253
|
+
**Session context reload**: At implement start, read `.claude/afc/specs/{feature}/context.md` if it exists. This restores key decisions and constraints from Plan phase (resilient to context compaction).
|
|
250
254
|
|
|
251
255
|
Execute `/afc:implement` logic inline — **follow all orchestration rules defined in `commands/implement.md`** (task generation, mode selection, batch/swarm execution, failure recovery, task execution pattern). The implement command is the single source of truth for orchestration details.
|
|
252
256
|
|
|
@@ -414,16 +418,16 @@ Execute `/afc:review` logic inline — **follow all review perspectives defined
|
|
|
414
418
|
6. **Retrospective check**: if `.claude/afc/memory/retrospectives/` exists, load and check:
|
|
415
419
|
- Were there recurring Critical finding categories in past reviews? Prioritize those perspectives.
|
|
416
420
|
- Were there false positives that wasted effort? Reduce sensitivity for those patterns.
|
|
417
|
-
|
|
421
|
+
7. **Critic Loop until convergence** (safety cap: 5, follow Critic Loop rules):
|
|
418
422
|
- COMPLETENESS: were all changed files reviewed across all 8 perspectives (A-H)?
|
|
419
423
|
- SPEC_ALIGNMENT: cross-check implementation against spec.md — (1) every SC verified with `{M}/{N}` count, (2) every acceptance scenario (GWT) has corresponding code path, (3) no spec constraint is violated
|
|
420
424
|
- PRECISION: are there unnecessary changes? Are there out-of-scope modifications?
|
|
421
425
|
- FAIL → auto-fix and continue. ESCALATE → pause, present options, resume after response. DEFER → record reason, mark clean.
|
|
422
|
-
|
|
426
|
+
8. **Handling SC shortfalls**:
|
|
423
427
|
- Fixable → attempt auto-fix → re-run `{config.ci}` verification
|
|
424
428
|
- Not fixable → state in final report with reason (no post-hoc rationalization; record as Plan-phase target-setting error)
|
|
425
|
-
|
|
426
|
-
|
|
429
|
+
9. **Checkpoint**: phase transition already recorded by `afc-pipeline-manage.sh phase review` at phase start
|
|
430
|
+
10. Progress: `✓ 4/5 Review complete (Critical:{N} Warning:{N} Info:{N}, SC shortfalls: {N})`
|
|
427
431
|
|
|
428
432
|
### Phase 5: Clean (5/5)
|
|
429
433
|
|
|
@@ -548,7 +552,7 @@ Pipeline aborted (Phase {N}/5)
|
|
|
548
552
|
- **[P] parallel is mandatory**: if a [P] marker is assigned in tasks.md, it must be executed in parallel. Orchestration mode (batch vs swarm) is selected automatically based on task count. Sequential substitution is prohibited.
|
|
549
553
|
- **Swarm mode is automatic**: when a phase has 6+ [P] tasks, the orchestrator pre-assigns tasks to swarm workers. Do not manually batch.
|
|
550
554
|
- **Implementation Context travels with workers**: every sub-agent prompt includes the Implementation Context section from plan.md, ensuring spec intent propagates to parallel workers.
|
|
551
|
-
- **Session context resilience**: key decisions are
|
|
555
|
+
- **Session context resilience**: key decisions are written to `.claude/afc/specs/{feature}/context.md` at Plan completion and read at Implement start, surviving context compaction.
|
|
552
556
|
- **Specialist agents enhance review**: afc-architect and afc-security agents are invoked during Review to provide persistent-memory-aware analysis. Their findings are merged into the consolidated review. Agent memory updates happen automatically during the agent call.
|
|
553
557
|
- **Debug-based RCA replaces blind retry**: CI failures trigger `/afc:debug` logic (hypothesis → targeted fix) instead of generic "retry 3 times". This produces better fixes and records patterns via retrospective.
|
|
554
558
|
- **Acceptance tests close the spec-to-code gap**: When spec contains GWT scenarios and a test framework is configured, acceptance tests are auto-generated after implementation, verifying spec intent is met.
|
package/commands/implement.md
CHANGED
|
@@ -94,7 +94,7 @@ If tasks.md already exists (e.g., from standalone `/afc:tasks` run): use as-is,
|
|
|
94
94
|
- Identify already-completed `[x]` tasks
|
|
95
95
|
2. Load **Implementation Context** section from plan.md (used in sub-agent prompts)
|
|
96
96
|
|
|
97
|
-
### 1.
|
|
97
|
+
### 1.7. Retrospective Check
|
|
98
98
|
|
|
99
99
|
If `.claude/afc/memory/retrospectives/` exists, load and check:
|
|
100
100
|
- Were there implementation issues in past pipelines (e.g., file conflicts, unexpected dependencies, CI failures after parallel execution)?
|
package/commands/init.md
CHANGED
|
@@ -136,14 +136,15 @@ Check for presence of `<!-- AFC:START -->` or `<!-- SELFISH:START -->` marker.
|
|
|
136
136
|
|
|
137
137
|
#### Step 2. Conflict Pattern Scan
|
|
138
138
|
|
|
139
|
-
Search
|
|
139
|
+
Search CLAUDE.md for the patterns below. **IMPORTANT: EXCLUDE content inside any marker blocks (`<!-- *:START -->` ~ `<!-- *:END -->`). Only scan unguarded content outside marker blocks.** Other tools (OMC, etc.) manage their own blocks — their internal agent names are not conflicts.
|
|
140
140
|
|
|
141
141
|
**A. Marker Block Detection**
|
|
142
142
|
- Regex: `<!-- ([A-Z0-9_-]+):START -->` ~ `<!-- \1:END -->`
|
|
143
143
|
- Record all found block names and line ranges
|
|
144
|
+
- **Strip these ranges from the scan target** — only scan lines NOT inside any marker block
|
|
144
145
|
|
|
145
146
|
**B. Agent Routing Conflict Detection**
|
|
146
|
-
|
|
147
|
+
In the **unguarded** (non-marker-block) content only, find directives containing these keywords:
|
|
147
148
|
- `executor`, `deep-executor` — conflicts with afc:implement
|
|
148
149
|
- `code-reviewer`, `quality-reviewer`, `style-reviewer`, `api-reviewer`, `security-reviewer`, `performance-reviewer` — conflicts with afc:review
|
|
149
150
|
- `debugger` (in agent routing context) — conflicts with afc:debug
|
|
@@ -152,7 +153,7 @@ Find directives containing these keywords:
|
|
|
152
153
|
- `test-engineer` — conflicts with afc:test
|
|
153
154
|
|
|
154
155
|
**C. Skill Routing Conflict Detection**
|
|
155
|
-
|
|
156
|
+
In the **unguarded** content only, find these patterns:
|
|
156
157
|
- Another tool's skill trigger table (e.g., tables like `| situation | skill |`)
|
|
157
158
|
- `delegate to`, `route to`, `always use` + agent name combinations
|
|
158
159
|
- Directives related to `auto-trigger`, `intent detection`, `intent-based routing`
|
package/commands/resume.md
CHANGED
|
@@ -22,8 +22,8 @@ allowed-tools:
|
|
|
22
22
|
### 1. Load Checkpoint
|
|
23
23
|
|
|
24
24
|
Read `.claude/afc/memory/checkpoint.md`:
|
|
25
|
-
- If not found: output "No saved checkpoint found." then **stop**
|
|
26
|
-
- If found: parse the full contents
|
|
25
|
+
- If not found: output "No saved checkpoint found. Use `/afc:checkpoint` to create one, or checkpoints are created automatically on context compaction." then **stop**
|
|
26
|
+
- If found: parse the full contents (extract branch, commit hash, pipeline feature, task progress, modified files)
|
|
27
27
|
|
|
28
28
|
### 2. Validate Environment
|
|
29
29
|
|
|
@@ -32,7 +32,10 @@ Compare the checkpoint state against the current environment:
|
|
|
32
32
|
1. **Branch check**: Does the checkpoint branch match the current branch?
|
|
33
33
|
- If different: warn + suggest switching
|
|
34
34
|
2. **File state**: Have any files changed since the checkpoint?
|
|
35
|
-
-
|
|
35
|
+
- First verify HEAD exists: `git rev-parse --verify HEAD 2>/dev/null`
|
|
36
|
+
- If HEAD does not exist (empty repo / no commits): report "No commits yet — cannot check changes since checkpoint." and skip this check
|
|
37
|
+
- If checkpoint hash is present and non-empty: `git log {checkpoint hash}..HEAD --oneline`
|
|
38
|
+
- If checkpoint hash is empty or missing: report "Checkpoint has no git reference — cannot diff." and skip this check
|
|
36
39
|
3. **Feature directory**: Does .claude/afc/specs/{feature}/ still exist?
|
|
37
40
|
|
|
38
41
|
### 3. Report State
|
package/commands/spec.md
CHANGED
|
@@ -181,7 +181,8 @@ Run the critic loop until convergence. Safety cap: 5 passes.
|
|
|
181
181
|
### 5.5. Auto-Checkpoint (standalone only)
|
|
182
182
|
|
|
183
183
|
When not running inside `/afc:auto`, save progress for `/afc:resume`:
|
|
184
|
-
-
|
|
184
|
+
- Create `.claude/afc/memory/` directory if it does not exist (`mkdir -p .claude/afc/memory/`)
|
|
185
|
+
- Write/update `.claude/afc/memory/checkpoint.md` with: branch, last commit (or "none" if empty repo), feature name, current phase (spec complete), next step (`/afc:plan`)
|
|
185
186
|
- Skip if running inside auto pipeline (auto manages its own checkpoints via phase transitions)
|
|
186
187
|
|
|
187
188
|
### 6. Final Output
|
|
@@ -33,12 +33,24 @@ Quantitatively inspect changed files within the Phase against `{config.code_styl
|
|
|
33
33
|
|
|
34
34
|
After passing the Phase gate, automatically save session state:
|
|
35
35
|
|
|
36
|
+
1. Create `.claude/afc/memory/` directory if it does not exist
|
|
37
|
+
2. Write/update `.claude/afc/memory/checkpoint.md`:
|
|
38
|
+
|
|
36
39
|
```markdown
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
# Phase Gate Checkpoint
|
|
41
|
+
> Auto-generated: {YYYY-MM-DD HH:mm:ss}
|
|
42
|
+
> Trigger: phase gate
|
|
43
|
+
|
|
44
|
+
## Git Status
|
|
45
|
+
- Branch: {current branch}
|
|
46
|
+
- Commit: {short hash} — {commit message}
|
|
47
|
+
|
|
48
|
+
## Pipeline Status
|
|
49
|
+
- Active: Yes ({feature name})
|
|
50
|
+
- Current Phase: {N}/{total}
|
|
51
|
+
- Completed tasks: {list of completed IDs}
|
|
52
|
+
- Changed files: {file list}
|
|
53
|
+
- Last CI: ✓
|
|
42
54
|
```
|
|
43
55
|
|
|
44
56
|
- Even if the session is interrupted, resume from this point with `/afc:resume`
|
package/hooks/hooks.json
CHANGED
|
@@ -84,10 +84,8 @@
|
|
|
84
84
|
{
|
|
85
85
|
"hooks": [
|
|
86
86
|
{
|
|
87
|
-
"type": "
|
|
88
|
-
"
|
|
89
|
-
"model": "haiku",
|
|
90
|
-
"timeout": 60,
|
|
87
|
+
"type": "command",
|
|
88
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/afc-stop-todo-check.sh\"",
|
|
91
89
|
"statusMessage": "Checking code completeness..."
|
|
92
90
|
}
|
|
93
91
|
]
|
package/package.json
CHANGED
|
@@ -203,8 +203,10 @@ check_version_sync() {
|
|
|
203
203
|
else
|
|
204
204
|
pkg_ver=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$pkg" | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"//;s/"//')
|
|
205
205
|
plugin_ver=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$plugin" | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"//;s/"//')
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
# Extract metadata.version (appears after "metadata" key) and plugins[].version (appears after "plugins" key)
|
|
207
|
+
# Use awk to track context instead of relying on field ordering
|
|
208
|
+
market_meta=$(awk '/"metadata"/{found=1} found && /"version"/{gsub(/.*"version"[[:space:]]*:[[:space:]]*"/,""); gsub(/".*/,""); print; exit}' "$market" 2>/dev/null || true)
|
|
209
|
+
market_plugin=$(awk '/"plugins"/{found=1} found && /"version"/{gsub(/.*"version"[[:space:]]*:[[:space:]]*"/,""); gsub(/".*/,""); print; exit}' "$market" 2>/dev/null || true)
|
|
208
210
|
fi
|
|
209
211
|
|
|
210
212
|
if [ "$pkg_ver" = "$plugin_ver" ] && [ "$plugin_ver" = "$market_meta" ] && [ "$market_meta" = "$market_plugin" ]; then
|
|
@@ -221,8 +223,9 @@ check_phase_ssot() {
|
|
|
221
223
|
# shellcheck source=afc-state.sh
|
|
222
224
|
. "$SCRIPT_DIR/afc-state.sh"
|
|
223
225
|
|
|
224
|
-
local
|
|
225
|
-
|
|
226
|
+
local issues=0
|
|
227
|
+
|
|
228
|
+
# Sub-check A: No hardcoded phase lists in other scripts
|
|
226
229
|
for script in "$PROJECT_DIR"/scripts/afc-*.sh; do
|
|
227
230
|
local scriptname
|
|
228
231
|
scriptname=$(basename "$script")
|
|
@@ -233,12 +236,34 @@ check_phase_ssot() {
|
|
|
233
236
|
# Check for hardcoded phase case patterns (spec|plan|...|clean style)
|
|
234
237
|
if grep -qE 'spec\|plan\|.*\|clean' "$script" 2>/dev/null; then
|
|
235
238
|
fail "$scriptname contains hardcoded phase list — use SSOT helpers from afc-state.sh"
|
|
236
|
-
|
|
239
|
+
issues=$((issues + 1))
|
|
237
240
|
fi
|
|
238
241
|
done
|
|
239
242
|
|
|
240
|
-
|
|
241
|
-
|
|
243
|
+
# Sub-check B: Every command name should map to a valid phase or be a known non-phase command
|
|
244
|
+
# Non-phase commands that are not pipeline phases
|
|
245
|
+
# NOTE: Update this list when adding non-phase commands to commands/
|
|
246
|
+
local non_phase_cmds="auto|init|doctor|principles|checkpoint|resume|launch|ideate|research|architect|security|debug|analyze|test"
|
|
247
|
+
local commands_dir="$PROJECT_DIR/commands"
|
|
248
|
+
if [ -d "$commands_dir" ]; then
|
|
249
|
+
for cmd_file in "$commands_dir"/*.md; do
|
|
250
|
+
[ -f "$cmd_file" ] || continue
|
|
251
|
+
local cmd_name
|
|
252
|
+
cmd_name=$(basename "$cmd_file" .md)
|
|
253
|
+
# Skip known non-phase commands
|
|
254
|
+
if printf '%s\n' "$non_phase_cmds" | tr '|' '\n' | grep -qxF "$cmd_name"; then
|
|
255
|
+
continue
|
|
256
|
+
fi
|
|
257
|
+
# Remaining commands should correspond to a valid phase
|
|
258
|
+
if ! afc_is_valid_phase "$cmd_name"; then
|
|
259
|
+
warn "Command '$cmd_name' is not a recognized phase in AFC_VALID_PHASES and not in non-phase list"
|
|
260
|
+
issues=$((issues + 1))
|
|
261
|
+
fi
|
|
262
|
+
done
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
if [ "$issues" -eq 0 ]; then
|
|
266
|
+
ok "Phase SSOT: no hardcoded lists, all commands map to valid phases"
|
|
242
267
|
fi
|
|
243
268
|
}
|
|
244
269
|
|
|
@@ -36,8 +36,9 @@ fi
|
|
|
36
36
|
|
|
37
37
|
# Parse tasks and dependencies
|
|
38
38
|
TMPDIR_WORK="$(mktemp -d)"
|
|
39
|
-
# shellcheck disable=
|
|
40
|
-
|
|
39
|
+
# shellcheck disable=SC2329
|
|
40
|
+
cleanup() { rm -rf "$TMPDIR_WORK"; }
|
|
41
|
+
trap cleanup EXIT
|
|
41
42
|
|
|
42
43
|
NODES_FILE="$TMPDIR_WORK/nodes.txt"
|
|
43
44
|
EDGES_FILE="$TMPDIR_WORK/edges.txt"
|
package/scripts/afc-notify.sh
CHANGED
|
@@ -40,16 +40,15 @@ case "$NOTIFICATION_TYPE" in
|
|
|
40
40
|
esac
|
|
41
41
|
|
|
42
42
|
# Detect platform and send notification (non-blocking via async: true in hooks.json)
|
|
43
|
-
# Sanitize message (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# shellcheck disable=SC1003
|
|
47
|
-
SAFE_TITLE=$(printf '%s' "$TITLE" | sed 's/[\"\\$`]/\\&/g')
|
|
43
|
+
# Sanitize message (truncate, single line)
|
|
44
|
+
SAFE_MESSAGE=$(printf '%s' "$MESSAGE" | head -1 | cut -c1-200)
|
|
45
|
+
SAFE_TITLE=$(printf '%s' "$TITLE" | head -1 | cut -c1-50)
|
|
48
46
|
|
|
49
47
|
OS=$(uname -s)
|
|
50
48
|
case "$OS" in
|
|
51
49
|
Darwin)
|
|
52
|
-
|
|
50
|
+
# Use positional arguments to avoid shell/AppleScript injection
|
|
51
|
+
osascript -e 'on run argv' -e 'display notification (item 2 of argv) with title (item 1 of argv)' -e 'end run' -- "$SAFE_TITLE" "$SAFE_MESSAGE" &>/dev/null || true
|
|
53
52
|
;;
|
|
54
53
|
Linux)
|
|
55
54
|
if command -v notify-send &>/dev/null; then
|
|
@@ -15,9 +15,6 @@ cleanup() {
|
|
|
15
15
|
}
|
|
16
16
|
trap cleanup EXIT
|
|
17
17
|
|
|
18
|
-
# shellcheck disable=SC2034
|
|
19
|
-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
20
|
-
|
|
21
18
|
TASKS_FILE="${1:-}"
|
|
22
19
|
if [ -z "$TASKS_FILE" ]; then
|
|
23
20
|
printf 'Usage: %s <tasks_file_path>\n' "$0" >&2
|
|
@@ -44,8 +41,9 @@ conflict_found=0
|
|
|
44
41
|
conflict_messages=""
|
|
45
42
|
|
|
46
43
|
TMPDIR_WORK="$(mktemp -d)"
|
|
47
|
-
# shellcheck disable=
|
|
48
|
-
|
|
44
|
+
# shellcheck disable=SC2329
|
|
45
|
+
cleanup() { rm -rf "$TMPDIR_WORK"; }
|
|
46
|
+
trap cleanup EXIT
|
|
49
47
|
|
|
50
48
|
phase_index="$TMPDIR_WORK/phase_index.tsv"
|
|
51
49
|
|
|
@@ -59,7 +59,14 @@ if [ -f "$CONFIG_FILE" ]; then
|
|
|
59
59
|
# Extract ci, gate, test values from YAML code block
|
|
60
60
|
# Handles both quoted and unquoted: ci: "npm run lint" or ci: npm run lint
|
|
61
61
|
for key in ci gate test; do
|
|
62
|
+
# Try double-quoted first, then single-quoted, then unquoted
|
|
62
63
|
val=$(grep -E "^\s*${key}:\s*\"[^\"]*\"" "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*'"${key}"': *"\([^"]*\)".*/\1/' || true)
|
|
64
|
+
if [ -z "$val" ] || [ "$val" = '""' ]; then
|
|
65
|
+
val=$(grep -E "^\s*${key}:\s*'[^']*'" "$CONFIG_FILE" 2>/dev/null | head -1 | sed "s/.*${key}: *'\\([^']*\\)'.*/\\1/" || true)
|
|
66
|
+
fi
|
|
67
|
+
if [ -z "$val" ] || [ "$val" = '""' ]; then
|
|
68
|
+
val=$(grep -E "^\s*${key}:\s*[^\"']" "$CONFIG_FILE" 2>/dev/null | head -1 | sed "s/.*${key}:[[:space:]]*//" | sed 's/[[:space:]]*$//' || true)
|
|
69
|
+
fi
|
|
63
70
|
if [ -n "$val" ] && [ "$val" != '""' ]; then
|
|
64
71
|
DYNAMIC_WHITELIST="${DYNAMIC_WHITELIST:+${DYNAMIC_WHITELIST}|}${val}"
|
|
65
72
|
# Generate PM-agnostic variants (npm → pnpm, yarn, bun)
|
|
@@ -101,6 +108,7 @@ fi
|
|
|
101
108
|
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-}"
|
|
102
109
|
if [ "$ALLOWED" = "false" ] && [ -n "$PLUGIN_ROOT" ]; then
|
|
103
110
|
case "$COMMAND" in
|
|
111
|
+
*..*) ;; # Block path traversal
|
|
104
112
|
"\"${PLUGIN_ROOT}/scripts/"*|"${PLUGIN_ROOT}/scripts/"*)
|
|
105
113
|
ALLOWED=true
|
|
106
114
|
;;
|
|
@@ -117,11 +125,17 @@ if [ "$ALLOWED" = "false" ]; then
|
|
|
117
125
|
ALLOWED=true
|
|
118
126
|
;;
|
|
119
127
|
"chmod +x "*)
|
|
120
|
-
# Only allow paths within project directory (block path traversal)
|
|
128
|
+
# Only allow paths within project directory (block path traversal + symlinks)
|
|
121
129
|
TARGET="${COMMAND#chmod +x }"
|
|
122
130
|
case "$TARGET" in
|
|
123
131
|
*..*) ;; # Block path traversal
|
|
124
|
-
"$PROJECT_DIR"/*|./scripts/*|scripts/*)
|
|
132
|
+
"$PROJECT_DIR"/*|./scripts/*|scripts/*)
|
|
133
|
+
# Resolve symlinks to verify target is actually within project
|
|
134
|
+
RESOLVED=$(realpath -m "$TARGET" 2>/dev/null || echo "$TARGET")
|
|
135
|
+
case "$RESOLVED" in
|
|
136
|
+
"$PROJECT_DIR"/*) ALLOWED=true ;;
|
|
137
|
+
esac
|
|
138
|
+
;;
|
|
125
139
|
esac
|
|
126
140
|
;;
|
|
127
141
|
esac
|
|
@@ -73,9 +73,14 @@ case "$COMMAND" in
|
|
|
73
73
|
|
|
74
74
|
phase)
|
|
75
75
|
PHASE="${2:?Phase name required}"
|
|
76
|
+
if ! afc_state_is_active; then
|
|
77
|
+
echo "[afc:pipeline] No active pipeline — run '$0 start <feature>' first" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
76
80
|
if afc_is_valid_phase "$PHASE"; then
|
|
77
81
|
afc_state_write "phase" "$PHASE"
|
|
78
82
|
afc_state_invalidate_ci
|
|
83
|
+
afc_state_checkpoint "$PHASE"
|
|
79
84
|
echo "Phase: $PHASE"
|
|
80
85
|
else
|
|
81
86
|
printf "[afc:pipeline] Invalid phase: %s\n → Valid phases: %s\n" "$PHASE" "$AFC_VALID_PHASES" >&2
|
|
@@ -84,6 +89,10 @@ case "$COMMAND" in
|
|
|
84
89
|
;;
|
|
85
90
|
|
|
86
91
|
ci-pass)
|
|
92
|
+
if ! afc_state_is_active; then
|
|
93
|
+
echo "[afc:pipeline] No active pipeline — run '$0 start <feature>' first" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
87
96
|
afc_state_ci_pass
|
|
88
97
|
echo "CI passed at $(date '+%H:%M:%S')"
|
|
89
98
|
;;
|
|
@@ -126,6 +135,13 @@ case "$COMMAND" in
|
|
|
126
135
|
CHANGE_COUNT=$(printf '%s\n' "$CHANGES" | wc -l | tr -d ' ')
|
|
127
136
|
echo "Changes: $CHANGE_COUNT files"
|
|
128
137
|
fi
|
|
138
|
+
# Show checkpoint count if available
|
|
139
|
+
if command -v jq >/dev/null 2>&1; then
|
|
140
|
+
CP_COUNT=$(jq '.phaseCheckpoints | length' "$_AFC_STATE_FILE" 2>/dev/null || echo 0)
|
|
141
|
+
if [ "$CP_COUNT" -gt 0 ]; then
|
|
142
|
+
echo "Checkpoints: $CP_COUNT phases recorded"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
129
145
|
else
|
|
130
146
|
echo "No active pipeline"
|
|
131
147
|
fi
|
package/scripts/afc-state.sh
CHANGED
|
@@ -30,10 +30,16 @@ afc_is_ci_exempt() {
|
|
|
30
30
|
|
|
31
31
|
# --- Public API ---
|
|
32
32
|
|
|
33
|
-
# Check if pipeline is active (state file exists and
|
|
33
|
+
# Check if pipeline is active (state file exists, non-empty, and valid JSON with feature)
|
|
34
34
|
# Returns: 0 if active, 1 if not
|
|
35
35
|
afc_state_is_active() {
|
|
36
|
-
[ -f "$_AFC_STATE_FILE" ] && [ -s "$_AFC_STATE_FILE" ]
|
|
36
|
+
[ -f "$_AFC_STATE_FILE" ] && [ -s "$_AFC_STATE_FILE" ] || return 1
|
|
37
|
+
# Validate JSON structure — reject corrupt/truncated files
|
|
38
|
+
if command -v jq >/dev/null 2>&1; then
|
|
39
|
+
jq -e '.feature // empty' "$_AFC_STATE_FILE" >/dev/null 2>&1 || return 1
|
|
40
|
+
else
|
|
41
|
+
grep -q '"feature"' "$_AFC_STATE_FILE" 2>/dev/null || return 1
|
|
42
|
+
fi
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
# Read a field from state file
|
|
@@ -89,10 +95,11 @@ afc_state_write() {
|
|
|
89
95
|
fi
|
|
90
96
|
else
|
|
91
97
|
# sed fallback: replace or append field
|
|
92
|
-
# Escape sed-special chars in value: \ first, then &
|
|
98
|
+
# Escape sed-special chars in value: \ first, then & and /
|
|
93
99
|
local safe_val="$value"
|
|
94
100
|
safe_val="${safe_val//\\/\\\\}"
|
|
95
101
|
safe_val="${safe_val//&/\\&}"
|
|
102
|
+
safe_val="${safe_val//\//\\/}"
|
|
96
103
|
if grep -q "\"${field}\"" "$_AFC_STATE_FILE" 2>/dev/null; then
|
|
97
104
|
local tmp
|
|
98
105
|
tmp=$(mktemp)
|
|
@@ -133,8 +140,18 @@ afc_state_remove() {
|
|
|
133
140
|
else
|
|
134
141
|
rm -f "$tmp"
|
|
135
142
|
fi
|
|
143
|
+
else
|
|
144
|
+
# sed fallback: remove the field line from JSON
|
|
145
|
+
if grep -q "\"${field}\"" "$_AFC_STATE_FILE" 2>/dev/null; then
|
|
146
|
+
local tmp
|
|
147
|
+
tmp=$(mktemp)
|
|
148
|
+
# Remove line containing the field, then fix trailing commas
|
|
149
|
+
grep -v "\"${field}\"" "$_AFC_STATE_FILE" > "$tmp" 2>/dev/null || true
|
|
150
|
+
# Fix ",}" or ",]" left by removal
|
|
151
|
+
sed 's/,[[:space:]]*}/}/g; s/,[[:space:]]*\]/]/g' "$tmp" > "$_AFC_STATE_FILE"
|
|
152
|
+
rm -f "$tmp"
|
|
153
|
+
fi
|
|
136
154
|
fi
|
|
137
|
-
# sed fallback: skip removal (non-critical)
|
|
138
155
|
}
|
|
139
156
|
|
|
140
157
|
# Initialize state for a new pipeline
|
|
@@ -217,9 +234,7 @@ afc_state_checkpoint() {
|
|
|
217
234
|
return 1
|
|
218
235
|
fi
|
|
219
236
|
local git_sha=""
|
|
220
|
-
|
|
221
|
-
git_sha=$(git rev-parse --short HEAD 2>/dev/null || echo "")
|
|
222
|
-
fi
|
|
237
|
+
git_sha=$(cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" 2>/dev/null && git rev-parse --short HEAD 2>/dev/null || echo "")
|
|
223
238
|
local now
|
|
224
239
|
now=$(date +%s)
|
|
225
240
|
if command -v jq >/dev/null 2>&1; then
|
|
@@ -235,22 +250,3 @@ afc_state_checkpoint() {
|
|
|
235
250
|
fi
|
|
236
251
|
# No sed fallback — phaseCheckpoints is array-typed, too complex for sed
|
|
237
252
|
}
|
|
238
|
-
|
|
239
|
-
# Legacy fallback: check if old flag files exist
|
|
240
|
-
# Returns: 0 if legacy state found, 1 if not
|
|
241
|
-
# Side effect: sets FEATURE and PHASE variables
|
|
242
|
-
_afc_state_legacy_check() {
|
|
243
|
-
local dir="${_AFC_STATE_DIR}"
|
|
244
|
-
if [ -f "$dir/.afc-active" ]; then
|
|
245
|
-
# shellcheck disable=SC2034
|
|
246
|
-
FEATURE=$(head -1 "$dir/.afc-active" 2>/dev/null | tr -d '\n\r')
|
|
247
|
-
# shellcheck disable=SC2034
|
|
248
|
-
PHASE=""
|
|
249
|
-
if [ -f "$dir/.afc-phase" ]; then
|
|
250
|
-
# shellcheck disable=SC2034
|
|
251
|
-
PHASE=$(head -1 "$dir/.afc-phase" 2>/dev/null | tr -d '\n\r')
|
|
252
|
-
fi
|
|
253
|
-
return 0
|
|
254
|
-
fi
|
|
255
|
-
return 1
|
|
256
|
-
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
# Stop TODO Check: Scan recently changed files for leftover TODO/FIXME/HACK
|
|
4
|
+
# Replaces the haiku agent hook — runs as command for zero-overhead when pipeline inactive.
|
|
5
|
+
#
|
|
6
|
+
# Returns exit 2 (block stop) if unresolved markers found in pipeline files.
|
|
7
|
+
# Returns exit 0 (allow stop) otherwise.
|
|
8
|
+
|
|
9
|
+
# shellcheck source=afc-state.sh
|
|
10
|
+
. "$(dirname "$0")/afc-state.sh"
|
|
11
|
+
|
|
12
|
+
# shellcheck disable=SC2329
|
|
13
|
+
cleanup() {
|
|
14
|
+
:
|
|
15
|
+
}
|
|
16
|
+
trap cleanup EXIT
|
|
17
|
+
|
|
18
|
+
# Consume stdin
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
|
|
21
|
+
# Exit immediately if pipeline is not active (zero overhead)
|
|
22
|
+
if ! afc_state_is_active; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Parse stop_hook_active to prevent infinite loop
|
|
27
|
+
STOP_HOOK_ACTIVE=""
|
|
28
|
+
if command -v jq &>/dev/null; then
|
|
29
|
+
STOP_HOOK_ACTIVE=$(printf '%s\n' "$INPUT" | jq -r '.stop_hook_active // empty' 2>/dev/null || true)
|
|
30
|
+
else
|
|
31
|
+
if printf '%s\n' "$INPUT" | grep -q '"stop_hook_active"[[:space:]]*:[[:space:]]*true' 2>/dev/null; then
|
|
32
|
+
STOP_HOOK_ACTIVE="true"
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Read current phase — only check in implementation/review/clean phases
|
|
40
|
+
CURRENT_PHASE="$(afc_state_read phase || echo '')"
|
|
41
|
+
if afc_is_ci_exempt "${CURRENT_PHASE:-}"; then
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Read changed files from state
|
|
46
|
+
CHANGES=""
|
|
47
|
+
CHANGES=$(afc_state_read_changes 2>/dev/null || true)
|
|
48
|
+
|
|
49
|
+
if [ -z "$CHANGES" ]; then
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Check up to 5 recently changed files for TODO/FIXME/HACK markers
|
|
54
|
+
FOUND_MARKERS=""
|
|
55
|
+
COUNT=0
|
|
56
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
57
|
+
|
|
58
|
+
while IFS= read -r file_path; do
|
|
59
|
+
[ -z "$file_path" ] && continue
|
|
60
|
+
COUNT=$((COUNT + 1))
|
|
61
|
+
[ "$COUNT" -gt 5 ] && break
|
|
62
|
+
|
|
63
|
+
# Handle both absolute paths (from Claude tool_input) and relative paths
|
|
64
|
+
if [ "${file_path#/}" != "$file_path" ]; then
|
|
65
|
+
FULL_PATH="$file_path"
|
|
66
|
+
else
|
|
67
|
+
FULL_PATH="$PROJECT_DIR/$file_path"
|
|
68
|
+
fi
|
|
69
|
+
[ -f "$FULL_PATH" ] || continue
|
|
70
|
+
|
|
71
|
+
MARKERS=$(grep -nE '\b(TODO|FIXME|HACK)\b' "$FULL_PATH" 2>/dev/null | head -3 || true)
|
|
72
|
+
if [ -n "$MARKERS" ]; then
|
|
73
|
+
FOUND_MARKERS="${FOUND_MARKERS}${file_path}:\n${MARKERS}\n\n"
|
|
74
|
+
fi
|
|
75
|
+
done <<< "$CHANGES"
|
|
76
|
+
|
|
77
|
+
if [ -n "$FOUND_MARKERS" ]; then
|
|
78
|
+
printf "[afc:todo-check] Unresolved markers found in changed files:\n%b" "$FOUND_MARKERS" >&2
|
|
79
|
+
printf "[afc:todo-check] Resolve TODO/FIXME/HACK markers before completing the pipeline.\n" >&2
|
|
80
|
+
exit 2
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
exit 0
|
|
@@ -27,11 +27,18 @@ ENCODED_PATH="${PROJECT_PATH//\//-}"
|
|
|
27
27
|
MEMORY_DIR="$HOME/.claude/projects/$ENCODED_PATH/memory"
|
|
28
28
|
CHECKPOINT="$MEMORY_DIR/checkpoint.md"
|
|
29
29
|
|
|
30
|
-
#
|
|
30
|
+
# Project-local checkpoint path (used by /afc:resume)
|
|
31
|
+
LOCAL_MEMORY_DIR="$PROJECT_DIR/.claude/afc/memory"
|
|
32
|
+
LOCAL_CHECKPOINT="$LOCAL_MEMORY_DIR/checkpoint.md"
|
|
33
|
+
|
|
34
|
+
# Create both memory directories
|
|
31
35
|
mkdir -p "$MEMORY_DIR"
|
|
36
|
+
mkdir -p "$LOCAL_MEMORY_DIR"
|
|
32
37
|
|
|
33
38
|
# Collect current git status
|
|
34
39
|
BRANCH=$(cd "$PROJECT_DIR" 2>/dev/null && git branch --show-current 2>/dev/null || echo "unknown")
|
|
40
|
+
COMMIT_HASH=$(cd "$PROJECT_DIR" 2>/dev/null && git rev-parse --short HEAD 2>/dev/null || echo "none")
|
|
41
|
+
COMMIT_MSG=$(cd "$PROJECT_DIR" 2>/dev/null && git log -1 --format='%s' 2>/dev/null || echo "no commits")
|
|
35
42
|
|
|
36
43
|
ALL_MODIFIED=$(cd "$PROJECT_DIR" 2>/dev/null && git diff --name-only 2>/dev/null || true)
|
|
37
44
|
MODIFIED=$(printf '%s\n' "$ALL_MODIFIED" | head -10)
|
|
@@ -84,14 +91,15 @@ else
|
|
|
84
91
|
STAGED_LIST=" (none)"
|
|
85
92
|
fi
|
|
86
93
|
|
|
87
|
-
#
|
|
88
|
-
|
|
94
|
+
# Build checkpoint content (shared between both locations)
|
|
95
|
+
CHECKPOINT_CONTENT="$(cat << EOF
|
|
89
96
|
# Auto Checkpoint (Pre-Compact)
|
|
90
97
|
> Auto-generated: $(date '+%Y-%m-%d %H:%M:%S')
|
|
91
98
|
> Trigger: context compaction
|
|
92
99
|
|
|
93
100
|
## Git Status
|
|
94
101
|
- Branch: $BRANCH
|
|
102
|
+
- Commit: $COMMIT_HASH — $COMMIT_MSG
|
|
95
103
|
- Modified files: ${MODIFIED_COUNT}
|
|
96
104
|
$MODIFIED_LIST
|
|
97
105
|
|
|
@@ -107,8 +115,15 @@ $STAGED_LIST
|
|
|
107
115
|
/afc:resume
|
|
108
116
|
\`\`\`
|
|
109
117
|
EOF
|
|
118
|
+
)"
|
|
119
|
+
|
|
120
|
+
# Write to auto-memory (Claude context recovery after compaction)
|
|
121
|
+
printf '%s\n' "$CHECKPOINT_CONTENT" > "$CHECKPOINT"
|
|
122
|
+
|
|
123
|
+
# Write to project-local path (used by /afc:resume command)
|
|
124
|
+
printf '%s\n' "$CHECKPOINT_CONTENT" > "$LOCAL_CHECKPOINT"
|
|
110
125
|
|
|
111
126
|
# Inject context via stdout (Claude can see this info after compaction)
|
|
112
|
-
echo "Auto-checkpoint saved
|
|
127
|
+
echo "Auto-checkpoint saved (branch: $BRANCH, commit: $COMMIT_HASH, pipeline: ${PIPELINE_FEATURE:-inactive})"
|
|
113
128
|
|
|
114
129
|
exit 0
|
|
@@ -51,9 +51,17 @@ if afc_state_is_active; then
|
|
|
51
51
|
fi
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
-
# 2. Check if checkpoint exists
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
# 2. Check if checkpoint exists (project-local first, fallback to auto-memory)
|
|
55
|
+
LOCAL_CHECKPOINT="$PROJECT_DIR/.claude/afc/memory/checkpoint.md"
|
|
56
|
+
CHECKPOINT_FILE=""
|
|
57
|
+
if [ -f "$LOCAL_CHECKPOINT" ]; then
|
|
58
|
+
CHECKPOINT_FILE="$LOCAL_CHECKPOINT"
|
|
59
|
+
elif [ -f "$CHECKPOINT" ]; then
|
|
60
|
+
CHECKPOINT_FILE="$CHECKPOINT"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [ -n "$CHECKPOINT_FILE" ]; then
|
|
64
|
+
RAW_LINE=$(grep 'Auto-generated:' "$CHECKPOINT_FILE" 2>/dev/null || echo "")
|
|
57
65
|
FIRST_LINE=$(echo "$RAW_LINE" | head -1)
|
|
58
66
|
CHECKPOINT_DATE="${FIRST_LINE##*Auto-generated: }"
|
|
59
67
|
if [ -n "$CHECKPOINT_DATE" ]; then
|