murmur8 4.3.0 → 4.3.2
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_cost-tracking/FEATURE_SPEC.md +216 -0
- package/.blueprint/features/feature_cost-tracking/IMPLEMENTATION_PLAN.md +50 -0
- package/.blueprint/features/feature_diff-preview/FEATURE_SPEC.md +182 -0
- package/.blueprint/features/feature_diff-preview/IMPLEMENTATION_PLAN.md +42 -0
- package/README.md +60 -2
- package/SKILL.md +82 -7
- package/package.json +1 -1
- package/src/commands/cost-config.js +28 -0
- package/src/commands/help.js +5 -0
- package/src/commands/history.js +2 -2
- package/src/commands/utils.js +1 -0
- package/src/cost.js +122 -0
- package/src/diff-preview.js +165 -0
- package/src/history.js +51 -3
- package/src/index.js +25 -0
- package/src/theme.js +2 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Feature Specification — Cost Tracking
|
|
2
|
+
|
|
3
|
+
## 1. Feature Intent
|
|
4
|
+
**Why this feature exists.**
|
|
5
|
+
|
|
6
|
+
- Users have no visibility into token consumption or estimated costs when running the pipeline
|
|
7
|
+
- Without cost data, users cannot identify expensive stages or optimize prompt efficiency
|
|
8
|
+
- Cost awareness enables informed decisions about when to use `--skip-stories` or other optimizations
|
|
9
|
+
- Aligns with existing observability patterns (history, insights, feedback) in the system spec
|
|
10
|
+
|
|
11
|
+
> This feature reinforces Section 8 (Cross-Cutting Concerns: Observability) of the System Spec.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Scope
|
|
16
|
+
### In Scope
|
|
17
|
+
- Track input/output tokens per agent stage (alex, cass, nigel, codey-plan, codey-implement)
|
|
18
|
+
- Estimate cost per stage using configurable pricing (default: Claude Sonnet pricing)
|
|
19
|
+
- Store token/cost data in pipeline history alongside existing timing data
|
|
20
|
+
- Display cost summary at pipeline completion
|
|
21
|
+
- Add `--cost` flag to `history` command to show cost breakdowns
|
|
22
|
+
- Add `cost-config` CLI command for pricing configuration
|
|
23
|
+
|
|
24
|
+
### Out of Scope
|
|
25
|
+
- Real-time token streaming (report after each stage completes)
|
|
26
|
+
- Multi-model pricing within a single run (assumes one model for entire pipeline)
|
|
27
|
+
- Billing integration or payment tracking
|
|
28
|
+
- Token optimization recommendations (covered by insights.js)
|
|
29
|
+
- Cost alerts or budget limits (potential future feature)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Actors Involved
|
|
34
|
+
|
|
35
|
+
| Actor | Role in Cost Tracking |
|
|
36
|
+
|-------|----------------------|
|
|
37
|
+
| Human User | Views cost summaries, configures pricing, uses data for optimization decisions |
|
|
38
|
+
| Pipeline Orchestrator | Records token usage at each stage transition |
|
|
39
|
+
| History Module | Persists and retrieves cost data |
|
|
40
|
+
| Each Agent (Alex, Cass, Nigel, Codey) | Token consumption is measured but agents are unaware of tracking |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 4. Behaviour Overview
|
|
45
|
+
|
|
46
|
+
**Happy Path:**
|
|
47
|
+
1. User runs `/implement-feature "slug"`
|
|
48
|
+
2. At each stage completion, orchestrator records tokens used (input + output)
|
|
49
|
+
3. Pipeline completion displays cost summary table
|
|
50
|
+
4. History entry includes token/cost data per stage
|
|
51
|
+
|
|
52
|
+
**Cost Summary Display (at pipeline end):**
|
|
53
|
+
```
|
|
54
|
+
Cost Summary for feature: user-auth
|
|
55
|
+
|
|
56
|
+
STAGE INPUT OUTPUT COST
|
|
57
|
+
alex 2,450 1,230 $0.014
|
|
58
|
+
cass 3,100 1,850 $0.019
|
|
59
|
+
nigel 2,800 2,100 $0.018
|
|
60
|
+
codey-plan 1,500 890 $0.009
|
|
61
|
+
codey-impl 4,200 3,500 $0.028
|
|
62
|
+
─────────────────────────────────────────
|
|
63
|
+
TOTAL 14,050 9,570 $0.088
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**History with Costs:**
|
|
67
|
+
```bash
|
|
68
|
+
$ murmur8 history --cost
|
|
69
|
+
|
|
70
|
+
SLUG STATUS DURATION TOTAL COST
|
|
71
|
+
user-auth success 12m 30s $0.088
|
|
72
|
+
api-validation success 8m 15s $0.062
|
|
73
|
+
cache-layer failed 4m 22s $0.031 (failed at: nigel)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Stats with Costs:**
|
|
77
|
+
```bash
|
|
78
|
+
$ murmur8 history --stats --cost
|
|
79
|
+
|
|
80
|
+
Pipeline Statistics (based on 15 runs)
|
|
81
|
+
|
|
82
|
+
METRIC VALUE
|
|
83
|
+
...existing stats...
|
|
84
|
+
Avg cost per run $0.075
|
|
85
|
+
Total cost (all runs) $1.12
|
|
86
|
+
Most expensive stage codey-implement ($0.42 total)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 5. State & Lifecycle Interactions
|
|
92
|
+
|
|
93
|
+
**State additions to history entry:**
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"slug": "user-auth",
|
|
97
|
+
"stages": {
|
|
98
|
+
"alex": {
|
|
99
|
+
"durationMs": 45000,
|
|
100
|
+
"tokens": { "input": 2450, "output": 1230 },
|
|
101
|
+
"cost": 0.014
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"totalTokens": { "input": 14050, "output": 9570 },
|
|
105
|
+
"totalCost": 0.088
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Lifecycle:**
|
|
110
|
+
- **State-extending:** Adds new fields to existing history entries
|
|
111
|
+
- No new states created
|
|
112
|
+
- Backward compatible (missing token data displays "N/A")
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 6. Rules & Decision Logic
|
|
117
|
+
|
|
118
|
+
| Rule | Description | Deterministic |
|
|
119
|
+
|------|-------------|---------------|
|
|
120
|
+
| Token counting | Sum of input + output tokens per stage | Yes |
|
|
121
|
+
| Cost calculation | `(input_tokens * input_price) + (output_tokens * output_price)` | Yes |
|
|
122
|
+
| Default pricing | Claude Sonnet 3.5: $3/M input, $15/M output | Yes (configurable) |
|
|
123
|
+
| Skipped stages | Record 0 tokens for skipped stages (e.g., Cass when `--skip-stories`) | Yes |
|
|
124
|
+
| Failed stages | Record tokens consumed up to failure point | Yes |
|
|
125
|
+
| Rounding | Costs rounded to 3 decimal places ($0.001 precision) | Yes |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 7. Dependencies
|
|
130
|
+
|
|
131
|
+
**System Dependencies:**
|
|
132
|
+
- `src/history.js` - Extended to store/retrieve token data
|
|
133
|
+
- `.claude/pipeline-history.json` - Schema extended with token fields
|
|
134
|
+
|
|
135
|
+
**New Files:**
|
|
136
|
+
- `src/cost.js` - Token counting, cost calculation, formatting
|
|
137
|
+
- `src/commands/cost-config.js` - CLI command for pricing config
|
|
138
|
+
- `.claude/cost-config.json` - Pricing configuration (gitignored)
|
|
139
|
+
|
|
140
|
+
**Technical Dependencies:**
|
|
141
|
+
- Claude Code must expose token usage in Task tool responses
|
|
142
|
+
- If token data unavailable, feature gracefully degrades to "N/A"
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 8. Non-Functional Considerations
|
|
147
|
+
|
|
148
|
+
| Concern | Consideration |
|
|
149
|
+
|---------|---------------|
|
|
150
|
+
| Performance | Token counting adds negligible overhead (<1ms per stage) |
|
|
151
|
+
| Storage | ~100 bytes additional per history entry |
|
|
152
|
+
| Backward compatibility | Existing history entries without token data display "N/A" |
|
|
153
|
+
| Accuracy | Estimated costs only; actual billing may differ |
|
|
154
|
+
| Privacy | No sensitive data; token counts are numeric |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 9. Assumptions & Open Questions
|
|
159
|
+
|
|
160
|
+
**Assumptions:**
|
|
161
|
+
- Claude Code Task tool responses include token usage metadata
|
|
162
|
+
- Single pricing model per pipeline run is acceptable
|
|
163
|
+
- Users want cost visibility but not enforcement (no budget limits)
|
|
164
|
+
|
|
165
|
+
**Open Questions:**
|
|
166
|
+
- How does Claude Code expose token usage? Via response metadata or separate API?
|
|
167
|
+
- Should we support custom pricing for enterprise/volume discounts?
|
|
168
|
+
- Should cost data be included in CSV/JSON exports by default?
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 10. Impact on System Specification
|
|
173
|
+
|
|
174
|
+
**Reinforces existing patterns:**
|
|
175
|
+
- Section 8 (Observability) - Adds cost as new observability dimension
|
|
176
|
+
- Section 9 (Reliability) - Token data helps diagnose token limit failures
|
|
177
|
+
|
|
178
|
+
**No contradictions identified.**
|
|
179
|
+
|
|
180
|
+
**Suggested System Spec addition (Section 8):**
|
|
181
|
+
```markdown
|
|
182
|
+
### Cost Visibility
|
|
183
|
+
- Token usage tracked per stage
|
|
184
|
+
- Estimated costs calculated and persisted
|
|
185
|
+
- Cost summaries available via `history --cost`
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 11. Handover to BA (Cass)
|
|
191
|
+
|
|
192
|
+
**Story themes:**
|
|
193
|
+
1. **Core tracking** - Record token usage at each stage completion
|
|
194
|
+
2. **Cost calculation** - Apply pricing model to token counts
|
|
195
|
+
3. **Display** - Show cost summary at pipeline end
|
|
196
|
+
4. **History integration** - Store in history, display in `history` command
|
|
197
|
+
5. **Configuration** - `cost-config` command for pricing settings
|
|
198
|
+
|
|
199
|
+
**Expected story boundaries:**
|
|
200
|
+
- Story 1: Token tracking infrastructure (cost.js, history schema)
|
|
201
|
+
- Story 2: Cost calculation and formatting
|
|
202
|
+
- Story 3: Pipeline completion summary display
|
|
203
|
+
- Story 4: History command integration (`--cost` flag)
|
|
204
|
+
- Story 5: Configuration CLI (`cost-config` command)
|
|
205
|
+
|
|
206
|
+
**Areas needing careful framing:**
|
|
207
|
+
- Graceful degradation when token data unavailable
|
|
208
|
+
- Backward compatibility with existing history entries
|
|
209
|
+
- Cost display formatting (currency, precision)
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 12. Change Log (Feature-Level)
|
|
214
|
+
| Date | Change | Reason | Raised By |
|
|
215
|
+
|-----|------|--------|-----------|
|
|
216
|
+
| 2026-03-04 | Initial spec | Enable cost visibility for pipeline optimization | Alex |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Implementation Plan — Cost Tracking
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Implement a cost tracking module (`src/cost.js`) that calculates token costs using configurable pricing, integrate it with the existing history module to persist cost data per stage, and add CLI commands for displaying costs (`history --cost`) and managing pricing configuration (`cost-config`).
|
|
6
|
+
|
|
7
|
+
## Files to Create/Modify
|
|
8
|
+
|
|
9
|
+
| Path | Action | Purpose |
|
|
10
|
+
|------|--------|---------|
|
|
11
|
+
| `src/cost.js` | Create | Core module: cost calculation, formatting, pricing config |
|
|
12
|
+
| `src/commands/cost-config.js` | Create | CLI command for managing pricing configuration |
|
|
13
|
+
| `src/history.js` | Modify | Add `--cost` display support, cost data in formatters |
|
|
14
|
+
| `src/commands/history.js` | Modify | Parse `--cost` flag and pass to display functions |
|
|
15
|
+
| `src/commands/help.js` | Modify | Add `cost-config` command to help output |
|
|
16
|
+
| `.gitignore` | Modify | Add `.claude/cost-config.json` entry |
|
|
17
|
+
|
|
18
|
+
## Implementation Steps
|
|
19
|
+
|
|
20
|
+
1. **Create `src/cost.js`** — Implement core functions using `config-factory.js` pattern:
|
|
21
|
+
- `getDefaultPricing()` — Returns `{ inputPricePerMillion: 3, outputPricePerMillion: 15 }`
|
|
22
|
+
- `loadPricingConfig()` / `savePricingConfig()` — Read/write `.claude/cost-config.json`
|
|
23
|
+
- `calculateCost(inputTokens, outputTokens, pricing)` — Formula per FEATURE_SPEC.md Section 6
|
|
24
|
+
- `formatCost(cost)` — Returns `$X.XXX` or `N/A` for null/undefined
|
|
25
|
+
- `formatTokens(tokens)` — Returns number with thousands separator or `N/A`
|
|
26
|
+
- `getCostSummary(stages)` — Generates formatted cost summary table
|
|
27
|
+
|
|
28
|
+
2. **Create `src/commands/cost-config.js`** — CLI command following `retry-config.js` pattern:
|
|
29
|
+
- `cost-config` — Display current pricing
|
|
30
|
+
- `cost-config set <key> <value>` — Update pricing
|
|
31
|
+
- `cost-config reset` — Reset to defaults
|
|
32
|
+
|
|
33
|
+
3. **Modify `src/history.js`** — Extend `displayHistory()` and `showStats()`:
|
|
34
|
+
- Add `cost` option parameter to `displayHistory()`
|
|
35
|
+
- Display `TOTAL COST` column when `--cost` flag present
|
|
36
|
+
- Add cost statistics to `showStats()` when cost data available
|
|
37
|
+
- Handle legacy entries gracefully (display N/A for missing token data)
|
|
38
|
+
|
|
39
|
+
4. **Modify `src/commands/history.js`** — Parse `--cost` flag:
|
|
40
|
+
- Pass `{ cost: flags.cost }` to `displayHistory()` and `showStats()`
|
|
41
|
+
|
|
42
|
+
5. **Modify `src/commands/help.js`** — Add `cost-config` to command list
|
|
43
|
+
|
|
44
|
+
6. **Update `.gitignore`** — Add `.claude/cost-config.json` entry
|
|
45
|
+
|
|
46
|
+
## Risks/Questions
|
|
47
|
+
|
|
48
|
+
- **Token data availability**: Claude Code must expose token usage in Task tool responses. If unavailable, gracefully degrade to N/A.
|
|
49
|
+
- **History schema migration**: No migration needed — new fields are additive and missing data displays as N/A.
|
|
50
|
+
- **Pricing accuracy**: Costs are estimates only; document this in CLI output.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Feature Specification — Diff Preview
|
|
2
|
+
|
|
3
|
+
## 1. Feature Intent
|
|
4
|
+
|
|
5
|
+
Provide transparency into pipeline-generated changes before committing them.
|
|
6
|
+
|
|
7
|
+
**Problem:** The auto-commit stage (Step 11) commits all changes without showing users what will be committed. Users may want to review file additions, modifications, and deletions before they become part of the git history.
|
|
8
|
+
|
|
9
|
+
**Solution:** Before auto-commit, display a summary of all pending changes (staged and unstaged) so users can review and approve or abort.
|
|
10
|
+
|
|
11
|
+
**System alignment:** Supports the system spec's "Observability" cross-cutting concern and empowers the Human User actor who has "final arbiter" authority over scope and quality.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Scope
|
|
16
|
+
|
|
17
|
+
### In Scope
|
|
18
|
+
- Show diff summary before auto-commit (single-feature pipeline)
|
|
19
|
+
- Show diff summary before worktree commit (murmuration mode, Step M5.5)
|
|
20
|
+
- Display file counts: additions, modifications, deletions
|
|
21
|
+
- Display file paths for each category
|
|
22
|
+
- Display actual diff content (optionally truncated for large changes)
|
|
23
|
+
- Prompt user to confirm commit, abort, or review full diff
|
|
24
|
+
- `--no-diff-preview` flag to skip (for automation)
|
|
25
|
+
- Integration with `--no-commit` flag (skip preview when commit is skipped)
|
|
26
|
+
|
|
27
|
+
### Out of Scope
|
|
28
|
+
- Interactive editing of changes before commit
|
|
29
|
+
- Selective staging/unstaging from the preview
|
|
30
|
+
- Diff viewing in external tools (editor integration)
|
|
31
|
+
- Syntax highlighting in diff output
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 3. Actors Involved
|
|
36
|
+
|
|
37
|
+
### Human User
|
|
38
|
+
- **Can:** View diff summary, approve commit, abort commit, request full diff
|
|
39
|
+
- **Cannot:** Edit changes from the preview screen
|
|
40
|
+
|
|
41
|
+
### Pipeline Orchestrator
|
|
42
|
+
- **Can:** Trigger diff preview, pass result to commit stage
|
|
43
|
+
- **Cannot:** Auto-approve on user's behalf (unless `--no-diff-preview`)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 4. Behaviour Overview
|
|
48
|
+
|
|
49
|
+
### Single-Feature Pipeline (Step 11)
|
|
50
|
+
|
|
51
|
+
Before committing, the pipeline:
|
|
52
|
+
|
|
53
|
+
1. Runs `git status --porcelain` to detect changes
|
|
54
|
+
2. Categorizes changes: Added (A/??), Modified (M), Deleted (D)
|
|
55
|
+
3. Displays summary:
|
|
56
|
+
```
|
|
57
|
+
Changes to commit for feature_user-auth:
|
|
58
|
+
|
|
59
|
+
Added (3 files):
|
|
60
|
+
+ .blueprint/features/feature_user-auth/FEATURE_SPEC.md
|
|
61
|
+
+ test/feature_user-auth.test.js
|
|
62
|
+
+ src/auth.js
|
|
63
|
+
|
|
64
|
+
Modified (1 file):
|
|
65
|
+
~ src/index.js
|
|
66
|
+
|
|
67
|
+
Deleted (0 files):
|
|
68
|
+
(none)
|
|
69
|
+
|
|
70
|
+
Total: 4 files changed
|
|
71
|
+
|
|
72
|
+
[c]ommit / [a]bort / [d]iff (show full diff)?
|
|
73
|
+
```
|
|
74
|
+
4. On 'c': proceed to commit
|
|
75
|
+
5. On 'a': abort pipeline, exit cleanly
|
|
76
|
+
6. On 'd': show full `git diff --staged` output, then re-prompt
|
|
77
|
+
|
|
78
|
+
### Murmuration Mode (Step M5.5)
|
|
79
|
+
|
|
80
|
+
For each successful worktree, before committing:
|
|
81
|
+
|
|
82
|
+
1. Change to worktree directory
|
|
83
|
+
2. Run same diff preview flow
|
|
84
|
+
3. If user aborts one worktree, mark it as "user-aborted" (not "failed")
|
|
85
|
+
4. Continue to next worktree
|
|
86
|
+
|
|
87
|
+
### Skip Conditions
|
|
88
|
+
|
|
89
|
+
Diff preview is skipped when:
|
|
90
|
+
- `--no-commit` flag is set (no commit will happen)
|
|
91
|
+
- `--no-diff-preview` flag is set (user opted out)
|
|
92
|
+
- `--yes` flag is set (non-interactive mode)
|
|
93
|
+
- No changes detected (`git status` returns empty)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 5. State & Lifecycle Interactions
|
|
98
|
+
|
|
99
|
+
**States touched:**
|
|
100
|
+
- Feature enters a new transient state: `awaiting-commit-review`
|
|
101
|
+
- Exits to: `committing` (on approve) or `aborted` (on abort)
|
|
102
|
+
|
|
103
|
+
**This feature is:** state-constraining (adds a gate before commit)
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 6. Rules & Decision Logic
|
|
108
|
+
|
|
109
|
+
| Rule | Description | Deterministic |
|
|
110
|
+
|------|-------------|---------------|
|
|
111
|
+
| Empty diff | Skip preview if no changes detected | Yes |
|
|
112
|
+
| Truncation | Show first 20 files per category; "... and N more" for overflow | Yes |
|
|
113
|
+
| Large diff | For full diff view, paginate if >100 lines | Yes |
|
|
114
|
+
| Abort handling | Exit code 0 (user choice, not error) | Yes |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 7. Dependencies
|
|
119
|
+
|
|
120
|
+
- Git CLI (`git status`, `git diff`)
|
|
121
|
+
- readline module (for interactive prompt)
|
|
122
|
+
- Existing orchestrator.js or SKILL.md commit flow
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 8. Non-Functional Considerations
|
|
127
|
+
|
|
128
|
+
- **Performance:** `git status` and `git diff` are fast; no significant overhead
|
|
129
|
+
- **Error tolerance:** If git commands fail, show error and proceed with prompt
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 9. Assumptions & Open Questions
|
|
134
|
+
|
|
135
|
+
### Assumptions
|
|
136
|
+
- Git working tree is accessible at commit time
|
|
137
|
+
- Terminal supports interactive prompts (or flags are used for CI)
|
|
138
|
+
|
|
139
|
+
### Open Questions
|
|
140
|
+
- Should diff preview show binary files differently? (Assume: just note "[binary]")
|
|
141
|
+
- Should large diffs auto-truncate in full view? (Assume: yes, with option to show all)
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 10. Impact on System Specification
|
|
146
|
+
|
|
147
|
+
**Reinforces:**
|
|
148
|
+
- Observability (users see what's happening)
|
|
149
|
+
- Human User authority (explicit confirmation)
|
|
150
|
+
|
|
151
|
+
**Stretches:**
|
|
152
|
+
- Pipeline lifecycle adds a transient state (`awaiting-commit-review`)
|
|
153
|
+
- May need to document this in system spec's lifecycle section
|
|
154
|
+
|
|
155
|
+
**No contradictions identified.**
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 11. Handover to BA (Cass)
|
|
160
|
+
|
|
161
|
+
### Story Themes
|
|
162
|
+
1. **Preview display:** Show diff summary before commit
|
|
163
|
+
2. **User choice:** Confirm, abort, or view full diff
|
|
164
|
+
3. **Skip conditions:** Honor flags to bypass preview
|
|
165
|
+
4. **Murmuration integration:** Per-worktree preview in parallel mode
|
|
166
|
+
|
|
167
|
+
### Expected Story Boundaries
|
|
168
|
+
- Keep preview display and user interaction in one story
|
|
169
|
+
- Separate story for murmuration integration if complex
|
|
170
|
+
- Flags/skip conditions can be part of the main preview story
|
|
171
|
+
|
|
172
|
+
### Areas Needing Careful Framing
|
|
173
|
+
- Abort behavior should clearly distinguish user-abort from pipeline failure
|
|
174
|
+
- Full diff view interaction: single prompt or back-and-forth?
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 12. Change Log
|
|
179
|
+
|
|
180
|
+
| Date | Change | Reason |
|
|
181
|
+
|------|--------|--------|
|
|
182
|
+
| 2026-03-04 | Initial spec | Add transparency before auto-commit |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Implementation Plan: Diff Preview
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Create a new `src/diff-preview.js` module that provides transparency before auto-commit by displaying pending changes and prompting for user confirmation. The module exports pure functions for parsing git status, formatting summaries, and handling user choices, plus integration functions for the pipeline commit flow.
|
|
6
|
+
|
|
7
|
+
## Files to Create/Modify
|
|
8
|
+
|
|
9
|
+
| Path | Action | Purpose |
|
|
10
|
+
|------|--------|---------|
|
|
11
|
+
| `src/diff-preview.js` | Create | Core module with all exported functions |
|
|
12
|
+
| `src/index.js` | Modify | Export diff-preview functions |
|
|
13
|
+
| `src/theme.js` | Modify | Add `user-aborted` status icon |
|
|
14
|
+
| `SKILL.md` | Modify | Document `--no-diff-preview` flag and Step 10.5 |
|
|
15
|
+
|
|
16
|
+
## Implementation Steps
|
|
17
|
+
|
|
18
|
+
1. **Create `src/diff-preview.js` skeleton** with all required exports: `parseGitStatus`, `formatDiffSummary`, `shouldSkipPreview`, `parseUserChoice`, `createAbortResult`, `truncateDiff`, `getPreviewState`, `markWorktreeAborted`, `getPromptText`, `hasChanges`, `formatFeatureHeader`.
|
|
19
|
+
|
|
20
|
+
2. **Implement `parseGitStatus(porcelainOutput)`** - Parse `git status --porcelain` output into `{ added: [], modified: [], deleted: [], total: number }`. Handle status codes: `A`/`??` (added), `M`/` M` (modified), `D`/` D` (deleted).
|
|
21
|
+
|
|
22
|
+
3. **Implement display functions** - `formatDiffSummary(changes, slug)` with 20-file truncation per category, `formatFeatureHeader(slug)`, and `hasChanges(changes)`.
|
|
23
|
+
|
|
24
|
+
4. **Implement skip logic** - `shouldSkipPreview({ noCommit, noDiffPreview, yes, hasChanges })` returns true if any skip condition met.
|
|
25
|
+
|
|
26
|
+
5. **Implement user choice handling** - `parseUserChoice(input)` maps `c/C` to `'commit'`, `a/A` to `'abort'`, `d/D` to `'diff'`, else `null`.
|
|
27
|
+
|
|
28
|
+
6. **Implement abort/state functions** - `createAbortResult(slug)` returns `{ exitCode: 0, reason: 'user-aborted', slug }`, `getPreviewState()` returns `'awaiting-commit-review'`, `markWorktreeAborted(worktree)` sets status to `'user-aborted'`.
|
|
29
|
+
|
|
30
|
+
7. **Implement truncation** - `truncateDiff(diffOutput, threshold)` limits to threshold lines with "... N more lines" message.
|
|
31
|
+
|
|
32
|
+
8. **Implement `getPromptText()`** - Return the interactive prompt string `[c]ommit / [a]bort / [d]iff`.
|
|
33
|
+
|
|
34
|
+
9. **Update `src/theme.js`** - Add `'user-aborted': '\u25a1'` (or similar) to STATUS_ICONS for murmuration display consistency.
|
|
35
|
+
|
|
36
|
+
10. **Update `src/index.js`** - Import and export diff-preview functions for programmatic access.
|
|
37
|
+
|
|
38
|
+
## Risks/Questions
|
|
39
|
+
|
|
40
|
+
- Git command execution not part of pure functions (caller provides porcelain output)
|
|
41
|
+
- Interactive prompt loop (re-prompt after 'd') handled by integration layer, not tested in unit tests
|
|
42
|
+
- Binary file detection (`[binary]` note) deferred to display integration
|
package/README.md
CHANGED
|
@@ -103,7 +103,9 @@ This updates `.blueprint/agents/`, `.blueprint/templates/`, `.blueprint/ways_of_
|
|
|
103
103
|
| Command | Description |
|
|
104
104
|
|---------|-------------|
|
|
105
105
|
| `npx murmur8 history` | View recent pipeline runs |
|
|
106
|
+
| `npx murmur8 history --cost` | View runs with cost breakdown |
|
|
106
107
|
| `npx murmur8 history --stats` | View aggregate statistics |
|
|
108
|
+
| `npx murmur8 history --stats --cost` | View stats with cost metrics |
|
|
107
109
|
| `npx murmur8 history --all` | View all runs |
|
|
108
110
|
| `npx murmur8 history clear` | Clear history |
|
|
109
111
|
| `npx murmur8 history export` | Export history as CSV (default) |
|
|
@@ -136,6 +138,9 @@ This updates `.blueprint/agents/`, `.blueprint/templates/`, `.blueprint/ways_of_
|
|
|
136
138
|
| `npx murmur8 stack-config` | View detected tech stack |
|
|
137
139
|
| `npx murmur8 stack-config set <key> <value>` | Modify stack settings (language, frameworks, testRunner, etc.) |
|
|
138
140
|
| `npx murmur8 stack-config reset` | Reset to empty defaults |
|
|
141
|
+
| `npx murmur8 cost-config` | View cost tracking pricing |
|
|
142
|
+
| `npx murmur8 cost-config set <key> <value>` | Modify pricing (inputPrice, outputPrice) |
|
|
143
|
+
| `npx murmur8 cost-config reset` | Reset to default Claude pricing |
|
|
139
144
|
| `npx murmur8 retry-config` | View retry configuration |
|
|
140
145
|
| `npx murmur8 retry-config set <key> <value>` | Modify retry settings |
|
|
141
146
|
| `npx murmur8 retry-config reset` | Reset to defaults |
|
|
@@ -155,6 +160,7 @@ Run the pipeline with the `/implement-feature` skill in Claude Code:
|
|
|
155
160
|
/implement-feature "Your Slug" --no-validate # Skip pre-flight validation
|
|
156
161
|
/implement-feature "Your Slug" --no-history # Skip history recording
|
|
157
162
|
/implement-feature "Your Slug" --no-commit # Skip auto-commit
|
|
163
|
+
/implement-feature "Your Slug" --no-diff-preview # Skip diff preview before commit
|
|
158
164
|
/implement-feature "Your Slug" --pause-after=alex|cass|nigel|codey-plan
|
|
159
165
|
/implement-feature "Your Slug" --with-stories # Force include Cass stage
|
|
160
166
|
/implement-feature "Your Slug" --skip-stories # Force skip Cass stage
|
|
@@ -243,8 +249,15 @@ The pipeline includes validation, smart routing, feedback loops, and history tra
|
|
|
243
249
|
│
|
|
244
250
|
▼
|
|
245
251
|
┌─────────────────────────────────────────────────────────────────┐
|
|
252
|
+
│ Diff Preview (unless --no-diff-preview) │
|
|
253
|
+
│ • Show file changes (added/modified/deleted) │
|
|
254
|
+
│ • User confirms: commit / abort / view full diff │
|
|
255
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
256
|
+
│
|
|
257
|
+
▼
|
|
258
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
246
259
|
│ Auto-commit → Record to History │
|
|
247
|
-
│ • Duration, feedback scores, outcome
|
|
260
|
+
│ • Duration, feedback scores, token cost, outcome │
|
|
248
261
|
└─────────────────────────────────────────────────────────────────┘
|
|
249
262
|
```
|
|
250
263
|
|
|
@@ -255,7 +268,7 @@ murmur8 includes these built-in modules for observability and self-improvement:
|
|
|
255
268
|
| Module | Purpose |
|
|
256
269
|
|--------|---------|
|
|
257
270
|
| **validate** | Pre-flight checks before pipeline runs |
|
|
258
|
-
| **history** | Records execution data (timing, status, feedback) |
|
|
271
|
+
| **history** | Records execution data (timing, status, feedback, cost) |
|
|
259
272
|
| **insights** | Analyzes patterns, detects bottlenecks, recommends improvements |
|
|
260
273
|
| **retry** | Smart retry strategies based on failure history |
|
|
261
274
|
| **feedback** | Agent-to-agent quality assessment with correlation tracking |
|
|
@@ -265,6 +278,8 @@ murmur8 includes these built-in modules for observability and self-improvement:
|
|
|
265
278
|
| **tools** | Tool schemas and validation for Claude native features |
|
|
266
279
|
| **murm** | Murmuration pipeline execution using git worktrees |
|
|
267
280
|
| **stack** | Configurable tech stack detection and configuration |
|
|
281
|
+
| **cost** | Token usage tracking and cost estimation per stage |
|
|
282
|
+
| **diff-preview** | Pre-commit change review with user confirmation |
|
|
268
283
|
|
|
269
284
|
### How They Work Together
|
|
270
285
|
|
|
@@ -273,8 +288,12 @@ Pipeline Run
|
|
|
273
288
|
│
|
|
274
289
|
├──► history.js records timing at each stage
|
|
275
290
|
│
|
|
291
|
+
├──► cost.js tracks token usage per stage
|
|
292
|
+
│
|
|
276
293
|
├──► feedback.js collects quality ratings between stages
|
|
277
294
|
│
|
|
295
|
+
├──► diff-preview.js shows changes before commit
|
|
296
|
+
│
|
|
278
297
|
└──► On completion/failure, data stored in pipeline-history.json
|
|
279
298
|
│
|
|
280
299
|
▼
|
|
@@ -326,6 +345,7 @@ your-project/
|
|
|
326
345
|
│ ├── pipeline-history.json # Execution history (gitignored)
|
|
327
346
|
│ ├── retry-config.json # Retry configuration (gitignored)
|
|
328
347
|
│ ├── feedback-config.json # Feedback thresholds (gitignored)
|
|
348
|
+
│ ├── cost-config.json # Cost tracking pricing (gitignored)
|
|
329
349
|
│ ├── murm-config.json # Murmuration execution config (gitignored)
|
|
330
350
|
│ ├── murm-queue.json # Murmuration pipeline state (gitignored)
|
|
331
351
|
│ ├── stack-config.json # Tech stack configuration (gitignored)
|
|
@@ -419,6 +439,44 @@ Version 2.7 introduces several optimizations to reduce token usage:
|
|
|
419
439
|
|
|
420
440
|
**Total estimated savings: 10,000+ tokens per pipeline run** (more for technical features)
|
|
421
441
|
|
|
442
|
+
## Cost Tracking
|
|
443
|
+
|
|
444
|
+
Track token usage and estimated costs per pipeline stage:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
# View costs in history
|
|
448
|
+
npx murmur8 history --cost
|
|
449
|
+
|
|
450
|
+
SLUG STATUS DURATION TOTAL COST
|
|
451
|
+
user-auth success 12m 30s $0.088
|
|
452
|
+
api-validation success 8m 15s $0.062
|
|
453
|
+
|
|
454
|
+
# View cost statistics
|
|
455
|
+
npx murmur8 history --stats --cost
|
|
456
|
+
|
|
457
|
+
Avg cost per run: $0.075
|
|
458
|
+
Total cost (all runs): $1.12
|
|
459
|
+
Most expensive stage: codey-implement
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Configuration
|
|
463
|
+
|
|
464
|
+
Default pricing uses Claude Sonnet rates ($3/M input, $15/M output):
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
# View current pricing
|
|
468
|
+
npx murmur8 cost-config
|
|
469
|
+
|
|
470
|
+
# Customize pricing (per million tokens)
|
|
471
|
+
npx murmur8 cost-config set inputPrice 3
|
|
472
|
+
npx murmur8 cost-config set outputPrice 15
|
|
473
|
+
|
|
474
|
+
# Reset to defaults
|
|
475
|
+
npx murmur8 cost-config reset
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Cost data is stored in `pipeline-history.json` alongside timing and feedback data.
|
|
479
|
+
|
|
422
480
|
## Murmuration
|
|
423
481
|
|
|
424
482
|
Run multiple feature pipelines simultaneously using git worktrees for isolation. Each feature gets its own worktree and branch, allowing true parallel development without conflicts.
|
package/SKILL.md
CHANGED
|
@@ -21,6 +21,7 @@ description: Run the Alex → Cass → Nigel → Codey pipeline using Task tool
|
|
|
21
21
|
| `{HISTORY}` | `.claude/pipeline-history.json` |
|
|
22
22
|
| `{RETRY_CONFIG}` | `.claude/retry-config.json` |
|
|
23
23
|
| `{FEEDBACK_CONFIG}` | `.claude/feedback-config.json` |
|
|
24
|
+
| `{COST_CONFIG}` | `.claude/cost-config.json` |
|
|
24
25
|
| `{HANDOFF_ALEX}` | `{FEAT_DIR}/handoff-alex.md` |
|
|
25
26
|
| `{HANDOFF_CASS}` | `{FEAT_DIR}/handoff-cass.md` |
|
|
26
27
|
| `{HANDOFF_NIGEL}` | `{FEAT_DIR}/handoff-nigel.md` |
|
|
@@ -43,6 +44,7 @@ description: Run the Alex → Cass → Nigel → Codey pipeline using Task tool
|
|
|
43
44
|
/implement-feature "user-auth" --interactive # Force interactive spec creation
|
|
44
45
|
/implement-feature "user-auth" --pause-after=alex|cass|nigel|codey-plan
|
|
45
46
|
/implement-feature "user-auth" --no-commit
|
|
47
|
+
/implement-feature "user-auth" --no-diff-preview # Skip diff preview before commit
|
|
46
48
|
/implement-feature "user-auth" --no-feedback # Skip feedback collection
|
|
47
49
|
/implement-feature "user-auth" --no-validate # Skip pre-flight validation
|
|
48
50
|
/implement-feature "user-auth" --no-history # Skip history recording
|
|
@@ -71,12 +73,12 @@ description: Run the Alex → Cass → Nigel → Codey pipeline using Task tool
|
|
|
71
73
|
▼
|
|
72
74
|
ALEX → [feedback] → CASS → [feedback] → NIGEL → [feedback] → CODEY
|
|
73
75
|
│ │
|
|
74
|
-
└──────────── Record timing in history.js
|
|
76
|
+
└──────────── Record timing + tokens in history.js ─────────┘
|
|
75
77
|
│ │
|
|
76
78
|
└──────────── On failure: retry.js strategy ────────────────┘
|
|
77
79
|
│
|
|
78
80
|
▼
|
|
79
|
-
AUTO-COMMIT → Record completion in history
|
|
81
|
+
DIFF-PREVIEW → AUTO-COMMIT → Record completion + cost in history
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
## Multi-Feature Pipeline Overview (Murmuration Mode)
|
|
@@ -327,9 +329,15 @@ Wait for ALL sub-agents to complete before proceeding.
|
|
|
327
329
|
|
|
328
330
|
---
|
|
329
331
|
|
|
330
|
-
## Step M5.5: Commit Worktree Changes
|
|
332
|
+
## Step M5.5: Diff Preview & Commit Worktree Changes
|
|
331
333
|
|
|
332
|
-
For each successful pipeline,
|
|
334
|
+
For each successful pipeline, show diff preview (unless `--no-diff-preview`) then commit the changes in its worktree.
|
|
335
|
+
|
|
336
|
+
**Diff Preview per Worktree:**
|
|
337
|
+
- Show changes for each worktree before committing
|
|
338
|
+
- User can approve/abort each worktree individually
|
|
339
|
+
- If user aborts a worktree, mark it as `user-aborted` (not failed)
|
|
340
|
+
- Continue to next worktree regardless
|
|
333
341
|
|
|
334
342
|
**IMPORTANT:** Use absolute paths to avoid context confusion.
|
|
335
343
|
|
|
@@ -946,6 +954,50 @@ For detailed guidance, see: .blueprint/agents/AGENT_DEVELOPER_CODEY.md
|
|
|
946
954
|
|
|
947
955
|
---
|
|
948
956
|
|
|
957
|
+
## Step 10.5: Diff Preview
|
|
958
|
+
|
|
959
|
+
**Module:** `src/diff-preview.js`
|
|
960
|
+
|
|
961
|
+
Before committing, show the user a preview of all changes unless skipped.
|
|
962
|
+
|
|
963
|
+
**Skip conditions** (any of these skips the preview):
|
|
964
|
+
- `--no-commit` flag is set
|
|
965
|
+
- `--no-diff-preview` flag is set
|
|
966
|
+
- `--yes` flag is set (non-interactive mode)
|
|
967
|
+
- No changes detected
|
|
968
|
+
|
|
969
|
+
**Display:**
|
|
970
|
+
```
|
|
971
|
+
Changes to commit for feature_{slug}:
|
|
972
|
+
|
|
973
|
+
Added (3 files):
|
|
974
|
+
+ .blueprint/features/feature_{slug}/FEATURE_SPEC.md
|
|
975
|
+
+ test/feature_{slug}.test.js
|
|
976
|
+
+ src/feature.js
|
|
977
|
+
|
|
978
|
+
Modified (1 file):
|
|
979
|
+
~ src/index.js
|
|
980
|
+
|
|
981
|
+
Deleted (0 files):
|
|
982
|
+
(none)
|
|
983
|
+
|
|
984
|
+
Total: 4 files changed
|
|
985
|
+
|
|
986
|
+
[c]ommit / [a]bort / [d]iff (show full diff)?
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
**User choices:**
|
|
990
|
+
- `c` or `commit`: Proceed to auto-commit
|
|
991
|
+
- `a` or `abort`: Exit pipeline cleanly (exit code 0, not a failure)
|
|
992
|
+
- `d` or `diff`: Show full `git diff` output, then re-prompt
|
|
993
|
+
|
|
994
|
+
**On abort:**
|
|
995
|
+
- Record in history: `status: "user-aborted"`, `reason: "User aborted at diff preview"`
|
|
996
|
+
- Do NOT record as failure
|
|
997
|
+
- Clean exit
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
949
1001
|
## Step 11: Auto-commit & Backlog Cleanup
|
|
950
1002
|
|
|
951
1003
|
If not `--no-commit`:
|
|
@@ -1001,7 +1053,7 @@ After successful commit, remove the completed feature from `{BACKLOG}`:
|
|
|
1001
1053
|
|
|
1002
1054
|
## Step 12: Report Status & Finalize History (ENHANCED)
|
|
1003
1055
|
|
|
1004
|
-
**
|
|
1056
|
+
**Modules:** `src/history.js`, `src/cost.js`
|
|
1005
1057
|
|
|
1006
1058
|
Unless `--no-history` flag is set, finalize the history entry:
|
|
1007
1059
|
|
|
@@ -1010,6 +1062,8 @@ historyEntry.status = "success";
|
|
|
1010
1062
|
historyEntry.completedAt = new Date().toISOString();
|
|
1011
1063
|
historyEntry.totalDurationMs = completedAt - startedAt;
|
|
1012
1064
|
historyEntry.commitHash = "{hash}";
|
|
1065
|
+
historyEntry.totalTokens = { input: N, output: M };
|
|
1066
|
+
historyEntry.totalCost = X.XXX;
|
|
1013
1067
|
// Save to .claude/pipeline-history.json
|
|
1014
1068
|
```
|
|
1015
1069
|
|
|
@@ -1033,6 +1087,16 @@ historyEntry.commitHash = "{hash}";
|
|
|
1033
1087
|
- Cass stories: rated 5/5 by Nigel
|
|
1034
1088
|
- Nigel tests: rated 4/5 by Codey
|
|
1035
1089
|
|
|
1090
|
+
## Cost Summary
|
|
1091
|
+
STAGE INPUT OUTPUT COST
|
|
1092
|
+
alex 2,450 1,230 $0.014
|
|
1093
|
+
cass 3,100 1,850 $0.019
|
|
1094
|
+
nigel 2,800 2,100 $0.018
|
|
1095
|
+
codey-plan 1,500 890 $0.009
|
|
1096
|
+
codey-impl 4,200 3,500 $0.028
|
|
1097
|
+
─────────────────────────────────────────
|
|
1098
|
+
TOTAL 14,050 9,570 $0.088
|
|
1099
|
+
|
|
1036
1100
|
## Next Action
|
|
1037
1101
|
Pipeline complete. Run `npm test` to verify or `/implement-feature` for next feature.
|
|
1038
1102
|
```
|
|
@@ -1189,10 +1253,12 @@ The pipeline integrates these murmur8 modules:
|
|
|
1189
1253
|
| Module | File | Integration Points |
|
|
1190
1254
|
|--------|------|-------------------|
|
|
1191
1255
|
| **validate** | `src/validate.js` | Step 0: Pre-flight checks |
|
|
1192
|
-
| **history** | `src/history.js` | Steps 5-12: Record timing,
|
|
1256
|
+
| **history** | `src/history.js` | Steps 5-12: Record timing, tokens, cost |
|
|
1193
1257
|
| **insights** | `src/insights.js` | Step 3.5: Preview, On failure: Analysis |
|
|
1194
1258
|
| **feedback** | `src/feedback.js` | Steps 6.5, 7.5, 8.5: Quality gates |
|
|
1195
1259
|
| **retry** | `src/retry.js` | On failure: Strategy recommendation |
|
|
1260
|
+
| **cost** | `src/cost.js` | Steps 6-12: Track tokens, calculate cost |
|
|
1261
|
+
| **diff-preview** | `src/diff-preview.js` | Step 10.5: Show changes before commit |
|
|
1196
1262
|
|
|
1197
1263
|
### CLI Commands Available
|
|
1198
1264
|
|
|
@@ -1202,7 +1268,9 @@ npx murmur8 validate
|
|
|
1202
1268
|
|
|
1203
1269
|
# History management
|
|
1204
1270
|
npx murmur8 history
|
|
1271
|
+
npx murmur8 history --cost # Include cost breakdown
|
|
1205
1272
|
npx murmur8 history --stats
|
|
1273
|
+
npx murmur8 history --stats --cost # Include cost metrics
|
|
1206
1274
|
npx murmur8 history --all
|
|
1207
1275
|
|
|
1208
1276
|
# Pipeline insights
|
|
@@ -1211,6 +1279,12 @@ npx murmur8 insights --feedback
|
|
|
1211
1279
|
npx murmur8 insights --bottlenecks
|
|
1212
1280
|
npx murmur8 insights --failures
|
|
1213
1281
|
|
|
1282
|
+
# Cost configuration
|
|
1283
|
+
npx murmur8 cost-config
|
|
1284
|
+
npx murmur8 cost-config set inputPrice 3 # Per million tokens
|
|
1285
|
+
npx murmur8 cost-config set outputPrice 15 # Per million tokens
|
|
1286
|
+
npx murmur8 cost-config reset
|
|
1287
|
+
|
|
1214
1288
|
# Retry configuration
|
|
1215
1289
|
npx murmur8 retry-config
|
|
1216
1290
|
npx murmur8 retry-config set maxRetries 5
|
|
@@ -1224,7 +1298,8 @@ npx murmur8 feedback-config set minRatingThreshold 3.5
|
|
|
1224
1298
|
|
|
1225
1299
|
| File | Purpose |
|
|
1226
1300
|
|------|---------|
|
|
1227
|
-
| `.claude/pipeline-history.json` | Execution history with timing and
|
|
1301
|
+
| `.claude/pipeline-history.json` | Execution history with timing, feedback, and cost |
|
|
1228
1302
|
| `.claude/retry-config.json` | Retry strategies and thresholds |
|
|
1229
1303
|
| `.claude/feedback-config.json` | Feedback quality gate thresholds |
|
|
1304
|
+
| `.claude/cost-config.json` | Token pricing configuration |
|
|
1230
1305
|
| `.claude/implement-queue.json` | Pipeline queue state (existing) |
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cost-config command - Manage pricing configuration for cost tracking
|
|
3
|
+
*/
|
|
4
|
+
const { displayConfig, setConfigValue, resetConfig } = require('../cost');
|
|
5
|
+
|
|
6
|
+
const description = 'Manage pricing configuration for cost tracking';
|
|
7
|
+
|
|
8
|
+
async function run(args) {
|
|
9
|
+
const subArg = args[1];
|
|
10
|
+
|
|
11
|
+
if (subArg === 'set') {
|
|
12
|
+
const key = args[2];
|
|
13
|
+
const value = args[3];
|
|
14
|
+
if (!key || !value) {
|
|
15
|
+
console.error('Usage: cost-config set <key> <value>');
|
|
16
|
+
console.error('Valid keys: inputPricePerMillion, outputPricePerMillion');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
setConfigValue(key, value);
|
|
20
|
+
} else if (subArg === 'reset') {
|
|
21
|
+
resetConfig();
|
|
22
|
+
console.log('Cost configuration reset to defaults.');
|
|
23
|
+
} else {
|
|
24
|
+
displayConfig();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { run, description };
|
package/src/commands/help.js
CHANGED
|
@@ -19,6 +19,8 @@ Commands:
|
|
|
19
19
|
history View recent pipeline runs (last 10 by default)
|
|
20
20
|
history --all View all pipeline runs
|
|
21
21
|
history --stats View aggregate statistics
|
|
22
|
+
history --cost View history with total cost column
|
|
23
|
+
history --stats --cost View statistics with cost metrics
|
|
22
24
|
history clear Clear all pipeline history (with confirmation)
|
|
23
25
|
history clear --force Clear all pipeline history (no confirmation)
|
|
24
26
|
history export Export history as CSV (default) or JSON
|
|
@@ -42,6 +44,9 @@ Commands:
|
|
|
42
44
|
stack-config View current tech stack configuration
|
|
43
45
|
stack-config set <key> <value> Modify a config value (language, runtime, frameworks, etc.)
|
|
44
46
|
stack-config reset Reset tech stack configuration to defaults
|
|
47
|
+
cost-config View current pricing configuration for cost tracking
|
|
48
|
+
cost-config set <key> <value> Modify a config value (inputPricePerMillion, outputPricePerMillion)
|
|
49
|
+
cost-config reset Reset pricing configuration to defaults
|
|
45
50
|
murm <slugs...> Run multiple feature pipelines in parallel (murmuration)
|
|
46
51
|
murm <slugs...> --dry-run Show execution plan without running
|
|
47
52
|
murm <slugs...> --yes Skip confirmation prompt
|
package/src/commands/history.js
CHANGED
|
@@ -33,9 +33,9 @@ async function run(args) {
|
|
|
33
33
|
console.log(result.output);
|
|
34
34
|
}
|
|
35
35
|
} else if (flags.stats) {
|
|
36
|
-
showStats();
|
|
36
|
+
showStats({ cost: flags.cost });
|
|
37
37
|
} else {
|
|
38
|
-
displayHistory({ all: flags.all });
|
|
38
|
+
displayHistory({ all: flags.all, cost: flags.cost });
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
package/src/commands/utils.js
CHANGED
package/src/cost.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createConfigModule } = require('./config-factory');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_INPUT_PRICE = 3;
|
|
6
|
+
const DEFAULT_OUTPUT_PRICE = 15;
|
|
7
|
+
|
|
8
|
+
const costConfig = createConfigModule({
|
|
9
|
+
name: 'Cost',
|
|
10
|
+
file: '.claude/cost-config.json',
|
|
11
|
+
defaults: {
|
|
12
|
+
inputPricePerMillion: DEFAULT_INPUT_PRICE,
|
|
13
|
+
outputPricePerMillion: DEFAULT_OUTPUT_PRICE
|
|
14
|
+
},
|
|
15
|
+
validators: {
|
|
16
|
+
inputPricePerMillion: (v) => v >= 0 || 'Must be non-negative',
|
|
17
|
+
outputPricePerMillion: (v) => v >= 0 || 'Must be non-negative'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function getDefaultPricing() {
|
|
22
|
+
return {
|
|
23
|
+
inputPricePerMillion: DEFAULT_INPUT_PRICE,
|
|
24
|
+
outputPricePerMillion: DEFAULT_OUTPUT_PRICE
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadPricingConfig() {
|
|
29
|
+
return costConfig.read();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function savePricingConfig(config) {
|
|
33
|
+
costConfig.write(config);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function calculateCost(inputTokens, outputTokens, pricing = getDefaultPricing()) {
|
|
37
|
+
const inputCost = (inputTokens / 1_000_000) * pricing.inputPricePerMillion;
|
|
38
|
+
const outputCost = (outputTokens / 1_000_000) * pricing.outputPricePerMillion;
|
|
39
|
+
return Math.round((inputCost + outputCost) * 1000) / 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatCost(cost) {
|
|
43
|
+
if (cost === null || cost === undefined) return 'N/A';
|
|
44
|
+
return `$${cost.toFixed(3)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function formatTokens(tokens) {
|
|
48
|
+
if (tokens === null || tokens === undefined) return 'N/A';
|
|
49
|
+
return tokens.toLocaleString();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getCostSummary(stages, pricing = loadPricingConfig()) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
let totalInput = 0;
|
|
55
|
+
let totalOutput = 0;
|
|
56
|
+
let totalCost = 0;
|
|
57
|
+
|
|
58
|
+
for (const [name, data] of Object.entries(stages)) {
|
|
59
|
+
if (data && data.tokens) {
|
|
60
|
+
const input = data.tokens.input || 0;
|
|
61
|
+
const output = data.tokens.output || 0;
|
|
62
|
+
const cost = data.cost !== undefined ? data.cost : calculateCost(input, output, pricing);
|
|
63
|
+
|
|
64
|
+
totalInput += input;
|
|
65
|
+
totalOutput += output;
|
|
66
|
+
totalCost += cost;
|
|
67
|
+
|
|
68
|
+
lines.push({
|
|
69
|
+
stage: name,
|
|
70
|
+
input,
|
|
71
|
+
output,
|
|
72
|
+
cost
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
stages: lines,
|
|
79
|
+
totals: {
|
|
80
|
+
input: totalInput,
|
|
81
|
+
output: totalOutput,
|
|
82
|
+
cost: totalCost
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function displayCostSummary(slug, stages) {
|
|
88
|
+
const summary = getCostSummary(stages);
|
|
89
|
+
|
|
90
|
+
console.log(`\nCost Summary for feature: ${slug}\n`);
|
|
91
|
+
console.log('STAGE INPUT OUTPUT COST');
|
|
92
|
+
|
|
93
|
+
for (const s of summary.stages) {
|
|
94
|
+
const stage = s.stage.padEnd(16);
|
|
95
|
+
const input = formatTokens(s.input).padStart(9);
|
|
96
|
+
const output = formatTokens(s.output).padStart(10);
|
|
97
|
+
const cost = formatCost(s.cost).padStart(8);
|
|
98
|
+
console.log(`${stage} ${input} ${output} ${cost}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log('─'.repeat(45));
|
|
102
|
+
const totalStage = 'TOTAL'.padEnd(16);
|
|
103
|
+
const totalInput = formatTokens(summary.totals.input).padStart(9);
|
|
104
|
+
const totalOutput = formatTokens(summary.totals.output).padStart(10);
|
|
105
|
+
const totalCost = formatCost(summary.totals.cost).padStart(8);
|
|
106
|
+
console.log(`${totalStage} ${totalInput} ${totalOutput} ${totalCost}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
CONFIG_FILE: costConfig.CONFIG_FILE,
|
|
111
|
+
getDefaultPricing,
|
|
112
|
+
loadPricingConfig,
|
|
113
|
+
savePricingConfig,
|
|
114
|
+
calculateCost,
|
|
115
|
+
formatCost,
|
|
116
|
+
formatTokens,
|
|
117
|
+
getCostSummary,
|
|
118
|
+
displayCostSummary,
|
|
119
|
+
displayConfig: costConfig.display,
|
|
120
|
+
setConfigValue: costConfig.setValue,
|
|
121
|
+
resetConfig: costConfig.reset
|
|
122
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function parseGitStatus(porcelainOutput) {
|
|
4
|
+
const added = [];
|
|
5
|
+
const modified = [];
|
|
6
|
+
const deleted = [];
|
|
7
|
+
|
|
8
|
+
if (!porcelainOutput || porcelainOutput.trim() === '') {
|
|
9
|
+
return { added, modified, deleted, total: 0 };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const lines = porcelainOutput.trim().split('\n');
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
if (line.length < 3) continue;
|
|
15
|
+
|
|
16
|
+
const statusCode = line.substring(0, 2);
|
|
17
|
+
const filePath = line.substring(3);
|
|
18
|
+
|
|
19
|
+
if (statusCode === 'A ' || statusCode === '??') {
|
|
20
|
+
added.push(filePath);
|
|
21
|
+
} else if (statusCode === 'M ' || statusCode === ' M') {
|
|
22
|
+
modified.push(filePath);
|
|
23
|
+
} else if (statusCode === 'D ' || statusCode === ' D') {
|
|
24
|
+
deleted.push(filePath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
added,
|
|
30
|
+
modified,
|
|
31
|
+
deleted,
|
|
32
|
+
total: added.length + modified.length + deleted.length
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function formatDiffSummary(changes, slug) {
|
|
37
|
+
const lines = [];
|
|
38
|
+
const MAX_FILES = 20;
|
|
39
|
+
|
|
40
|
+
lines.push(formatFeatureHeader(slug));
|
|
41
|
+
lines.push('');
|
|
42
|
+
|
|
43
|
+
const addedLabel = `Added (${changes.added.length} file${changes.added.length === 1 ? '' : 's'})`;
|
|
44
|
+
lines.push(addedLabel);
|
|
45
|
+
if (changes.added.length === 0) {
|
|
46
|
+
lines.push(' (none)');
|
|
47
|
+
} else {
|
|
48
|
+
const displayFiles = changes.added.slice(0, MAX_FILES);
|
|
49
|
+
for (const file of displayFiles) {
|
|
50
|
+
lines.push(` + ${file}`);
|
|
51
|
+
}
|
|
52
|
+
if (changes.added.length > MAX_FILES) {
|
|
53
|
+
lines.push(` ... and ${changes.added.length - MAX_FILES} more`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
lines.push('');
|
|
57
|
+
|
|
58
|
+
const modifiedLabel = `Modified (${changes.modified.length} file${changes.modified.length === 1 ? '' : 's'})`;
|
|
59
|
+
lines.push(modifiedLabel);
|
|
60
|
+
if (changes.modified.length === 0) {
|
|
61
|
+
lines.push(' (none)');
|
|
62
|
+
} else {
|
|
63
|
+
const displayFiles = changes.modified.slice(0, MAX_FILES);
|
|
64
|
+
for (const file of displayFiles) {
|
|
65
|
+
lines.push(` ~ ${file}`);
|
|
66
|
+
}
|
|
67
|
+
if (changes.modified.length > MAX_FILES) {
|
|
68
|
+
lines.push(` ... and ${changes.modified.length - MAX_FILES} more`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
lines.push('');
|
|
72
|
+
|
|
73
|
+
const deletedLabel = `Deleted (${changes.deleted.length} file${changes.deleted.length === 1 ? '' : 's'})`;
|
|
74
|
+
lines.push(deletedLabel);
|
|
75
|
+
if (changes.deleted.length === 0) {
|
|
76
|
+
lines.push(' (none)');
|
|
77
|
+
} else {
|
|
78
|
+
const displayFiles = changes.deleted.slice(0, MAX_FILES);
|
|
79
|
+
for (const file of displayFiles) {
|
|
80
|
+
lines.push(` - ${file}`);
|
|
81
|
+
}
|
|
82
|
+
if (changes.deleted.length > MAX_FILES) {
|
|
83
|
+
lines.push(` ... and ${changes.deleted.length - MAX_FILES} more`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
lines.push('');
|
|
87
|
+
|
|
88
|
+
lines.push(`Total: ${changes.total} files changed`);
|
|
89
|
+
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatFeatureHeader(slug) {
|
|
94
|
+
return `--- Changes to commit for feature: ${slug} ---`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function hasChanges(changes) {
|
|
98
|
+
return changes.total > 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function shouldSkipPreview({ noCommit, noDiffPreview, yes, hasChanges }) {
|
|
102
|
+
if (noCommit) return true;
|
|
103
|
+
if (noDiffPreview) return true;
|
|
104
|
+
if (yes) return true;
|
|
105
|
+
if (!hasChanges) return true;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseUserChoice(input) {
|
|
110
|
+
if (!input || typeof input !== 'string') return null;
|
|
111
|
+
const normalized = input.toLowerCase().trim();
|
|
112
|
+
if (normalized === 'c') return 'commit';
|
|
113
|
+
if (normalized === 'a') return 'abort';
|
|
114
|
+
if (normalized === 'd') return 'diff';
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createAbortResult(slug) {
|
|
119
|
+
return {
|
|
120
|
+
exitCode: 0,
|
|
121
|
+
reason: 'user-aborted',
|
|
122
|
+
slug
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function truncateDiff(diffOutput, threshold) {
|
|
127
|
+
if (!diffOutput) return diffOutput;
|
|
128
|
+
const lines = diffOutput.split('\n');
|
|
129
|
+
if (lines.length <= threshold) {
|
|
130
|
+
return diffOutput;
|
|
131
|
+
}
|
|
132
|
+
const truncated = lines.slice(0, threshold);
|
|
133
|
+
const remaining = lines.length - threshold;
|
|
134
|
+
truncated.push(`... ${remaining} more lines`);
|
|
135
|
+
return truncated.join('\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function getPreviewState() {
|
|
139
|
+
return 'awaiting-commit-review';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function markWorktreeAborted(worktree) {
|
|
143
|
+
return {
|
|
144
|
+
...worktree,
|
|
145
|
+
status: 'user-aborted'
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getPromptText() {
|
|
150
|
+
return '[c]ommit / [a]bort / [d]iff';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
parseGitStatus,
|
|
155
|
+
formatDiffSummary,
|
|
156
|
+
formatFeatureHeader,
|
|
157
|
+
hasChanges,
|
|
158
|
+
shouldSkipPreview,
|
|
159
|
+
parseUserChoice,
|
|
160
|
+
createAbortResult,
|
|
161
|
+
truncateDiff,
|
|
162
|
+
getPreviewState,
|
|
163
|
+
markWorktreeAborted,
|
|
164
|
+
getPromptText
|
|
165
|
+
};
|
package/src/history.js
CHANGED
|
@@ -195,8 +195,14 @@ function formatDate(isoString) {
|
|
|
195
195
|
return date.toISOString().replace('T', ' ').slice(0, 19);
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
function formatCostValue(cost) {
|
|
199
|
+
if (cost === null || cost === undefined) return 'N/A';
|
|
200
|
+
return `$${cost.toFixed(3)}`;
|
|
201
|
+
}
|
|
202
|
+
|
|
198
203
|
function displayHistory(options = {}) {
|
|
199
204
|
const showAll = options.all || false;
|
|
205
|
+
const showCost = options.cost || false;
|
|
200
206
|
const useColor = options.color !== false && process.stdout.isTTY;
|
|
201
207
|
|
|
202
208
|
const history = readHistoryFile();
|
|
@@ -220,7 +226,11 @@ function displayHistory(options = {}) {
|
|
|
220
226
|
const showing = entries.length;
|
|
221
227
|
|
|
222
228
|
console.log(`\nPipeline History (showing ${showing} of ${total} runs)\n`);
|
|
223
|
-
|
|
229
|
+
if (showCost) {
|
|
230
|
+
console.log(' SLUG STATUS DURATION TOTAL COST');
|
|
231
|
+
} else {
|
|
232
|
+
console.log(' SLUG STATUS DATE DURATION');
|
|
233
|
+
}
|
|
224
234
|
|
|
225
235
|
for (const entry of entries) {
|
|
226
236
|
const slug = entry.slug.padEnd(18);
|
|
@@ -243,7 +253,12 @@ function displayHistory(options = {}) {
|
|
|
243
253
|
suffix = ` (paused at: ${entry.pausedAfter})`;
|
|
244
254
|
}
|
|
245
255
|
|
|
246
|
-
|
|
256
|
+
if (showCost) {
|
|
257
|
+
const costDisplay = formatCostValue(entry.totalCost).padEnd(10);
|
|
258
|
+
console.log(` ${slug} ${status} ${duration.padEnd(9)} ${costDisplay}${suffix}`);
|
|
259
|
+
} else {
|
|
260
|
+
console.log(` ${slug} ${status} ${date} ${duration}${suffix}`);
|
|
261
|
+
}
|
|
247
262
|
}
|
|
248
263
|
|
|
249
264
|
if (!showAll && total > 10) {
|
|
@@ -252,7 +267,8 @@ function displayHistory(options = {}) {
|
|
|
252
267
|
console.log(`Run 'murmur8 history --stats' for aggregate statistics.`);
|
|
253
268
|
}
|
|
254
269
|
|
|
255
|
-
function showStats() {
|
|
270
|
+
function showStats(options = {}) {
|
|
271
|
+
const showCost = options.cost || false;
|
|
256
272
|
const history = readHistoryFile();
|
|
257
273
|
|
|
258
274
|
if (history.error === 'corrupted') {
|
|
@@ -285,6 +301,38 @@ function showStats() {
|
|
|
285
301
|
console.log(` Avg pipeline duration ${formatDuration(avgTotal)}`);
|
|
286
302
|
}
|
|
287
303
|
|
|
304
|
+
if (showCost) {
|
|
305
|
+
const runsWithCost = history.filter(e => e.totalCost !== undefined);
|
|
306
|
+
if (runsWithCost.length > 0) {
|
|
307
|
+
const totalCostAll = runsWithCost.reduce((sum, e) => sum + e.totalCost, 0);
|
|
308
|
+
const avgCost = totalCostAll / runsWithCost.length;
|
|
309
|
+
console.log(` Avg cost per run ${formatCostValue(avgCost)}`);
|
|
310
|
+
console.log(` Total cost (all runs) ${formatCostValue(totalCostAll)}`);
|
|
311
|
+
|
|
312
|
+
const stages = ['alex', 'cass', 'nigel', 'codey-plan', 'codey-implement'];
|
|
313
|
+
const stageCosts = {};
|
|
314
|
+
for (const stage of stages) {
|
|
315
|
+
stageCosts[stage] = 0;
|
|
316
|
+
}
|
|
317
|
+
for (const entry of runsWithCost) {
|
|
318
|
+
if (entry.stages) {
|
|
319
|
+
for (const stage of stages) {
|
|
320
|
+
if (entry.stages[stage] && entry.stages[stage].cost !== undefined) {
|
|
321
|
+
stageCosts[stage] += entry.stages[stage].cost;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const maxCost = Math.max(...Object.values(stageCosts));
|
|
327
|
+
const mostExpensive = Object.entries(stageCosts)
|
|
328
|
+
.filter(([, cost]) => cost === maxCost)
|
|
329
|
+
.map(([stage]) => stage);
|
|
330
|
+
if (mostExpensive.length > 0 && maxCost > 0) {
|
|
331
|
+
console.log(` Most expensive stage ${mostExpensive[0]} (${formatCostValue(maxCost)} total)`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
288
336
|
const stages = ['alex', 'cass', 'nigel', 'codey-plan', 'codey-implement'];
|
|
289
337
|
const stageStats = {};
|
|
290
338
|
const failureCounts = {};
|
package/src/index.js
CHANGED
|
@@ -81,6 +81,19 @@ const {
|
|
|
81
81
|
detectStackConfig,
|
|
82
82
|
displayStackConfig
|
|
83
83
|
} = require('./stack');
|
|
84
|
+
const {
|
|
85
|
+
parseGitStatus,
|
|
86
|
+
formatDiffSummary,
|
|
87
|
+
formatFeatureHeader,
|
|
88
|
+
hasChanges,
|
|
89
|
+
shouldSkipPreview,
|
|
90
|
+
parseUserChoice,
|
|
91
|
+
createAbortResult,
|
|
92
|
+
truncateDiff,
|
|
93
|
+
getPreviewState,
|
|
94
|
+
markWorktreeAborted,
|
|
95
|
+
getPromptText
|
|
96
|
+
} = require('./diff-preview');
|
|
84
97
|
const tools = require('./tools');
|
|
85
98
|
const theme = require('./theme');
|
|
86
99
|
|
|
@@ -149,6 +162,18 @@ module.exports = {
|
|
|
149
162
|
tools,
|
|
150
163
|
// Theme module (murmuration visual theming)
|
|
151
164
|
theme,
|
|
165
|
+
// Diff preview exports
|
|
166
|
+
parseGitStatus,
|
|
167
|
+
formatDiffSummary,
|
|
168
|
+
formatFeatureHeader,
|
|
169
|
+
hasChanges,
|
|
170
|
+
shouldSkipPreview,
|
|
171
|
+
parseUserChoice,
|
|
172
|
+
createAbortResult,
|
|
173
|
+
truncateDiff,
|
|
174
|
+
getPreviewState,
|
|
175
|
+
markWorktreeAborted,
|
|
176
|
+
getPromptText,
|
|
152
177
|
// Interactive mode exports
|
|
153
178
|
parseInteractiveFlags,
|
|
154
179
|
shouldEnterInteractiveMode,
|
package/src/theme.js
CHANGED