aped-method 1.9.0 → 2.0.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/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # APED Method
2
+
3
+ Zero-dependency CLI that scaffolds a complete dev pipeline into any [Claude Code](https://claude.ai/download) project.
4
+
5
+ ```
6
+ npx aped-method
7
+ ```
8
+
9
+ ```
10
+ █████╗ ██████╗ ███████╗██████╗
11
+ ██╔══██╗██╔══██╗██╔════╝██╔══██╗
12
+ ███████║██████╔╝█████╗ ██║ ██║
13
+ ██╔══██║██╔═══╝ ██╔══╝ ██║ ██║
14
+ ██║ ██║██║ ███████╗██████╔╝
15
+ ╚═╝ ╚═╝╚═╝ ╚══════╝╚═════╝
16
+ M E T H O D
17
+
18
+ Analyze → PRD → UX → Epics → Dev → Review
19
+ ```
20
+
21
+ ## What it does
22
+
23
+ APED turns Claude Code into a disciplined dev pipeline. Instead of "code me an app", you get:
24
+
25
+ 1. **Analyze** — 3 parallel research agents (market, domain, technical) produce a product brief
26
+ 2. **PRD** — Autonomous generation with numbered FRs, validation scripts, domain detection
27
+ 3. **UX** — ANF framework: Assemble design DNA, Normalize with a live React prototype, Fill all screens with user validation
28
+ 4. **Epics** — Stories with ACs in Given/When/Then, FR coverage validation, ticket system integration
29
+ 5. **Dev** — TDD red-green-refactor with 5-condition gate, parallel agents for context gathering
30
+ 6. **Review** — Adversarial code review with minimum 3 findings, parallel agents (code-explorer + code-reviewer)
31
+
32
+ A guardrail hook blocks you from skipping steps. A state machine tracks progress. Everything chains automatically.
33
+
34
+ ## Quick start
35
+
36
+ ```bash
37
+ cd your-project
38
+ npx aped-method
39
+ ```
40
+
41
+ Interactive prompts ask for project name, author, languages, ticket system, and git provider. Or go non-interactive:
42
+
43
+ ```bash
44
+ npx aped-method --yes --project=my-app --author=Jane --lang=french --tickets=linear --git=github
45
+ ```
46
+
47
+ Then open Claude Code:
48
+
49
+ ```
50
+ /aped-a # Start with analysis
51
+ /aped-all # Or run the full pipeline
52
+ ```
53
+
54
+ ## Pipeline commands
55
+
56
+ | Command | Phase | What it produces |
57
+ |---------|-------|-----------------|
58
+ | `/aped-a` | Analyze | Product brief from 3 parallel research agents |
59
+ | `/aped-p` | PRD | PRD with numbered FRs/NFRs, validated by script |
60
+ | `/aped-ux` | UX | Live React prototype (Vite), design spec, component catalog |
61
+ | `/aped-e` | Epics | Stories with ACs, tasks, FR coverage map |
62
+ | `/aped-d` | Dev | TDD implementation with 5-condition gate |
63
+ | `/aped-r` | Review | Adversarial review, minimum 3 findings |
64
+
65
+ ## Utility commands
66
+
67
+ | Command | What it does |
68
+ |---------|-------------|
69
+ | `/aped-s` | Sprint status dashboard — progress, blockers, next actions |
70
+ | `/aped-c` | Correct course — manage scope changes with impact analysis |
71
+ | `/aped-ctx` | Brownfield analysis — generate project context from existing code |
72
+ | `/aped-qa` | Generate E2E and integration tests from acceptance criteria |
73
+ | `/aped-quick` | Quick fix/feature bypassing the full pipeline |
74
+ | `/aped-all` | Run the full pipeline A→P→UX→E→D→R with auto-resume |
75
+
76
+ ## What gets scaffolded
77
+
78
+ ```
79
+ .aped/ # Engine (immutable after install)
80
+ ├── config.yaml # Project settings, integrations
81
+ ├── hooks/guardrail.sh # Pipeline coherence hook
82
+ ├── templates/ # Document templates (brief, PRD, epics, story, quick-spec)
83
+ ├── aped-a/ # Analyze skill
84
+ │ ├── SKILL.md
85
+ │ ├── scripts/validate-brief.sh
86
+ │ └── references/research-prompts.md
87
+ ├── aped-p/ # PRD skill
88
+ │ ├── SKILL.md
89
+ │ ├── scripts/validate-prd.sh
90
+ │ └── references/fr-rules.md, *.csv
91
+ ├── aped-ux/ # UX skill (ANF framework)
92
+ │ ├── SKILL.md
93
+ │ ├── scripts/validate-ux.sh
94
+ │ └── references/ux-patterns.md # 99 priority-ranked UX rules
95
+ ├── aped-e/ # Epics skill
96
+ │ ├── SKILL.md
97
+ │ ├── scripts/validate-coverage.sh
98
+ │ └── references/epic-rules.md
99
+ ├── aped-d/ # Dev skill
100
+ │ ├── SKILL.md
101
+ │ ├── scripts/run-tests.sh
102
+ │ └── references/tdd-engine.md, ticket-git-workflow.md
103
+ ├── aped-r/ # Review skill
104
+ │ ├── SKILL.md
105
+ │ ├── scripts/git-audit.sh
106
+ │ └── references/review-criteria.md
107
+ ├── aped-s/ # Status dashboard
108
+ │ ├── SKILL.md
109
+ │ └── references/status-format.md
110
+ ├── aped-c/ # Correct course
111
+ │ ├── SKILL.md
112
+ │ └── references/scope-change-guide.md
113
+ ├── aped-ctx/ # Brownfield context
114
+ │ ├── SKILL.md
115
+ │ └── references/analysis-checklist.md
116
+ ├── aped-qa/ # QA tests
117
+ │ ├── SKILL.md
118
+ │ └── references/test-patterns.md
119
+ ├── aped-quick/SKILL.md # Quick fix
120
+ └── aped-all/SKILL.md # Orchestrator
121
+
122
+ docs/aped/ # Output (evolves during project)
123
+ ├── state.yaml # Pipeline state machine
124
+ ├── product-brief.md # From /aped-a
125
+ ├── prd.md # From /aped-p
126
+ ├── ux/ # From /aped-ux
127
+ │ ├── design-spec.md
128
+ │ ├── screen-inventory.md
129
+ │ ├── components.md
130
+ │ └── flows.md
131
+ ├── epics.md # From /aped-e
132
+ ├── stories/ # One file per story
133
+ └── quick-specs/ # From /aped-quick
134
+
135
+ .claude/
136
+ ├── commands/aped-*.md # 12 slash commands
137
+ └── settings.local.json # Guardrail hook config
138
+ ```
139
+
140
+ ## Integrations
141
+
142
+ ### Ticket systems
143
+
144
+ Configure during install or via `--tickets=`:
145
+
146
+ | Provider | Commit format | Auto-link |
147
+ |----------|--------------|-----------|
148
+ | `linear` | `feat(TEAM-XX): description` | `Part of TEAM-XX` / `Fixes TEAM-XX` |
149
+ | `jira` | `feat(PROJ-XX): description` | Smart commits |
150
+ | `github-issues` | `feat(#XX): description` | `Closes #XX` / `Fixes #XX` |
151
+ | `gitlab-issues` | `feat(#XX): description` | `Closes #XX` |
152
+ | `none` | `feat: description` | — |
153
+
154
+ ### Git providers
155
+
156
+ Configure via `--git=`:
157
+
158
+ | Provider | PR/MR creation | Branch strategy |
159
+ |----------|---------------|-----------------|
160
+ | `github` | `gh pr create` | `feature/{ticket}-{slug}` |
161
+ | `gitlab` | `glab mr create` | `feature/{ticket}-{slug}` |
162
+ | `bitbucket` | Web UI | `feature/{ticket}-{slug}` |
163
+
164
+ ## UX phase — ANF framework
165
+
166
+ The `/aped-ux` skill produces a live React prototype, not wireframes:
167
+
168
+ - **Assemble** — Collect design DNA: user inspirations, UI library (shadcn, MUI, Radix...), design tokens, branding
169
+ - **Normalize** — Scaffold Vite+React app with real PRD content (no lorem ipsum), working navigation, all screens
170
+ - **Fill** — Complete interaction states, responsive (3 breakpoints), dark mode, accessibility, then user review cycles until approved
171
+
172
+ The approved prototype becomes the UX spec that `/aped-e` consumes. Includes 99 priority-ranked UX rules and a pre-delivery checklist.
173
+
174
+ ## Guardrail hook
175
+
176
+ Every prompt is intercepted by `guardrail.sh` which checks pipeline coherence:
177
+
178
+ | Situation | Reaction |
179
+ |-----------|----------|
180
+ | Coding without epics | Warns: run A→P→E first |
181
+ | PRD without brief | Warns: run /aped-a first |
182
+ | Modifying PRD during dev | Warns: use /aped-c for scope changes |
183
+ | Quick fix request | Bypasses (that's what /aped-quick is for) |
184
+
185
+ The hook injects context — it doesn't block. Claude explains the issue and asks for confirmation.
186
+
187
+ ## Install / Update / Fresh
188
+
189
+ ```bash
190
+ # First install
191
+ npx aped-method
192
+
193
+ # Re-run on existing project → auto-detects, offers:
194
+ # 1. Update engine (preserve config + artifacts)
195
+ # 2. Fresh install (delete everything, start over)
196
+ # 3. Cancel
197
+
198
+ # Non-interactive
199
+ npx aped-method --yes # Auto-update if exists
200
+ npx aped-method --yes --update # Explicit update
201
+ npx aped-method --yes --fresh # Nuke and redo
202
+
203
+ # Version check
204
+ npx aped-method --version # Shows current version
205
+ # If installed version < CLI version → "Upgrade available: v1.x → v2.x"
206
+ ```
207
+
208
+ ## Anthropic Skills Guide compliance
209
+
210
+ All 12 skills follow the [Complete Guide to Building Skills for Claude](https://www.anthropic.com/engineering/claude-skills-guide):
211
+
212
+ - YAML frontmatter with name, description (WHAT + WHEN + negative triggers), license, metadata
213
+ - Progressive disclosure: frontmatter → SKILL.md body → references/
214
+ - `disable-model-invocation: true` on side-effect skills (dev, review, correct, all)
215
+ - `allowed-tools` restrictions on read-only skills (status, context)
216
+ - Critical Rules section at top of pipeline skills
217
+ - Examples and Common Issues in every skill
218
+ - Validation scripts with clear exit codes
219
+ - Skill tool chaining (not slash notation)
220
+ - Third-person descriptions, <1024 chars, no XML tags
221
+
222
+ ## Requirements
223
+
224
+ - [Claude Code](https://claude.ai/download)
225
+ - Node.js 18+
226
+
227
+ ## License
228
+
229
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aped-method",
3
- "version": "1.9.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "description": "Scaffold the APED pipeline (Analyze, PRD, Epics, Dev, Review) into any Claude Code project",
6
6
  "bin": {
@@ -7,85 +7,86 @@ export function guardrail(c) {
7
7
  executable: true,
8
8
  content: `#!/usr/bin/env bash
9
9
  # APED Guardrail — UserPromptSubmit hook
10
- # Validates prompt coherence against pipeline state, existing artifacts, and memories.
11
- # Returns JSON with "decision" and optionally "addToPrompt" to inject context.
10
+ # Uses the official Claude Code hook output format.
11
+ # See: https://code.claude.com/docs/hooks
12
12
 
13
- set -euo pipefail
13
+ # NOT using set -e or pipefail — grep returns exit 1 on no match
14
+ # which would kill the script. We handle errors explicitly.
14
15
 
15
- PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
16
- STATE_FILE="$PROJECT_ROOT/${o}/state.yaml"
17
- CONFIG_FILE="$PROJECT_ROOT/${a}/config.yaml"
18
- OUTPUT_DIR="$PROJECT_ROOT/${o}"
16
+ # ── Read stdin JSON ──
17
+ INPUT=$(cat < /dev/stdin)
19
18
 
20
- # Read stdin (JSON: {"session_id":"...","prompt":"..."})
21
- INPUT=$(cat)
22
- PROMPT=$(echo "$INPUT" | grep -o '"prompt":"[^"]*"' | sed 's/"prompt":"//;s/"$//' || echo "")
19
+ # Extract prompt using simple string parsing (no jq dependency)
20
+ PROMPT=""
21
+ if command -v jq &>/dev/null; then
22
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty' 2>/dev/null || echo "")
23
+ else
24
+ PROMPT=$(echo "$INPUT" | grep -o '"prompt":"[^"]*"' | sed 's/"prompt":"//;s/"$//' 2>/dev/null || echo "")
25
+ fi
23
26
 
24
27
  # If no prompt or empty, allow
25
28
  if [[ -z "$PROMPT" ]]; then
26
29
  exit 0
27
30
  fi
28
31
 
32
+ # ── Resolve paths ──
33
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
34
+ STATE_FILE="$PROJECT_ROOT/${o}/state.yaml"
35
+ CONFIG_FILE="$PROJECT_ROOT/${a}/config.yaml"
36
+ OUTPUT_DIR="$PROJECT_ROOT/${o}"
37
+
29
38
  # ── Read current phase from state.yaml ──
30
39
  CURRENT_PHASE="none"
31
40
  if [[ -f "$STATE_FILE" ]]; then
32
- CURRENT_PHASE=$(grep 'current_phase:' "$STATE_FILE" | sed 's/.*current_phase:[[:space:]]*"\\{0,1\\}\\([^"]*\\)"\\{0,1\\}/\\1/' | tr -d ' ' || echo "none")
41
+ PHASE_LINE=$(grep 'current_phase:' "$STATE_FILE" 2>/dev/null || echo "")
42
+ if [[ -n "$PHASE_LINE" ]]; then
43
+ CURRENT_PHASE=$(echo "$PHASE_LINE" | sed 's/.*current_phase:[[:space:]]*//;s/"//g;s/^[[:space:]]*//;s/[[:space:]]*$//' || echo "none")
44
+ fi
33
45
  fi
34
46
 
35
47
  # ── Read communication language from config ──
36
- LANG="english"
48
+ COMM_LANG="english"
37
49
  if [[ -f "$CONFIG_FILE" ]]; then
38
- LANG=$(grep 'communication_language:' "$CONFIG_FILE" | sed 's/.*communication_language:[[:space:]]*//' | tr -d ' ' || echo "english")
50
+ LANG_LINE=$(grep 'communication_language:' "$CONFIG_FILE" 2>/dev/null || echo "")
51
+ if [[ -n "$LANG_LINE" ]]; then
52
+ COMM_LANG=$(echo "$LANG_LINE" | sed 's/.*communication_language:[[:space:]]*//' | tr -d ' ' || echo "english")
53
+ fi
39
54
  fi
40
55
 
41
- # ── Detect what the user is trying to do ──
56
+ # ── Detect intent from prompt ──
42
57
  PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
43
58
 
44
- # Detect APED command invocations
45
59
  WANTS_ANALYZE=false
46
60
  WANTS_PRD=false
47
61
  WANTS_EPICS=false
48
62
  WANTS_DEV=false
49
63
  WANTS_REVIEW=false
50
64
  WANTS_ALL=false
51
- WANTS_CODE=false
52
-
53
- [[ "$PROMPT_LOWER" =~ (aped-a|/aped-a|analyze|analyse|research) ]] && WANTS_ANALYZE=true
54
- [[ "$PROMPT_LOWER" =~ (aped-p|/aped-p|prd|product.req) ]] && WANTS_PRD=true
55
- [[ "$PROMPT_LOWER" =~ (aped-e|/aped-e|epic|stories|story) ]] && WANTS_EPICS=true
56
- [[ "$PROMPT_LOWER" =~ (aped-d|/aped-d|dev|implement|code|build|create.*component|create.*service) ]] && WANTS_DEV=true
57
- [[ "$PROMPT_LOWER" =~ (aped-r|/aped-r|review|audit) ]] && WANTS_REVIEW=true
58
- [[ "$PROMPT_LOWER" =~ (aped-all|/aped-all|full.pipeline|start.from.scratch) ]] && WANTS_ALL=true
59
65
  WANTS_QUICK=false
66
+ WANTS_CODE=false
60
67
 
61
- [[ "$PROMPT_LOWER" =~ (aped-quick|/aped-quick|quick.fix|quick.feature|hotfix) ]] && WANTS_QUICK=true
62
- [[ "$PROMPT_LOWER" =~ (code|implement|write.*function|create.*file|add.*feature|fix.*bug|refactor) ]] && WANTS_CODE=true
68
+ echo "$PROMPT_LOWER" | grep -qE '(aped-a|/aped-a|analyze|analyse|research)' && WANTS_ANALYZE=true
69
+ echo "$PROMPT_LOWER" | grep -qE '(aped-p|/aped-p|prd|product.req)' && WANTS_PRD=true
70
+ echo "$PROMPT_LOWER" | grep -qE '(aped-e|/aped-e|epic|stories|story)' && WANTS_EPICS=true
71
+ echo "$PROMPT_LOWER" | grep -qE '(aped-d|/aped-d|dev|implement|build|create.*component|create.*service)' && WANTS_DEV=true
72
+ echo "$PROMPT_LOWER" | grep -qE '(aped-r|/aped-r|review|audit)' && WANTS_REVIEW=true
73
+ echo "$PROMPT_LOWER" | grep -qE '(aped-all|/aped-all|full.pipeline|start.from.scratch)' && WANTS_ALL=true
74
+ echo "$PROMPT_LOWER" | grep -qE '(aped-quick|/aped-quick|quick.fix|quick.feature|hotfix)' && WANTS_QUICK=true
75
+ echo "$PROMPT_LOWER" | grep -qE '(code|implement|write.*function|create.*file|add.*feature|fix.*bug|refactor)' && WANTS_CODE=true
63
76
 
64
77
  # ── Check artifact existence ──
65
78
  HAS_BRIEF=false
66
79
  HAS_PRD=false
67
80
  HAS_EPICS=false
68
81
 
69
- [[ -n "$(find "$OUTPUT_DIR" -name '*brief*' -o -name '*product-brief*' 2>/dev/null | head -1)" ]] && HAS_BRIEF=true
70
- [[ -n "$(find "$OUTPUT_DIR" -name '*prd*' 2>/dev/null | head -1)" ]] && HAS_PRD=true
71
- [[ -n "$(find "$OUTPUT_DIR" -name '*epic*' 2>/dev/null | head -1)" ]] && HAS_EPICS=true
82
+ if [[ -d "$OUTPUT_DIR" ]]; then
83
+ test -n "$(find "$OUTPUT_DIR" -maxdepth 1 -name '*brief*' 2>/dev/null | head -1)" && HAS_BRIEF=true
84
+ test -n "$(find "$OUTPUT_DIR" -maxdepth 1 -name '*prd*' 2>/dev/null | head -1)" && HAS_PRD=true
85
+ test -n "$(find "$OUTPUT_DIR" -maxdepth 1 -name '*epic*' 2>/dev/null | head -1)" && HAS_EPICS=true
86
+ fi
72
87
 
73
88
  # ── Phase-aware guardrail logic ──
74
- WARNINGS=()
75
-
76
- # Phase transition map: none → analyze → prd → ux → epics → dev ↔ review → done
77
- PHASE_ORDER="none analyze prd ux epics dev review done"
78
-
79
- phase_index() {
80
- local i=0
81
- for p in $PHASE_ORDER; do
82
- [[ "$p" == "$1" ]] && echo "$i" && return
83
- i=$((i + 1))
84
- done
85
- echo "0"
86
- }
87
-
88
- CURRENT_IDX=$(phase_index "$CURRENT_PHASE")
89
+ WARNINGS=""
89
90
 
90
91
  # Rule 0: Quick mode bypasses pipeline checks
91
92
  if [[ "$WANTS_QUICK" == "true" ]]; then
@@ -95,65 +96,66 @@ fi
95
96
  # Rule 1: Trying to code without epics/stories
96
97
  if [[ "$WANTS_CODE" == "true" || "$WANTS_DEV" == "true" ]] && [[ "$CURRENT_PHASE" != "dev" && "$CURRENT_PHASE" != "review" ]]; then
97
98
  if [[ "$HAS_EPICS" == "false" ]]; then
98
- WARNINGS+=("SKIP_DETECTED: Attempting dev/code without epics. Current phase: $CURRENT_PHASE. Run /aped-a /aped-p /aped-e first.")
99
+ WARNINGS="$WARNINGS\\nSKIP_DETECTED: Attempting dev/code without epics. Current phase: $CURRENT_PHASE. Run /aped-a, /aped-p, /aped-e first."
99
100
  elif [[ "$HAS_PRD" == "false" ]]; then
100
- WARNINGS+=("SKIP_DETECTED: Attempting dev/code without PRD. Current phase: $CURRENT_PHASE. Run /aped-a /aped-p first.")
101
+ WARNINGS="$WARNINGS\\nSKIP_DETECTED: Attempting dev/code without PRD. Current phase: $CURRENT_PHASE. Run /aped-a, /aped-p first."
101
102
  fi
102
103
  fi
103
104
 
104
105
  # Rule 2: PRD without brief
105
106
  if [[ "$WANTS_PRD" == "true" ]] && [[ "$HAS_BRIEF" == "false" ]] && [[ "$CURRENT_PHASE" != "prd" ]]; then
106
- WARNINGS+=("MISSING_ARTIFACT: No product brief found. Run /aped-a first to generate the brief.")
107
+ WARNINGS="$WARNINGS\\nMISSING_ARTIFACT: No product brief found. Run /aped-a first."
107
108
  fi
108
109
 
109
110
  # Rule 3: Epics without PRD
110
111
  if [[ "$WANTS_EPICS" == "true" ]] && [[ "$HAS_PRD" == "false" ]]; then
111
- WARNINGS+=("MISSING_ARTIFACT: No PRD found. Run /aped-p first to generate the PRD.")
112
+ WARNINGS="$WARNINGS\\nMISSING_ARTIFACT: No PRD found. Run /aped-p first."
112
113
  fi
113
114
 
114
115
  # Rule 4: Review without dev
115
116
  if [[ "$WANTS_REVIEW" == "true" ]] && [[ "$CURRENT_PHASE" != "dev" && "$CURRENT_PHASE" != "review" ]]; then
116
- WARNINGS+=("PREMATURE_REVIEW: No story has been developed yet. Run /aped-d first.")
117
+ WARNINGS="$WARNINGS\\nPREMATURE_REVIEW: No story developed yet. Run /aped-d first."
117
118
  fi
118
119
 
119
- # Rule 5: Trying to modify upstream artifacts during dev
120
+ # Rule 5: Modifying upstream during dev
120
121
  if [[ "$CURRENT_PHASE" == "dev" || "$CURRENT_PHASE" == "review" ]]; then
121
122
  if [[ "$WANTS_PRD" == "true" || "$WANTS_ANALYZE" == "true" ]]; then
122
- WARNINGS+=("SCOPE_CHANGE: You are in phase '$CURRENT_PHASE'. Modifying the PRD/brief now will invalidate existing epics and stories. Use /aped-r with correct-course protocol instead.")
123
+ WARNINGS="$WARNINGS\\nSCOPE_CHANGE: Phase is '$CURRENT_PHASE'. Modifying PRD/brief invalidates epics and stories. Use /aped-c instead."
123
124
  fi
124
125
  fi
125
126
 
126
127
  # Rule 6: Skipping phases
127
- if [[ "$WANTS_DEV" == "true" ]] && [[ "$CURRENT_IDX" -lt 3 ]] && [[ "$WANTS_ALL" == "false" ]]; then
128
+ if [[ "$WANTS_DEV" == "true" ]] && [[ "$WANTS_ALL" == "false" ]]; then
128
129
  if [[ "$CURRENT_PHASE" == "none" || "$CURRENT_PHASE" == "analyze" ]]; then
129
- WARNINGS+=("PHASE_SKIP: Jumping from '$CURRENT_PHASE' to dev skips critical pipeline steps. The pipeline ensures quality: Analyze PRD Epics Dev.")
130
+ WARNINGS="$WARNINGS\\nPHASE_SKIP: Jumping from '$CURRENT_PHASE' to dev skips critical steps. Pipeline: Analyze -> PRD -> Epics -> Dev."
130
131
  fi
131
132
  fi
132
133
 
133
- # ── Build response ──
134
- if [[ \${#WARNINGS[@]} -eq 0 ]]; then
135
- # No issues, allow silently
134
+ # ── No warnings = allow silently ──
135
+ if [[ -z "$WARNINGS" ]]; then
136
136
  exit 0
137
137
  fi
138
138
 
139
- # Build the addToPrompt context
140
- CONTEXT="[APED GUARDRAIL Pipeline Coherence Check]\\n"
141
- CONTEXT+="Current phase: $CURRENT_PHASE\\n"
142
- CONTEXT+="Artifacts: brief=\${HAS_BRIEF}, prd=\${HAS_PRD}, epics=\${HAS_EPICS}\\n"
143
- CONTEXT+="\\nWarnings detected:\\n"
144
-
145
- for w in "\${WARNINGS[@]}"; do
146
- CONTEXT+=" ⚠ $w\\n"
147
- done
139
+ # ── Build context for Claude ──
140
+ CONTEXT="[APED GUARDRAIL] Pipeline coherence check.\\nCurrent phase: $CURRENT_PHASE | Artifacts: brief=$HAS_BRIEF, prd=$HAS_PRD, epics=$HAS_EPICS\\nWarnings:$WARNINGS"
148
141
 
149
- if [[ "$LANG" == "french" ]] || [[ "$LANG" == "français" ]]; then
150
- CONTEXT+="\\nINSTRUCTION: Signale ces avertissements à l'utilisateur en français AVANT d'exécuter quoi que ce soit. Explique le problème et propose le chemin correct dans le pipeline. Demande confirmation si l'utilisateur veut quand même continuer."
142
+ if [[ "$COMM_LANG" == "french" ]] || [[ "$COMM_LANG" == "français" ]]; then
143
+ CONTEXT="$CONTEXT\\n\\nINSTRUCTION: Signale ces avertissements a l utilisateur en francais AVANT d executer quoi que ce soit. Explique le probleme et propose le chemin correct. Demande confirmation pour continuer."
151
144
  else
152
- CONTEXT+="\\nINSTRUCTION: Report these warnings to the user BEFORE executing anything. Explain the issue and suggest the correct pipeline path. Ask for confirmation if the user wants to proceed anyway."
145
+ CONTEXT="$CONTEXT\\n\\nINSTRUCTION: Report these warnings to the user BEFORE executing anything. Explain the issue and suggest the correct pipeline path. Ask for confirmation to proceed."
153
146
  fi
154
147
 
155
- # Output JSON with addToPrompt does not block, but injects context
156
- printf '{"addToPrompt": "%s"}' "$CONTEXT"
148
+ # ── Output using official Claude Code hook format ──
149
+ # UserPromptSubmit: use hookSpecificOutput.additionalContext to inject context
150
+ # Does NOT block — injects context so Claude warns the user
151
+ cat << HOOKEOF
152
+ {
153
+ "hookSpecificOutput": {
154
+ "hookEventName": "UserPromptSubmit",
155
+ "additionalContext": "$(echo "$CONTEXT" | sed 's/"/\\\\"/g')"
156
+ }
157
+ }
158
+ HOOKEOF
157
159
  `,
158
160
  },
159
161
  {
@@ -66,6 +66,10 @@ export function references(c) {
66
66
  path: `${a}/aped-qa/references/test-patterns.md`,
67
67
  content: TEST_PATTERNS,
68
68
  },
69
+ {
70
+ path: `${a}/aped-d/references/ticket-git-workflow.md`,
71
+ content: TICKET_GIT_WORKFLOW,
72
+ },
69
73
  ];
70
74
  }
71
75
 
@@ -1111,3 +1115,192 @@ describe("{Endpoint/Service}", () => {
1111
1115
  - **Testing implementation**: Test behavior, not internal structure.
1112
1116
  - **No assertions**: Every test must assert something. \`expect(true).toBe(true)\` is not a test.
1113
1117
  `;
1118
+
1119
+ const TICKET_GIT_WORKFLOW = `# Ticket System & Git Provider Integration
1120
+
1121
+ Read \`ticket_system\` and \`git_provider\` from config.yaml to adapt all instructions below.
1122
+
1123
+ ---
1124
+
1125
+ ## Ticket System Sync Rules
1126
+
1127
+ ### If ticket_system = "none"
1128
+ Skip all ticket references. Use plain commit messages without ticket IDs.
1129
+
1130
+ ### If ticket_system = "linear"
1131
+
1132
+ **BEFORE starting a story:**
1133
+ 1. Find the corresponding Linear issue
1134
+ 2. Move issue status to **In Progress**
1135
+ 3. Use the **Linear-suggested git branch name** (from Linear UI: "Copy git branch name")
1136
+ 4. Add a comment on the issue: what you're about to implement
1137
+
1138
+ **DURING development:**
1139
+ - Reference the Linear issue ID in EVERY commit message
1140
+ - Use **Linear magic words** for auto-linking:
1141
+ - \`Part of TEAM-XX\` — links without closing (use in intermediate commits)
1142
+ - \`Fixes TEAM-XX\` — links and auto-closes issue on merge
1143
+ - Commit format: \`type(TEAM-XX): description\\n\\nPart of TEAM-XX\`
1144
+
1145
+ **AFTER completing:**
1146
+ 1. Create PR with issue ID: \`gh pr create --title "feat(TEAM-XX): Story X.Y - Description" --body "Fixes TEAM-XX"\`
1147
+ 2. Move issue to **In Review**
1148
+ 3. After merge: move to **Done**
1149
+ 4. Update state.yaml to match
1150
+
1151
+ ### If ticket_system = "jira"
1152
+
1153
+ **BEFORE:** Find JIRA issue (PROJ-XX), move to In Progress, use branch: \`feature/PROJ-XX-description\`
1154
+
1155
+ **DURING:**
1156
+ - Reference JIRA issue ID in every commit: \`type(PROJ-XX): description\`
1157
+ - JIRA smart commits: \`PROJ-XX #in-progress\`, \`PROJ-XX #done\`
1158
+
1159
+ **AFTER:**
1160
+ - PR title: \`feat(PROJ-XX): Story X.Y - Description\`
1161
+ - JIRA auto-links PRs via issue ID in branch name or commit
1162
+
1163
+ ### If ticket_system = "github-issues"
1164
+
1165
+ **BEFORE:** Find GitHub issue #XX, assign yourself
1166
+
1167
+ **DURING:**
1168
+ - Reference in commits: \`type(#XX): description\`
1169
+ - Use \`Closes #XX\` or \`Fixes #XX\` in final commit/PR body
1170
+
1171
+ **AFTER:**
1172
+ - \`gh pr create --title "feat: Story X.Y" --body "Closes #XX"\`
1173
+ - Issue auto-closes when PR merges
1174
+
1175
+ ### If ticket_system = "gitlab-issues"
1176
+
1177
+ **BEFORE:** Find GitLab issue #XX, assign yourself
1178
+
1179
+ **DURING:**
1180
+ - Reference: \`type(#XX): description\`
1181
+ - Use \`Closes #XX\` in commit/MR body
1182
+
1183
+ **AFTER:**
1184
+ - \`glab mr create --title "feat: Story X.Y" --description "Closes #XX"\`
1185
+ - Issue auto-closes when MR merges
1186
+
1187
+ ---
1188
+
1189
+ ## Git Provider Workflow
1190
+
1191
+ ### If git_provider = "github"
1192
+
1193
+ **Branch strategy:**
1194
+ \`\`\`
1195
+ main (production)
1196
+ └── develop (integration, if configured)
1197
+ └── feature/{ticket-id}-description
1198
+ \`\`\`
1199
+
1200
+ **Commands:**
1201
+ \`\`\`bash
1202
+ # Start story
1203
+ git checkout main # or develop if exists
1204
+ git pull
1205
+ git checkout -b feature/{ticket-id}-description
1206
+
1207
+ # During dev
1208
+ git add <specific-files> # NEVER git add . or git add -A
1209
+ git commit -m "type({ticket-id}): description"
1210
+
1211
+ # Complete
1212
+ git push -u origin feature/{ticket-id}-description
1213
+ gh pr create --base main --title "type({ticket-id}): Story X.Y - Title" --body "Fixes {ticket-id}"
1214
+
1215
+ # After merge
1216
+ git checkout main && git pull
1217
+ git branch -d feature/{ticket-id}-description
1218
+ \`\`\`
1219
+
1220
+ ### If git_provider = "gitlab"
1221
+
1222
+ **Commands:**
1223
+ \`\`\`bash
1224
+ # Start
1225
+ git checkout main && git pull
1226
+ git checkout -b feature/{ticket-id}-description
1227
+
1228
+ # Complete
1229
+ git push -u origin feature/{ticket-id}-description
1230
+ glab mr create --base main --title "type({ticket-id}): Story X.Y" --description "Closes {ticket-id}"
1231
+
1232
+ # After merge
1233
+ git checkout main && git pull
1234
+ git branch -d feature/{ticket-id}-description
1235
+ \`\`\`
1236
+
1237
+ ### If git_provider = "bitbucket"
1238
+
1239
+ **Commands:**
1240
+ \`\`\`bash
1241
+ # Start
1242
+ git checkout main && git pull
1243
+ git checkout -b feature/{ticket-id}-description
1244
+
1245
+ # Complete
1246
+ git push -u origin feature/{ticket-id}-description
1247
+ # Create PR via Bitbucket web UI or API
1248
+ \`\`\`
1249
+
1250
+ ---
1251
+
1252
+ ## Commit Message Format
1253
+
1254
+ \`\`\`
1255
+ type({ticket-id}): short description
1256
+
1257
+ [Optional body]
1258
+
1259
+ {Magic word} {ticket-id}
1260
+ \`\`\`
1261
+
1262
+ | Prefix | Usage |
1263
+ |--------|-------|
1264
+ | feat | New feature / story implementation |
1265
+ | fix | Bug fix |
1266
+ | refactor | Code restructuring (no behavior change) |
1267
+ | test | Adding or updating tests |
1268
+ | docs | Documentation changes |
1269
+ | chore | Build, config, tooling changes |
1270
+
1271
+ ---
1272
+
1273
+ ## State Sync
1274
+
1275
+ Local state.yaml and ticket system MUST agree:
1276
+
1277
+ | state.yaml | Linear | Jira | GitHub/GitLab Issues |
1278
+ |------------|--------|------|---------------------|
1279
+ | backlog | Backlog | Backlog | No label |
1280
+ | ready-for-dev | Todo | To Do | "ready" label |
1281
+ | in-progress | In Progress | In Progress | "in progress" label |
1282
+ | review | In Review | In Review | PR linked |
1283
+ | done | Done | Done | Closed |
1284
+
1285
+ **If they diverge, the ticket system is the authority.** Update state.yaml to match.
1286
+
1287
+ ---
1288
+
1289
+ ## Epic/Milestone Tracking
1290
+
1291
+ - When first story of an epic moves to In Progress → update epic/milestone status
1292
+ - When ALL stories in an epic are Done → mark milestone complete
1293
+ - Keep milestone descriptions updated if scope changes
1294
+
1295
+ ---
1296
+
1297
+ ## Critical Rules
1298
+
1299
+ 1. NEVER commit directly to main
1300
+ 2. ALWAYS create feature branch before starting
1301
+ 3. ALWAYS include ticket ID in every commit message
1302
+ 4. ALWAYS update ticket status: In Progress → In Review → Done
1303
+ 5. ALWAYS stage specific files — never \`git add .\` or \`git add -A\`
1304
+ 6. ALWAYS use ticket system's suggested branch name when available
1305
+ 7. NEVER commit secrets (.env, API keys, settings.local.json)
1306
+ `;
@@ -92,8 +92,8 @@ for section in "\${REQUIRED_SECTIONS[@]}"; do
92
92
  fi
93
93
  done
94
94
 
95
- # Check FR format
96
- FR_LINES=$(grep -E '^FR[0-9]+:' "$FILE" 2>/dev/null || true)
95
+ # Check FR format — accepts: FR1:, - FR1:, **FR1:**, * FR1:, etc.
96
+ FR_LINES=$(grep -E '(^|[-*>[:space:]])\\*{0,2}FR[0-9]+\\*{0,2}\\s*:' "$FILE" 2>/dev/null || true)
97
97
  FR_COUNT=0
98
98
  if [[ -n "$FR_LINES" ]]; then
99
99
  FR_COUNT=$(echo "$FR_LINES" | wc -l | tr -d ' ')
@@ -111,7 +111,7 @@ fi
111
111
  ANTI_PATTERNS=("easy" "intuitive" "fast" "responsive" "simple" "multiple" "several" "various")
112
112
 
113
113
  for pattern in "\${ANTI_PATTERNS[@]}"; do
114
- MATCHES=$(grep -inE "^FR[0-9]+:.*\\b\${pattern}\\b" "$FILE" 2>/dev/null || true)
114
+ MATCHES=$(grep -inE 'FR[0-9]+.*:.*\\b\${pattern}\\b' "$FILE" 2>/dev/null || true)
115
115
  if [[ -n "$MATCHES" ]]; then
116
116
  ISSUES+=("ANTI-PATTERN '$pattern' found in FR: $MATCHES")
117
117
  fi
@@ -335,13 +335,18 @@ Story files: \`${o}/stories/{story-key}.md\`
335
335
 
336
336
  ## Ticket System Integration
337
337
 
338
- Read \`ticket_system\` from config. If not \`none\`:
338
+ Read \`ticket_system\` from config. Read \`${a}/aped-d/references/ticket-git-workflow.md\` for full guide.
339
+
340
+ If \`ticket_system\` is not \`none\`:
339
341
  - Add ticket reference in each story header: \`**Ticket:** {{ticket_id}}\`
340
- - If \`jira\`: format as \`PROJ-###\` placeholder
341
- - If \`linear\`: format as \`TEAM-###\` placeholder
342
- - If \`github-issues\`: format as \`#issue_number\` placeholder
343
- - If \`gitlab-issues\`: format as \`#issue_number\` placeholder
342
+ - Add suggested branch name: \`**Branch:** feature/{{ticket_id}}-{{story-slug}}\`
343
+ - Format ticket ID per provider:
344
+ - \`linear\`: \`TEAM-###\` (e.g., \`KON-10\`)
345
+ - \`jira\`: \`PROJ-###\` (e.g., \`PROJ-42\`)
346
+ - \`github-issues\`: \`#issue_number\` (e.g., \`#10\`)
347
+ - \`gitlab-issues\`: \`#issue_number\` (e.g., \`#10\`)
344
348
  - Note: actual ticket creation is manual — these are reference placeholders
349
+ - In Dev Notes, add: "Commit prefix: \`feat({{ticket_id}})\`"
345
350
 
346
351
  ## FR Coverage Map
347
352
 
@@ -469,11 +474,32 @@ Mark \`[x]\` ONLY when: tests exist, pass 100%, implementation matches, ACs sati
469
474
 
470
475
  **STOP and ask user if:** new dependency, 3 consecutive failures, missing config, ambiguity.
471
476
 
472
- ## Git Commit Convention
477
+ ## Git & Ticket Workflow
478
+
479
+ Read \`${a}/aped-d/references/ticket-git-workflow.md\` for full integration guide.
480
+
481
+ Read \`ticket_system\` and \`git_provider\` from \`${a}/config.yaml\`.
482
+
483
+ ### Before Implementation
484
+ If \`ticket_system\` is not \`none\`:
485
+ 1. Find the corresponding ticket/issue for this story
486
+ 2. Move ticket status to **In Progress**
487
+ 3. Create feature branch using ticket system's suggested name
488
+ 4. Add a comment on the ticket: implementation plan
489
+
490
+ If \`ticket_system\` is \`none\`:
491
+ 1. Create branch: \`feature/{story-key}\`
473
492
 
474
- Read \`git_provider\` and \`ticket_system\` from config:
475
- - Commit message format: \`type(scope): description\`
476
- - If ticket system configured, append ticket ref: \`type(scope): description [TICKET-ID]\`
493
+ ### During Implementation
494
+ - Include ticket ID in EVERY commit: \`type({ticket-id}): description\`
495
+ - Use magic words for auto-linking (see reference doc)
496
+ - NEVER use \`git add .\` — stage specific files only
497
+
498
+ ### After Implementation
499
+ 1. Push branch and create PR/MR (adapt to \`git_provider\`):
500
+ - \`github\`: \`gh pr create --title "feat({ticket-id}): Story X.Y" --body "Fixes {ticket-id}"\`
501
+ - \`gitlab\`: \`glab mr create --title "feat({ticket-id}): Story X.Y" --description "Closes {ticket-id}"\`
502
+ 2. Move ticket to **In Review**
477
503
 
478
504
  ## Completion
479
505
 
@@ -572,6 +598,20 @@ Severity: CRITICAL > HIGH > MEDIUM > LOW. Format: \`[Severity] Description [file
572
598
  - MEDIUM/LOW only: fix automatically, story — \`done\`
573
599
  - HIGH+: fix or add \`[AI-Review]\` items, story — \`in-progress\`
574
600
 
601
+ ## Ticket & Git Update
602
+
603
+ Read \`ticket_system\` and \`git_provider\` from \`${a}/config.yaml\`.
604
+ Read \`${a}/aped-d/references/ticket-git-workflow.md\` for details.
605
+
606
+ If story → \`done\`:
607
+ 1. If PR exists: approve/merge (adapt to \`git_provider\`)
608
+ 2. If \`ticket_system\` is not \`none\`: move ticket to **Done**
609
+ 3. Cleanup: delete feature branch after merge
610
+
611
+ If story → \`in-progress\` (review found HIGH+ issues):
612
+ 1. Add [AI-Review] items as comments on the PR
613
+ 2. Ticket stays in **In Review**
614
+
575
615
  ## State Update
576
616
 
577
617
  Update \`${o}/state.yaml\`. If more stories remain: invoke Skill tool with \`skill: "aped-d"\`. If all stories done: report pipeline completion.
@@ -966,11 +1006,20 @@ Based on current state, suggest the next logical command:
966
1006
  - If all stories \`done\`: suggest pipeline complete
967
1007
  - If blockers found: describe resolution path
968
1008
 
969
- ## Ticket System Integration
1009
+ ## Ticket System Sync
1010
+
1011
+ Read \`${a}/aped-d/references/ticket-git-workflow.md\` for status mapping.
970
1012
 
971
1013
  If \`ticket_system\` is not \`none\`:
972
- - Show ticket references alongside story statuses
973
- - Note any stories without ticket references
1014
+ - Show ticket ID alongside each story status
1015
+ - Flag any stories without ticket references
1016
+ - Check sync: compare state.yaml statuses with expected ticket statuses
1017
+ - If divergence detected: warn user — "state.yaml says X, ticket system should be Y"
1018
+ - Display mapping table:
1019
+ - \`backlog\` → Backlog/Todo
1020
+ - \`in-progress\` → In Progress
1021
+ - \`review\` → In Review
1022
+ - \`done\` → Done
974
1023
 
975
1024
  ## Output
976
1025
 
@@ -1402,13 +1451,18 @@ Quick checklist — no full adversarial review:
1402
1451
  - [ ] No regressions in existing tests
1403
1452
  - [ ] AC from quick spec satisfied
1404
1453
 
1405
- ## Git Commit
1454
+ ## Git & Ticket Workflow
1406
1455
 
1407
1456
  Read \`ticket_system\` and \`git_provider\` from config.
1408
- - Format: \`type(scope): description\`
1409
- - Append ticket ref if configured
1410
- - If \`git_provider\` is \`github\`: suggest PR creation with \`gh pr create\`
1411
- - If \`git_provider\` is \`gitlab\`: suggest MR creation with \`glab mr create\`
1457
+ Read \`${a}/aped-d/references/ticket-git-workflow.md\` for full guide.
1458
+
1459
+ 1. **Branch**: create \`fix/{ticket-id}-{slug}\` or \`feature/{ticket-id}-{slug}\`
1460
+ 2. **Commits**: \`type({ticket-id}): description\` include magic words per ticket provider
1461
+ 3. **PR/MR**:
1462
+ - \`github\`: \`gh pr create --title "fix({ticket-id}): description" --body "Fixes {ticket-id}"\`
1463
+ - \`gitlab\`: \`glab mr create --title "fix({ticket-id}): description" --description "Closes {ticket-id}"\`
1464
+ - \`bitbucket\`: push branch, create PR via web
1465
+ 4. **Ticket**: move to Done after merge
1412
1466
 
1413
1467
  ## Output
1414
1468