murmur8 4.6.0 → 4.7.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/.blueprint/features/feature_refine-feature-skill/FEATURE_SPEC.md +180 -0
- package/.blueprint/features/feature_refine-feature-skill/IMPLEMENTATION_PLAN.md +47 -0
- package/.blueprint/features/feature_refine-feature-skill/handoff-alex.md +19 -0
- package/.blueprint/features/feature_refine-feature-skill/handoff-cass.md +26 -0
- package/.blueprint/features/feature_refine-feature-skill/handoff-nigel.md +30 -0
- package/.blueprint/features/feature_refine-feature-skill/story-codey-confirmation.md +41 -0
- package/.blueprint/features/feature_refine-feature-skill/story-conversation-approval.md +41 -0
- package/.blueprint/features/feature_refine-feature-skill/story-initiation.md +42 -0
- package/.blueprint/features/feature_refine-feature-skill/story-story-propagation.md +42 -0
- package/.blueprint/features/feature_refine-feature-skill/story-telemetry-lineage.md +47 -0
- package/.blueprint/features/feature_refine-feature-skill/story-test-propagation.md +42 -0
- package/README.md +48 -7
- package/bin/cli.js +2 -1
- package/package.json +1 -1
- package/src/commands/refine.js +37 -0
- package/src/index.js +24 -1
- package/src/refine.js +172 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Feature Specification — Refine Feature Skill
|
|
2
|
+
|
|
3
|
+
## 1. Feature Intent
|
|
4
|
+
**Why this feature exists.**
|
|
5
|
+
|
|
6
|
+
- After running `/implement-feature`, the result may not match user intent — tests pass but behaviour is wrong, scope was misunderstood, or new information changes requirements
|
|
7
|
+
- Users need a structured path to refine an existing feature without starting from scratch
|
|
8
|
+
- This supports the system purpose of iterative, AI-assisted feature development
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 2. Scope
|
|
13
|
+
### In Scope
|
|
14
|
+
- New `/refine-feature [slug]` skill that initiates a refinement pipeline
|
|
15
|
+
- Alex reads existing spec, stories, and test output to build context, then converses with the user to identify changes
|
|
16
|
+
- Alex presents a proposed spec diff; user approves before changes are applied
|
|
17
|
+
- Cass updates affected story files and produces `story-changes.md`
|
|
18
|
+
- Nigel updates affected tests and produces `test-changes.md`
|
|
19
|
+
- Mandatory pause before Codey implements — user must confirm
|
|
20
|
+
- Codey implements changes using the same test-first approach
|
|
21
|
+
- Telemetry: `parentRunId` field linking this run to the run being refined; artifact diffs instead of full files
|
|
22
|
+
- SKILL.md written to project root; copied to `.claude/commands/refine-feature.md` on `murmur8 init`
|
|
23
|
+
|
|
24
|
+
### Out of Scope
|
|
25
|
+
- Branching or forking a feature into two separate features
|
|
26
|
+
- Rolling back a feature (separate concern)
|
|
27
|
+
- Bulk-refining multiple features in one invocation
|
|
28
|
+
- Changing the featureId (it must be preserved across refinements)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. Actors Involved
|
|
33
|
+
|
|
34
|
+
**User**
|
|
35
|
+
- Provides the slug of the feature to refine
|
|
36
|
+
- Engages in freeform conversation with Alex about what needs to change (feedback, error logs, test output)
|
|
37
|
+
- Reviews and approves/rejects the proposed spec diff
|
|
38
|
+
- Confirms before Codey implements
|
|
39
|
+
|
|
40
|
+
**Alex**
|
|
41
|
+
- Reads existing FEATURE_SPEC.md, story-*.md, test output, and pipeline history
|
|
42
|
+
- Converses with user to understand what changed or was wrong
|
|
43
|
+
- Identifies minimal set of spec changes needed
|
|
44
|
+
- Presents diff; updates FEATURE_SPEC.md only after approval
|
|
45
|
+
|
|
46
|
+
**Cass**
|
|
47
|
+
- Reads `story-changes.md` produced by Alex
|
|
48
|
+
- Updates only affected story files (not all stories)
|
|
49
|
+
- Produces `story-changes.md` summarising what changed and why
|
|
50
|
+
|
|
51
|
+
**Nigel**
|
|
52
|
+
- Reads `story-changes.md` to understand scope of change
|
|
53
|
+
- Updates only affected test cases
|
|
54
|
+
- Produces `test-changes.md` summarising what changed
|
|
55
|
+
|
|
56
|
+
**Codey**
|
|
57
|
+
- Receives explicit user confirmation before starting
|
|
58
|
+
- Implements changes to pass updated tests
|
|
59
|
+
- Uses the same test-first incremental approach as the main pipeline
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 4. Behaviour Overview
|
|
64
|
+
|
|
65
|
+
### Happy Path
|
|
66
|
+
1. User runs `/refine-feature [slug]`
|
|
67
|
+
2. Alex reads existing artifacts and presents a brief summary of current state
|
|
68
|
+
3. Alex asks: "What needs to change?"
|
|
69
|
+
4. User provides feedback (freeform: description, logs, error output, screenshots)
|
|
70
|
+
5. Alex identifies changes and presents a proposed diff to FEATURE_SPEC.md
|
|
71
|
+
6. User approves → Alex writes updated FEATURE_SPEC.md (featureId preserved)
|
|
72
|
+
7. Cass updates affected stories and writes `story-changes.md`
|
|
73
|
+
8. Nigel updates affected tests and writes `test-changes.md`
|
|
74
|
+
9. Pipeline pauses: user sees summary of changes across spec/stories/tests
|
|
75
|
+
10. User confirms → Codey implements, runs tests, iterates until passing
|
|
76
|
+
11. Pipeline commits (unless `--no-commit`) and records refinement in history
|
|
77
|
+
|
|
78
|
+
### Key Alternatives
|
|
79
|
+
- User rejects Alex's proposed diff → Alex revises and re-presents
|
|
80
|
+
- User aborts at any stage → clean exit, no partial writes
|
|
81
|
+
- No stories exist (technical feature) → Cass stage skipped; Nigel works from spec diff directly
|
|
82
|
+
- Tests already pass after spec change → Codey stage skipped with note
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 5. State & Lifecycle Interactions
|
|
87
|
+
|
|
88
|
+
- State-transitioning: moves an existing feature from a completed/deployed state back into active refinement
|
|
89
|
+
- Reads but does not replace existing artifacts until user approves diff
|
|
90
|
+
- After refinement, artifacts reflect the refined state; originals not preserved (git history provides rollback)
|
|
91
|
+
- Refinement history is a chain: each run's `parentRunId` points to the previous run's `runId`
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 6. Rules & Decision Logic
|
|
96
|
+
|
|
97
|
+
**R1: featureId preservation**
|
|
98
|
+
- The existing `featureId` in FEATURE_SPEC.md YAML frontmatter must never be changed
|
|
99
|
+
- Input: existing spec; Output: updated spec with same featureId
|
|
100
|
+
|
|
101
|
+
**R2: parentRunId linkage**
|
|
102
|
+
- Every refinement run must include `parentRunId` = the `runId` of the run being refined
|
|
103
|
+
- If no prior run exists in history for this slug, `parentRunId` = null (graceful)
|
|
104
|
+
- Input: pipeline history for slug; Output: parentRunId in telemetry payload
|
|
105
|
+
|
|
106
|
+
**R3: Mandatory pause before Codey**
|
|
107
|
+
- Codey must never run without explicit user confirmation
|
|
108
|
+
- Applies regardless of flags (no `--yes` bypass for this gate)
|
|
109
|
+
|
|
110
|
+
**R4: Cass skipped for technical features**
|
|
111
|
+
- If the original feature was classified as technical (no stories exist), Cass is skipped
|
|
112
|
+
- Nigel works from the spec diff directly
|
|
113
|
+
|
|
114
|
+
**R5: Artifact diff in telemetry**
|
|
115
|
+
- Refinement telemetry sends diffs (before/after) rather than full artifact content
|
|
116
|
+
- Keeps payload size proportional to the change, not the total feature size
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 7. Dependencies
|
|
121
|
+
|
|
122
|
+
- `src/telemetry.js` — existing telemetry module; refinements add `parentRunId` field
|
|
123
|
+
- `src/history.js` — existing history module; refinements are recorded with `type: "refinement"`
|
|
124
|
+
- `src/classifier.js` — determines whether Cass stage runs
|
|
125
|
+
- `src/diff-preview.js` — used for pre-commit diff review
|
|
126
|
+
- `src/feedback.js` — quality gates between stages (same thresholds)
|
|
127
|
+
- `.blueprint/agents/AGENT_SPECIFICATION_ALEX.md` — Alex agent spec
|
|
128
|
+
- `.blueprint/agents/AGENT_BA_CASS.md` — Cass agent spec
|
|
129
|
+
- `.blueprint/agents/AGENT_TESTER_NIGEL.md` — Nigel agent spec
|
|
130
|
+
- `.blueprint/agents/AGENT_DEVELOPER_CODEY.md` — Codey agent spec
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 8. Non-Functional Considerations
|
|
135
|
+
|
|
136
|
+
- **Token efficiency**: Alex reads existing artifacts selectively — handoff summaries first, full spec only if needed
|
|
137
|
+
- **Audit**: all refinements recorded in pipeline history; parentRunId chain enables full lineage
|
|
138
|
+
- **Safety**: mandatory pause before Codey prevents accidental overwrites on approval
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 9. Assumptions & Open Questions
|
|
143
|
+
|
|
144
|
+
**Assumptions:**
|
|
145
|
+
- The feature being refined already has a FEATURE_SPEC.md
|
|
146
|
+
- featureId exists in YAML frontmatter (if not, it should be added before refinement proceeds)
|
|
147
|
+
- Git is available for diff display and commit
|
|
148
|
+
|
|
149
|
+
**Open Questions:**
|
|
150
|
+
- Should a `--no-pause` flag be allowed to skip the Codey confirmation? (Current decision: no, always pause)
|
|
151
|
+
- Should `story-changes.md` and `test-changes.md` be committed alongside the updated files, or deleted after? (Current decision: commit them as refinement audit trail)
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 10. Impact on System Specification
|
|
156
|
+
|
|
157
|
+
This feature reinforces the system's core model of iterative, agent-assisted development. It stretches the pipeline slightly by adding a conversation phase (Alex ↔ user) before spec writing, but does not contradict any existing system assumptions.
|
|
158
|
+
|
|
159
|
+
No system spec changes required.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 11. Handover to BA (Cass)
|
|
164
|
+
|
|
165
|
+
Story themes:
|
|
166
|
+
- Initiating a refinement: user provides slug, Alex reads context
|
|
167
|
+
- Conversation and approval: user provides feedback, Alex proposes diff, user approves
|
|
168
|
+
- Story propagation: Cass updates affected stories, produces story-changes.md
|
|
169
|
+
- Test propagation: Nigel updates affected tests, produces test-changes.md
|
|
170
|
+
- Implementation confirmation: mandatory pause, user confirms, Codey implements
|
|
171
|
+
- Telemetry lineage: parentRunId chain, artifact diffs
|
|
172
|
+
|
|
173
|
+
Story boundaries: each stage transition (user→Alex, Alex→Cass, Cass→Nigel, Nigel→pause, pause→Codey) is a natural story boundary.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 12. Change Log (Feature-Level)
|
|
178
|
+
| Date | Change | Reason | Raised By |
|
|
179
|
+
|------|--------|--------|-----------|
|
|
180
|
+
| 2026-05-19 | Initial spec | New feature design | Steve Newman |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Implementation Plan — refine-feature-skill
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
Create `src/refine.js` exporting 10 pure helper functions tested by Nigel's 25-test suite. Add `src/commands/refine.js` CLI handler and wire the command into `bin/cli.js` and `src/index.js`. Write `REFINE_SKILL.md` at project root.
|
|
5
|
+
|
|
6
|
+
## Files to Create/Modify
|
|
7
|
+
|
|
8
|
+
| Path | Action | Purpose |
|
|
9
|
+
|------|--------|---------|
|
|
10
|
+
| `src/refine.js` | Create | All 10 pure helper functions |
|
|
11
|
+
| `src/commands/refine.js` | Create | CLI handler for `murm refine` / `refine-feature` command |
|
|
12
|
+
| `src/index.js` | Modify | Export refine module functions |
|
|
13
|
+
| `bin/cli.js` | Modify | Register `refine-feature` command |
|
|
14
|
+
| `REFINE_SKILL.md` | Create | Skill definition for /refine-feature |
|
|
15
|
+
|
|
16
|
+
## Implementation Steps
|
|
17
|
+
|
|
18
|
+
1. **Create `src/refine.js`** — implement all 10 functions:
|
|
19
|
+
- `parseRefinementArgs(argv)` → `{ slug }` from argv[3] or null
|
|
20
|
+
- `loadRefinementContext(slug, baseDir)` → reads FEATURE_SPEC.md (throws if missing), story-*.md files, .claude/pipeline-history.json; extracts/writes featureId; returns `{ spec, stories, history, featureId, slug }`
|
|
21
|
+
- `applySpecDiff(specPath, newContent, featureId)` → throws on null; writes spec with YAML frontmatter containing featureId
|
|
22
|
+
- `buildRefinementPayload(opts)` → returns plain object with storyChangesPath, testChangesPath, specDiff, commitSkipped
|
|
23
|
+
- `linkParentRun(slug, history)` → finds most recent entry for slug by completedAt; returns `{ parentRunId, type: 'refinement', featureId }`
|
|
24
|
+
- `isTechnicalFeature(stories)` → returns `stories.length === 0`
|
|
25
|
+
- `filterAffectedStories(stories, changedSlugs)` → filters by slug contained in filename
|
|
26
|
+
- `buildStoryChanges(entries)` → returns entries array (passthrough with validation)
|
|
27
|
+
- `buildChangeSummary(opts)` → returns `{ specPath, affectedStories, testChangesPath }`
|
|
28
|
+
- `isPauseBypassable(_flags)` → always returns `false`
|
|
29
|
+
|
|
30
|
+
2. **Run tests** after writing src/refine.js — expect 25/25 to pass
|
|
31
|
+
|
|
32
|
+
3. **Create `src/commands/refine.js`** — CLI handler that calls loadRefinementContext and orchestrates the pipeline flow (stub-level for now; skill handles orchestration)
|
|
33
|
+
|
|
34
|
+
4. **Modify `src/index.js`** — add require + exports for refine module
|
|
35
|
+
|
|
36
|
+
5. **Modify `bin/cli.js`** — register `refine-feature` command routing to `src/commands/refine.js`
|
|
37
|
+
|
|
38
|
+
6. **Create `REFINE_SKILL.md`** — skill definition with full `/refine-feature` pipeline documented
|
|
39
|
+
|
|
40
|
+
7. **Run full test suite** — `node --test` to verify no regressions
|
|
41
|
+
|
|
42
|
+
## Key Implementation Notes
|
|
43
|
+
|
|
44
|
+
- `loadRefinementContext`: use `fs.readdirSync` to find `story-*.md` files; parse YAML frontmatter with simple regex (no deps); if no featureId in frontmatter, generate UUID v4 and write it back (same pattern as telemetry's `ensureFeatureId`)
|
|
45
|
+
- `applySpecDiff`: ensure YAML frontmatter block always starts the file; preserve featureId even if newContent doesn't include it
|
|
46
|
+
- `linkParentRun`: sort history by `completedAt` descending, take first matching slug; if no match, parentRunId = null
|
|
47
|
+
- UUID generation: use `crypto.randomUUID()` (Node 18+, no deps needed)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Handoff Summary
|
|
2
|
+
**For:** Cass
|
|
3
|
+
**Feature:** refine-feature-skill
|
|
4
|
+
|
|
5
|
+
### Key Decisions
|
|
6
|
+
- Skill is `/refine-feature [slug]` — a separate skill from `/implement-feature`, not a flag
|
|
7
|
+
- Alex stage is conversation-based (reads existing artifacts, chats with user, proposes diff) rather than fresh spec creation
|
|
8
|
+
- Hard pause before Codey is mandatory — no `--yes` bypass
|
|
9
|
+
- Telemetry uses `parentRunId` to form a linked chain of refinements; `featureId` is preserved
|
|
10
|
+
- Cass skipped if original feature had no stories (technical classification)
|
|
11
|
+
|
|
12
|
+
### Files Created
|
|
13
|
+
- .blueprint/features/feature_refine-feature-skill/FEATURE_SPEC.md
|
|
14
|
+
|
|
15
|
+
### Open Questions
|
|
16
|
+
- None
|
|
17
|
+
|
|
18
|
+
### Critical Context
|
|
19
|
+
The six story themes map cleanly to pipeline stages: (1) initiation, (2) conversation + approval, (3) story propagation via Cass, (4) test propagation via Nigel, (5) mandatory pause + Codey confirmation, (6) telemetry lineage. Each story should have testable ACs focused on the observable behaviour at that stage boundary.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## Handoff Summary
|
|
2
|
+
**For:** Nigel
|
|
3
|
+
**Feature:** refine-feature-skill
|
|
4
|
+
|
|
5
|
+
### Stories Written (6)
|
|
6
|
+
- story-initiation.md — skill invocation, artifact loading, featureId capture/creation
|
|
7
|
+
- story-conversation-approval.md — freeform feedback, diff proposal loop, approval gate, clean abort
|
|
8
|
+
- story-story-propagation.md — Cass updates affected stories only, produces story-changes.md, skipped for technical features
|
|
9
|
+
- story-test-propagation.md — Nigel updates affected tests only, produces test-changes.md, uses spec diff when Cass skipped
|
|
10
|
+
- story-codey-confirmation.md — mandatory pre-Codey pause, no flag bypass, test-first implementation, conditional commit
|
|
11
|
+
- story-telemetry-lineage.md — parentRunId chain, type:"refinement", artifact diffs in telemetry, change files committed as audit trail
|
|
12
|
+
|
|
13
|
+
### Key Constraints for Nigel
|
|
14
|
+
- Every AC is independently testable; prefer unit/integration tests over end-to-end
|
|
15
|
+
- featureId preservation (AC-6 in initiation, AC-6 in conversation-approval, AC-4 in telemetry) must be verified as a cross-cutting invariant
|
|
16
|
+
- The no-flag-bypass rule (AC-2 in codey-confirmation) is safety-critical — test it explicitly with `--yes` and similar flags
|
|
17
|
+
- Cass-skip path (technical features) is tested in both story-propagation and test-propagation stories
|
|
18
|
+
- parentRunId = null graceful path (AC-2 in telemetry) must be a distinct test case
|
|
19
|
+
|
|
20
|
+
### Files Created
|
|
21
|
+
- story-initiation.md
|
|
22
|
+
- story-conversation-approval.md
|
|
23
|
+
- story-story-propagation.md
|
|
24
|
+
- story-test-propagation.md
|
|
25
|
+
- story-codey-confirmation.md
|
|
26
|
+
- story-telemetry-lineage.md
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## Handoff Summary
|
|
2
|
+
**For:** Codey
|
|
3
|
+
**Feature:** refine-feature-skill
|
|
4
|
+
|
|
5
|
+
### Tests Written
|
|
6
|
+
- File: `test/feature_refine-feature-skill.test.js`
|
|
7
|
+
- Spec: `test/artifacts/feature_refine-feature-skill/test-spec.md`
|
|
8
|
+
- Total tests: 25 across 6 describe blocks (one per story)
|
|
9
|
+
|
|
10
|
+
### Coverage
|
|
11
|
+
| Story | ACs covered | Test IDs |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| story-initiation.md | AC-1,2,3,4,5,6 | RF-IN-1…6 |
|
|
14
|
+
| story-conversation-approval.md | AC-3,5,6 | RF-CA-3,5,6 |
|
|
15
|
+
| story-story-propagation.md | AC-1,3,4,5,6 | RF-SP-1,3,4,5,6 |
|
|
16
|
+
| story-test-propagation.md | AC-1,3,5 | RF-TP-1,3,5 |
|
|
17
|
+
| story-codey-confirmation.md | AC-2,4,6 | RF-CC-2,4,6 |
|
|
18
|
+
| story-telemetry-lineage.md | AC-1,2,3,4,7 | RF-TL-1,2,3,4,7 |
|
|
19
|
+
|
|
20
|
+
### Key Assumptions for Codey
|
|
21
|
+
- `src/refine.js` exports all 10 pure functions listed in test-spec.md
|
|
22
|
+
- `loadRefinementContext(slug, baseDir)` accepts a base directory for testability
|
|
23
|
+
- `applySpecDiff(specPath, newContent, featureId)` — null newContent = abort (throws)
|
|
24
|
+
- `linkParentRun(slug, historyArray)` is pure; no file I/O
|
|
25
|
+
- `isPauseBypassable` always returns `false`; no flags override it
|
|
26
|
+
|
|
27
|
+
### Files to Create
|
|
28
|
+
- `src/refine.js` — all pure helper functions
|
|
29
|
+
- `src/commands/refine.js` — CLI handler wiring
|
|
30
|
+
- `REFINE_SKILL.md` and `.claude/commands/refine-feature.md`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Story: Mandatory Pause and Codey Confirmation
|
|
2
|
+
|
|
3
|
+
**As a** developer reviewing the proposed changes to spec, stories, and tests
|
|
4
|
+
**I want** to see a consolidated summary of all changes before Codey begins implementation, and to give explicit confirmation before any code is touched
|
|
5
|
+
**So that** I retain full control and cannot accidentally trigger implementation via a flag or shortcut
|
|
6
|
+
|
|
7
|
+
## Acceptance Criteria
|
|
8
|
+
|
|
9
|
+
### AC-1: Pipeline pauses before Codey with a change summary
|
|
10
|
+
**Given** Nigel has produced `test-changes.md`
|
|
11
|
+
**When** the pipeline reaches the pre-Codey gate
|
|
12
|
+
**Then** the pipeline displays a summary showing: files changed in spec, stories affected, and tests added/modified, then waits for explicit user input before proceeding
|
|
13
|
+
|
|
14
|
+
### AC-2: No flag or option bypasses the pre-Codey pause
|
|
15
|
+
**Given** the pre-Codey gate is active
|
|
16
|
+
**When** the user invokes `/refine-feature` with any combination of flags (including `--yes`, `--no-pause`, or any other flag)
|
|
17
|
+
**Then** the pipeline still pauses and requires explicit confirmation; no flag silently bypasses this gate
|
|
18
|
+
|
|
19
|
+
### AC-3: User confirmation triggers Codey implementation
|
|
20
|
+
**Given** the pre-Codey change summary is displayed
|
|
21
|
+
**When** the user confirms (e.g., types "yes" or "proceed")
|
|
22
|
+
**Then** Codey begins implementation using the updated tests as its acceptance target
|
|
23
|
+
|
|
24
|
+
### AC-4: User abort at pre-Codey gate exits cleanly
|
|
25
|
+
**Given** the pre-Codey change summary is displayed
|
|
26
|
+
**When** the user aborts (e.g., types "cancel" or "no")
|
|
27
|
+
**Then** the pipeline exits cleanly; all spec, story, and test file writes that have already occurred are preserved; no code changes are made
|
|
28
|
+
|
|
29
|
+
### AC-5: Codey uses test-first incremental approach
|
|
30
|
+
**Given** the user has confirmed at the pre-Codey gate
|
|
31
|
+
**When** Codey implements changes
|
|
32
|
+
**Then** Codey runs the updated tests first, implements code changes incrementally to make them pass, and iterates until all updated tests pass
|
|
33
|
+
|
|
34
|
+
### AC-6: Pipeline commits after successful implementation unless --no-commit
|
|
35
|
+
**Given** Codey has completed implementation and all tests pass
|
|
36
|
+
**When** the pipeline finishes
|
|
37
|
+
**Then** changes are committed to git unless the `--no-commit` flag was supplied, in which case the pipeline exits with a message indicating commit was skipped
|
|
38
|
+
|
|
39
|
+
## Out of Scope
|
|
40
|
+
- Codey making spec or story changes (those are locked before this gate)
|
|
41
|
+
- Any automated or timed confirmation; confirmation must be an explicit user action
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Story: Conversation and Spec Diff Approval
|
|
2
|
+
|
|
3
|
+
**As a** developer providing feedback on a completed feature
|
|
4
|
+
**I want** to describe what is wrong or what has changed to Alex in freeform language, then review a proposed diff before anything is written
|
|
5
|
+
**So that** spec changes are driven by my intent and I cannot accidentally overwrite existing work
|
|
6
|
+
|
|
7
|
+
## Acceptance Criteria
|
|
8
|
+
|
|
9
|
+
### AC-1: Alex accepts freeform feedback input
|
|
10
|
+
**Given** Alex has presented the current-state summary
|
|
11
|
+
**When** the user provides feedback in any form (plain text, error logs, test output, screenshots)
|
|
12
|
+
**Then** Alex accepts the input without requiring a specific format and proceeds to analyse it
|
|
13
|
+
|
|
14
|
+
### AC-2: Alex presents a proposed diff to FEATURE_SPEC.md
|
|
15
|
+
**Given** Alex has analysed the user's feedback
|
|
16
|
+
**When** Alex has identified the minimal set of spec changes required
|
|
17
|
+
**Then** Alex presents the proposed changes as a clearly labelled before/after diff and asks the user to approve or reject
|
|
18
|
+
|
|
19
|
+
### AC-3: User approval triggers spec write
|
|
20
|
+
**Given** Alex has presented the proposed diff
|
|
21
|
+
**When** the user explicitly approves the diff
|
|
22
|
+
**Then** Alex writes the updated `FEATURE_SPEC.md` with the changes applied, preserving the original featureId
|
|
23
|
+
|
|
24
|
+
### AC-4: User rejection triggers revision loop
|
|
25
|
+
**Given** Alex has presented the proposed diff
|
|
26
|
+
**When** the user rejects the diff or requests changes
|
|
27
|
+
**Then** Alex revises the proposed diff based on the user's clarification and presents an updated version; no files are written until approval is given
|
|
28
|
+
|
|
29
|
+
### AC-5: User abort exits cleanly with no writes
|
|
30
|
+
**Given** the conversation is in progress at any point before approval
|
|
31
|
+
**When** the user aborts (e.g., types "cancel" or "abort")
|
|
32
|
+
**Then** the pipeline exits cleanly, no files are modified, and the user receives confirmation that no changes were made
|
|
33
|
+
|
|
34
|
+
### AC-6: featureId is unchanged after spec write
|
|
35
|
+
**Given** the user has approved the proposed diff
|
|
36
|
+
**When** Alex writes the updated `FEATURE_SPEC.md`
|
|
37
|
+
**Then** the featureId in the YAML frontmatter is identical to the value read at initiation
|
|
38
|
+
|
|
39
|
+
## Out of Scope
|
|
40
|
+
- Automated approval via a `--yes` flag for this gate
|
|
41
|
+
- Approving changes to story or test files at this stage (those are handled by Cass and Nigel)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Story: Refinement Initiation
|
|
2
|
+
|
|
3
|
+
**As a** developer who has run `/implement-feature` and found the result does not fully match intent
|
|
4
|
+
**I want** to run `/refine-feature [slug]` and have Alex load all existing artifacts for that feature
|
|
5
|
+
**So that** I can start a targeted refinement without duplicating work already done
|
|
6
|
+
|
|
7
|
+
## Acceptance Criteria
|
|
8
|
+
|
|
9
|
+
### AC-1: Skill exists and accepts a slug argument
|
|
10
|
+
**Given** murmur8 has been initialised in a project
|
|
11
|
+
**When** the user runs `/refine-feature some-feature`
|
|
12
|
+
**Then** the `/refine-feature` skill is recognised as a valid command and begins execution with `some-feature` as the target slug
|
|
13
|
+
|
|
14
|
+
### AC-2: Missing FEATURE_SPEC.md produces a clear error
|
|
15
|
+
**Given** no `FEATURE_SPEC.md` exists for the given slug
|
|
16
|
+
**When** the user runs `/refine-feature missing-slug`
|
|
17
|
+
**Then** the pipeline exits with a message indicating the spec was not found and no files are modified
|
|
18
|
+
|
|
19
|
+
### AC-3: Alex reads existing artifacts before engaging the user
|
|
20
|
+
**Given** a feature with an existing `FEATURE_SPEC.md`, one or more `story-*.md` files, and pipeline history
|
|
21
|
+
**When** the refinement skill starts
|
|
22
|
+
**Then** Alex reads the spec, all story files, and the most recent pipeline history entry for the slug before prompting the user
|
|
23
|
+
|
|
24
|
+
### AC-4: Alex presents a current-state summary before asking for feedback
|
|
25
|
+
**Given** Alex has loaded all existing artifacts
|
|
26
|
+
**When** the context-loading phase completes
|
|
27
|
+
**Then** Alex presents a brief summary (feature name, current scope, last run status) and then asks: "What needs to change?"
|
|
28
|
+
|
|
29
|
+
### AC-5: featureId is read from YAML frontmatter
|
|
30
|
+
**Given** `FEATURE_SPEC.md` contains a `featureId` in its YAML frontmatter
|
|
31
|
+
**When** Alex initialises the refinement session
|
|
32
|
+
**Then** the featureId value is captured and preserved for all subsequent writes in this run
|
|
33
|
+
|
|
34
|
+
### AC-6: Missing featureId is added before refinement proceeds
|
|
35
|
+
**Given** `FEATURE_SPEC.md` exists but has no `featureId` in its YAML frontmatter
|
|
36
|
+
**When** the refinement skill starts
|
|
37
|
+
**Then** Alex adds a new featureId to the frontmatter before proceeding, and logs that it was added
|
|
38
|
+
|
|
39
|
+
## Out of Scope
|
|
40
|
+
- Running refinement on multiple features in a single invocation
|
|
41
|
+
- Forking or branching a feature into two separate features
|
|
42
|
+
- Rolling back a feature to a previous state
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Story: Story Propagation via Cass
|
|
2
|
+
|
|
3
|
+
**As a** developer who has approved a spec diff
|
|
4
|
+
**I want** Cass to update only the affected user story files and produce a `story-changes.md` summary
|
|
5
|
+
**So that** stories stay in sync with the refined spec without unnecessary churn to unchanged stories
|
|
6
|
+
|
|
7
|
+
## Acceptance Criteria
|
|
8
|
+
|
|
9
|
+
### AC-1: Cass is skipped for technical features
|
|
10
|
+
**Given** the original feature was classified as technical (no `story-*.md` files exist for the slug)
|
|
11
|
+
**When** the refinement pipeline reaches the Cass stage
|
|
12
|
+
**Then** the Cass stage is skipped and the pipeline proceeds directly to Nigel; a note is recorded in the run output
|
|
13
|
+
|
|
14
|
+
### AC-2: Cass reads the spec diff not the full spec
|
|
15
|
+
**Given** Alex has written an updated `FEATURE_SPEC.md`
|
|
16
|
+
**When** Cass starts
|
|
17
|
+
**Then** Cass reads `story-changes.md` (produced by Alex) to understand the scope of change before reading the full spec
|
|
18
|
+
|
|
19
|
+
### AC-3: Only affected story files are updated
|
|
20
|
+
**Given** a feature with multiple existing `story-*.md` files
|
|
21
|
+
**When** Cass has identified which stories are affected by the spec diff
|
|
22
|
+
**Then** Cass updates only those story files; stories not affected by the diff are left unchanged
|
|
23
|
+
|
|
24
|
+
### AC-4: Cass produces a `story-changes.md` file
|
|
25
|
+
**Given** Cass has completed updating stories
|
|
26
|
+
**When** the Cass stage finishes
|
|
27
|
+
**Then** a `story-changes.md` file exists in the feature directory listing which stories were changed and a brief reason for each change
|
|
28
|
+
|
|
29
|
+
### AC-5: Cass does not delete existing stories without cause
|
|
30
|
+
**Given** an existing story that is unaffected by the spec diff
|
|
31
|
+
**When** Cass completes
|
|
32
|
+
**Then** that story file is present and unmodified
|
|
33
|
+
|
|
34
|
+
### AC-6: New stories are created if the spec diff adds new scope
|
|
35
|
+
**Given** the approved diff introduces new user-facing behaviour not covered by any existing story
|
|
36
|
+
**When** Cass processes the diff
|
|
37
|
+
**Then** Cass creates a new `story-[slug].md` file for the new scope and lists it in `story-changes.md`
|
|
38
|
+
|
|
39
|
+
## Out of Scope
|
|
40
|
+
- Cass making changes to test files (that is Nigel's responsibility)
|
|
41
|
+
- Cass engaging in conversation with the user about story scope
|
|
42
|
+
- Bulk-updating all stories regardless of diff scope
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Story: Telemetry Lineage via parentRunId
|
|
2
|
+
|
|
3
|
+
**As a** developer or team lead reviewing a feature's evolution over multiple refinements
|
|
4
|
+
**I want** each refinement run to record a `parentRunId` linking it to the run it refines
|
|
5
|
+
**So that** I can trace the full chain of refinements for any feature from pipeline history
|
|
6
|
+
|
|
7
|
+
## Acceptance Criteria
|
|
8
|
+
|
|
9
|
+
### AC-1: Refinement run records parentRunId in history
|
|
10
|
+
**Given** a pipeline history entry exists for the slug being refined
|
|
11
|
+
**When** the refinement run completes
|
|
12
|
+
**Then** the history entry for this refinement run contains a `parentRunId` field set to the `runId` of the most recent prior run for that slug
|
|
13
|
+
|
|
14
|
+
### AC-2: parentRunId is null when no prior history exists
|
|
15
|
+
**Given** no pipeline history entry exists for the slug being refined
|
|
16
|
+
**When** the refinement run records its history entry
|
|
17
|
+
**Then** the `parentRunId` field is present and set to `null`; the run is not aborted or errored because of missing history
|
|
18
|
+
|
|
19
|
+
### AC-3: Refinement run is recorded with type "refinement"
|
|
20
|
+
**Given** a refinement run completes (successfully or with user abort)
|
|
21
|
+
**When** the history entry is written
|
|
22
|
+
**Then** the entry contains `"type": "refinement"` to distinguish it from original `"type": "implementation"` runs
|
|
23
|
+
|
|
24
|
+
### AC-4: featureId is identical to the original run's featureId
|
|
25
|
+
**Given** an original implementation run recorded a featureId for a slug
|
|
26
|
+
**When** a refinement run for the same slug records its history entry
|
|
27
|
+
**Then** the featureId in the refinement history entry is the same value as in the original run
|
|
28
|
+
|
|
29
|
+
### AC-5: Telemetry payload uses artifact diffs, not full files
|
|
30
|
+
**Given** spec, story, and test files were updated during the refinement
|
|
31
|
+
**When** telemetry is emitted
|
|
32
|
+
**Then** the payload contains before/after diffs for each changed artifact rather than the full file contents
|
|
33
|
+
|
|
34
|
+
### AC-6: story-changes.md and test-changes.md are committed as audit trail
|
|
35
|
+
**Given** Cass produced `story-changes.md` and/or Nigel produced `test-changes.md`
|
|
36
|
+
**When** the pipeline commits at the end of a successful refinement
|
|
37
|
+
**Then** both change summary files are included in the commit alongside the updated spec, story, and test files
|
|
38
|
+
|
|
39
|
+
### AC-7: parentRunId chain is traversable across multiple refinements
|
|
40
|
+
**Given** a feature has been refined three times, each run recording a parentRunId pointing to the previous
|
|
41
|
+
**When** a developer queries the history for that slug
|
|
42
|
+
**Then** the history entries can be ordered into a chain: run-1 ← run-2 ← run-3 ← run-4 via parentRunId links
|
|
43
|
+
|
|
44
|
+
## Out of Scope
|
|
45
|
+
- Visualising the refinement chain in the CLI (a future feature)
|
|
46
|
+
- Preserving original artifact files alongside refined versions (git history provides rollback)
|
|
47
|
+
- Changing the featureId at any point in the chain
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Story: Test Propagation via Nigel
|
|
2
|
+
|
|
3
|
+
**As a** developer whose stories have been updated by Cass
|
|
4
|
+
**I want** Nigel to update only the affected test cases and produce a `test-changes.md` summary
|
|
5
|
+
**So that** the test suite reflects the refined spec without replacing tests that are still valid
|
|
6
|
+
|
|
7
|
+
## Acceptance Criteria
|
|
8
|
+
|
|
9
|
+
### AC-1: Nigel reads story-changes.md to scope its work
|
|
10
|
+
**Given** Cass has produced a `story-changes.md` (or Cass was skipped and Alex produced a spec diff)
|
|
11
|
+
**When** Nigel starts
|
|
12
|
+
**Then** Nigel reads `story-changes.md` (or the spec diff directly if Cass was skipped) before reading any test files
|
|
13
|
+
|
|
14
|
+
### AC-2: Only affected test cases are modified
|
|
15
|
+
**Given** an existing test file with multiple test cases
|
|
16
|
+
**When** Nigel has identified which test cases correspond to changed stories or spec sections
|
|
17
|
+
**Then** Nigel modifies only those test cases; test cases unrelated to the diff are left unchanged
|
|
18
|
+
|
|
19
|
+
### AC-3: Nigel produces a `test-changes.md` file
|
|
20
|
+
**Given** Nigel has completed updating tests
|
|
21
|
+
**When** the Nigel stage finishes
|
|
22
|
+
**Then** a `test-changes.md` file exists in the feature directory listing which test cases were added, modified, or removed and why
|
|
23
|
+
|
|
24
|
+
### AC-4: New test cases are created for new acceptance criteria
|
|
25
|
+
**Given** a new acceptance criterion exists in an updated or new story
|
|
26
|
+
**When** Nigel processes the changes
|
|
27
|
+
**Then** Nigel creates a new test case covering that criterion and records it in `test-changes.md`
|
|
28
|
+
|
|
29
|
+
### AC-5: Nigel works from spec diff when no stories exist
|
|
30
|
+
**Given** the original feature was technical and Cass was skipped
|
|
31
|
+
**When** Nigel runs
|
|
32
|
+
**Then** Nigel uses the before/after spec diff produced by Alex as the sole input for determining which tests to update
|
|
33
|
+
|
|
34
|
+
### AC-6: Passing tests are not regressed
|
|
35
|
+
**Given** an existing test that covers behaviour not changed by the diff
|
|
36
|
+
**When** Nigel completes
|
|
37
|
+
**Then** that test case remains present and semantically equivalent to its pre-refinement version
|
|
38
|
+
|
|
39
|
+
## Out of Scope
|
|
40
|
+
- Nigel running the tests (execution happens during the Codey stage)
|
|
41
|
+
- Nigel modifying story files
|
|
42
|
+
- Nigel engaging in conversation with the user about test scope
|
package/README.md
CHANGED
|
@@ -1,19 +1,58 @@
|
|
|
1
1
|
# murmur8
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AI coding tools can be a black box. You describe what you want, magic happens, and code appears. If it's wrong, you describe it again and hope for better. There's no process, no trail, no shared understanding of why decisions were made.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
murmur8 is different. Agents Alex, Cass, Nigel, and Codey run a structured, documented pipeline — the kind a good engineering team would run naturally. Each agent produces real, readable artefacts: a feature spec, user stories, a test plan, an implementation. You can read every one of them, understand the reasoning, and step in at any point. It's not magic. It's a repeatable process that happens to move very fast.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Like a murmuration of starlings, the agents move together — each one responding to what came before and building upon it.
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
Initialize with `npx murmur8 init`, then run `/implement-feature your-feature` in Claude Code or Copilot CLI. Four AI agents collaborate to turn your idea into tested, working code — from spec to implementation. Add up to 3 feature slugs and the murmuration magic will build them in paralell in an isolated git worktree.
|
|
9
|
+
## The Workflow
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
### Start with a conversation
|
|
12
|
+
|
|
13
|
+
Every feature starts with intent. If you're setting up a new project, murmur8 will walk you through creating a system specification interactively — Alex asks questions, you answer, and together you produce a document that grounds everything that follows. If a feature spec doesn't exist yet, the same thing happens at the feature level.
|
|
14
|
+
|
|
15
|
+
You can also trigger this explicitly with `--interactive` flag. This is useful when an idea is still fuzzy, you can have a conversation with Alex until the shape of the feature becomes clear. The spec that comes out the other side is yours to review and approve before anything else runs.
|
|
16
|
+
|
|
17
|
+
### The pipeline runs
|
|
18
|
+
|
|
19
|
+
Once there's a spec, the pipeline takes over. Alex hands off to Cass, who writes user stories with explicit acceptance criteria. Cass hands off to Nigel, who turns those stories into a test plan and executable tests. Nigel hands off to Codey, who implements until the tests pass.
|
|
20
|
+
|
|
21
|
+
At every handoff, the agent writes a summary of what it did, what it decided, and what the next agent needs to know. These aren't logs — they're readable documents. If something goes wrong, or you want to understand why a decision was made, you can read the trail. The spec, the stories, the test plan, the implementation plan: they all live in your repository alongside the code.
|
|
22
|
+
|
|
23
|
+
### Refine it
|
|
24
|
+
|
|
25
|
+
The first run won't always land exactly right. Requirements shift, something was misunderstood, or the implementation reveals a gap in the spec. That's normal.
|
|
26
|
+
|
|
27
|
+
`/refine-feature` reopens the conversation. You can discuss with Alex whats not right, provide error logs, or user feedback. Alex reads what was built, and describes what needs to change, and proposes an updated spec diff for your approval. From there Cass updates only the affected stories, Nigel updates only the affected tests, and Codey reimplements. The pipeline pauses before Codey runs — you always see the full picture of what's changing before any code is touched.
|
|
28
|
+
|
|
29
|
+
Every refinement is linked to the run it came from, so the history of a feature — original intent, what changed, and why — is always traceable.
|
|
30
|
+
|
|
31
|
+
### Turning it up to 11
|
|
32
|
+
|
|
33
|
+
If you using the skill with Claude Code or Github Copilot, or via an npx command, you can run multiple of these pipelines at the same time, working independantly on features in parallel. The pipeline look at whats required and work out how multiple fetaures can be delivered at the same time withough Nigel 1, 2, and 3 treading on each others toes in the code base. The pipeline runs EXACTLY the same way... just mutiple of them at the same time!
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
**Inside Claude Code or Copilot CLI:**
|
|
38
|
+
```bash
|
|
39
|
+
npx murmur8 init
|
|
40
|
+
/implement-feature your-feature
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**From the terminal (parallel execution):**
|
|
44
|
+
```bash
|
|
45
|
+
npx murmur8 init
|
|
46
|
+
npx murmur8 murm feature-a feature-b feature-c
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Add up to three slugs and each feature runs in an isolated git worktree simultaneously. Successful features auto-merge to main. Use `--dry-run` to preview the plan before committing.
|
|
14
50
|
|
|
15
51
|
## Upgrading to v4.0
|
|
16
52
|
|
|
53
|
+
<details>
|
|
54
|
+
<summary>Breaking changes and migration notes</summary>
|
|
55
|
+
|
|
17
56
|
v4.0 completes the murmuration theming by renaming all parallel internals. Existing users should be aware of the following breaking changes.
|
|
18
57
|
|
|
19
58
|
### Breaking changes
|
|
@@ -32,6 +71,8 @@ Legacy on-disk files (`.claude/parallel-config.json`, `.claude/parallel-queue.js
|
|
|
32
71
|
|
|
33
72
|
The CLI commands `parallel`, `murmuration`, and `parallel-config` continue to work as aliases for `murm` and `murm-config` respectively.
|
|
34
73
|
|
|
74
|
+
</details>
|
|
75
|
+
|
|
35
76
|
## Agents
|
|
36
77
|
|
|
37
78
|
| Agent | Role |
|
package/bin/cli.js
CHANGED
|
@@ -10,7 +10,8 @@ const command = args[0];
|
|
|
10
10
|
const aliases = {
|
|
11
11
|
'parallel': 'murm',
|
|
12
12
|
'murmuration': 'murm',
|
|
13
|
-
'parallel-config': 'murm-config'
|
|
13
|
+
'parallel-config': 'murm-config',
|
|
14
|
+
'refine-feature': 'refine'
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
const resolvedCommand = aliases[command] || command;
|
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { loadRefinementContext, isPauseBypassable } = require('../refine');
|
|
4
|
+
|
|
5
|
+
const description = 'Refine an existing feature spec and propagate changes through stories, tests, and implementation';
|
|
6
|
+
|
|
7
|
+
async function run(argv) {
|
|
8
|
+
const { parseRefinementArgs } = require('../refine');
|
|
9
|
+
const { slug } = parseRefinementArgs(argv);
|
|
10
|
+
|
|
11
|
+
if (!slug) {
|
|
12
|
+
console.error('Usage: murmur8 refine-feature <slug>');
|
|
13
|
+
console.error('Example: murmur8 refine-feature user-auth');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
console.log(`Loading refinement context for "${slug}"...`);
|
|
18
|
+
|
|
19
|
+
let ctx;
|
|
20
|
+
try {
|
|
21
|
+
ctx = await loadRefinementContext(slug, process.cwd());
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`Error: ${err.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const storyCount = ctx.stories.length;
|
|
28
|
+
const lastStatus = ctx.lastRunStatus ? ` (last run: ${ctx.lastRunStatus})` : '';
|
|
29
|
+
console.log(`Feature: ${ctx.slug}${lastStatus}`);
|
|
30
|
+
console.log(`Stories: ${storyCount}`);
|
|
31
|
+
console.log(`Feature ID: ${ctx.featureId}`);
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log('To refine this feature, use the /refine-feature skill in Claude Code:');
|
|
34
|
+
console.log(` /refine-feature "${slug}"`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { run, description };
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
const { init } = require('./init');
|
|
2
|
+
const {
|
|
3
|
+
parseRefinementArgs,
|
|
4
|
+
loadRefinementContext,
|
|
5
|
+
applySpecDiff,
|
|
6
|
+
buildRefinementPayload,
|
|
7
|
+
linkParentRun,
|
|
8
|
+
isTechnicalFeature,
|
|
9
|
+
filterAffectedStories,
|
|
10
|
+
buildStoryChanges,
|
|
11
|
+
buildChangeSummary,
|
|
12
|
+
isPauseBypassable,
|
|
13
|
+
} = require('./refine');
|
|
2
14
|
const { update } = require('./update');
|
|
3
15
|
const { validate, formatOutput, checkNodeVersion } = require('./validate');
|
|
4
16
|
const { recordHistory, displayHistory, showStats, clearHistory, storeStageFeedback, updateStage } = require('./history');
|
|
@@ -209,5 +221,16 @@ module.exports = {
|
|
|
209
221
|
SESSION_STATES,
|
|
210
222
|
SECTION_ORDER,
|
|
211
223
|
MIN_REQUIRED_SECTIONS,
|
|
212
|
-
SYSTEM_SPEC_QUESTIONS
|
|
224
|
+
SYSTEM_SPEC_QUESTIONS,
|
|
225
|
+
// Refine module exports
|
|
226
|
+
parseRefinementArgs,
|
|
227
|
+
loadRefinementContext,
|
|
228
|
+
applySpecDiff,
|
|
229
|
+
buildRefinementPayload,
|
|
230
|
+
linkParentRun,
|
|
231
|
+
isTechnicalFeature,
|
|
232
|
+
filterAffectedStories,
|
|
233
|
+
buildStoryChanges,
|
|
234
|
+
buildChangeSummary,
|
|
235
|
+
isPauseBypassable,
|
|
213
236
|
};
|
package/src/refine.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
function parseRefinementArgs(argv) {
|
|
8
|
+
const slug = argv[3] || null;
|
|
9
|
+
return { slug };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function _extractFeatureId(content) {
|
|
13
|
+
const match = content.match(/^---[\s\S]*?featureId:\s*([^\s\n]+)[\s\S]*?---/m);
|
|
14
|
+
return match ? match[1] : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function _writeFeatureId(specPath, content, featureId) {
|
|
18
|
+
let updated;
|
|
19
|
+
if (content.startsWith('---')) {
|
|
20
|
+
const endIdx = content.indexOf('---', 3);
|
|
21
|
+
if (endIdx !== -1) {
|
|
22
|
+
const frontmatter = content.slice(3, endIdx);
|
|
23
|
+
if (frontmatter.includes('featureId:')) {
|
|
24
|
+
updated = content.replace(/featureId:\s*[^\s\n]+/, `featureId: ${featureId}`);
|
|
25
|
+
} else {
|
|
26
|
+
updated = `---${frontmatter}featureId: ${featureId}\n${content.slice(endIdx)}`;
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
updated = `---\nfeatureId: ${featureId}\n---\n${content}`;
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
updated = `---\nfeatureId: ${featureId}\n---\n${content}`;
|
|
33
|
+
}
|
|
34
|
+
fs.writeFileSync(specPath, updated, 'utf8');
|
|
35
|
+
return updated;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function loadRefinementContext(slug, baseDir) {
|
|
39
|
+
const base = baseDir || process.cwd();
|
|
40
|
+
const featDir = path.join(base, '.blueprint', 'features', `feature_${slug}`);
|
|
41
|
+
const specPath = path.join(featDir, 'FEATURE_SPEC.md');
|
|
42
|
+
|
|
43
|
+
if (!fs.existsSync(specPath)) {
|
|
44
|
+
throw new Error(`FEATURE_SPEC.md not found for slug "${slug}" — run /implement-feature first`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let specContent = fs.readFileSync(specPath, 'utf8');
|
|
48
|
+
let featureId = _extractFeatureId(specContent);
|
|
49
|
+
if (!featureId) {
|
|
50
|
+
featureId = crypto.randomUUID();
|
|
51
|
+
specContent = _writeFeatureId(specPath, specContent, featureId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const storyFiles = fs.existsSync(featDir)
|
|
55
|
+
? fs.readdirSync(featDir).filter(f => f.startsWith('story-') && f.endsWith('.md'))
|
|
56
|
+
: [];
|
|
57
|
+
|
|
58
|
+
const stories = storyFiles.map(f => ({
|
|
59
|
+
file: f,
|
|
60
|
+
content: fs.readFileSync(path.join(featDir, f), 'utf8'),
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
const historyPath = path.join(base, '.claude', 'pipeline-history.json');
|
|
64
|
+
let history = [];
|
|
65
|
+
if (fs.existsSync(historyPath)) {
|
|
66
|
+
try {
|
|
67
|
+
history = JSON.parse(fs.readFileSync(historyPath, 'utf8'));
|
|
68
|
+
} catch (_) {
|
|
69
|
+
history = [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const lastRun = history.filter(e => e.slug === slug).sort((a, b) =>
|
|
74
|
+
(b.completedAt || '').localeCompare(a.completedAt || '')
|
|
75
|
+
)[0];
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
slug,
|
|
79
|
+
featureId,
|
|
80
|
+
spec: specContent,
|
|
81
|
+
stories,
|
|
82
|
+
history: history.filter(e => e.slug === slug),
|
|
83
|
+
lastRunStatus: lastRun ? lastRun.status : null,
|
|
84
|
+
featureName: slug,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function applySpecDiff(specPath, newContent, featureId) {
|
|
89
|
+
if (newContent === null || newContent === undefined) {
|
|
90
|
+
throw new Error('abort: null diff provided — no changes applied');
|
|
91
|
+
}
|
|
92
|
+
_writeFeatureId(specPath, newContent, featureId);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildRefinementPayload(opts) {
|
|
96
|
+
const {
|
|
97
|
+
slug,
|
|
98
|
+
featureId,
|
|
99
|
+
storyChangesPath = null,
|
|
100
|
+
testChangesPath = null,
|
|
101
|
+
specDiff = null,
|
|
102
|
+
noCommit = false,
|
|
103
|
+
} = opts || {};
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
slug,
|
|
107
|
+
featureId,
|
|
108
|
+
storyChangesPath: storyChangesPath || null,
|
|
109
|
+
testChangesPath: testChangesPath || null,
|
|
110
|
+
specDiff: specDiff || null,
|
|
111
|
+
commitSkipped: Boolean(noCommit),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function linkParentRun(slug, history) {
|
|
116
|
+
const slugRuns = (history || [])
|
|
117
|
+
.filter(e => e.slug === slug)
|
|
118
|
+
.sort((a, b) => (b.completedAt || '').localeCompare(a.completedAt || ''));
|
|
119
|
+
|
|
120
|
+
const latest = slugRuns[0] || null;
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
parentRunId: latest ? latest.runId : null,
|
|
124
|
+
type: 'refinement',
|
|
125
|
+
featureId: latest ? latest.featureId : null,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isTechnicalFeature(stories) {
|
|
130
|
+
return stories.length === 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function filterAffectedStories(stories, changedSlugs) {
|
|
134
|
+
return stories.filter(f => {
|
|
135
|
+
const name = path.basename(f, '.md').replace(/^story-/, '');
|
|
136
|
+
return changedSlugs.some(s => name === s || f.includes(s));
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildStoryChanges(entries) {
|
|
141
|
+
return (entries || []).map(e => ({
|
|
142
|
+
file: e.file,
|
|
143
|
+
reason: e.reason,
|
|
144
|
+
isNew: Boolean(e.isNew),
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildChangeSummary(opts) {
|
|
149
|
+
const { specPath, affectedStories, testChangesPath } = opts || {};
|
|
150
|
+
return {
|
|
151
|
+
specPath: specPath || null,
|
|
152
|
+
affectedStories: Array.isArray(affectedStories) ? affectedStories : [],
|
|
153
|
+
testChangesPath: testChangesPath || null,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isPauseBypassable(_flags) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = {
|
|
162
|
+
parseRefinementArgs,
|
|
163
|
+
loadRefinementContext,
|
|
164
|
+
applySpecDiff,
|
|
165
|
+
buildRefinementPayload,
|
|
166
|
+
linkParentRun,
|
|
167
|
+
isTechnicalFeature,
|
|
168
|
+
filterAffectedStories,
|
|
169
|
+
buildStoryChanges,
|
|
170
|
+
buildChangeSummary,
|
|
171
|
+
isPauseBypassable,
|
|
172
|
+
};
|