context-planning 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +454 -0
- package/bin/commands/_helpers.js +53 -0
- package/bin/commands/_usage.js +67 -0
- package/bin/commands/capture.js +46 -0
- package/bin/commands/codebase-status.js +41 -0
- package/bin/commands/complete-milestone.js +57 -0
- package/bin/commands/config.js +70 -0
- package/bin/commands/doctor.js +139 -0
- package/bin/commands/gsd-import.js +90 -0
- package/bin/commands/inbox.js +81 -0
- package/bin/commands/index.js +33 -0
- package/bin/commands/init.js +87 -0
- package/bin/commands/install.js +43 -0
- package/bin/commands/scaffold-codebase.js +53 -0
- package/bin/commands/scaffold-milestone.js +58 -0
- package/bin/commands/scaffold-phase.js +65 -0
- package/bin/commands/status.js +42 -0
- package/bin/commands/statusline.js +108 -0
- package/bin/commands/tick.js +49 -0
- package/bin/commands/version.js +9 -0
- package/bin/commands/worktree.js +218 -0
- package/bin/commands/write-summary.js +54 -0
- package/bin/cp.cmd +2 -0
- package/bin/cp.js +54 -0
- package/commands/cp/capture.md +107 -0
- package/commands/cp/complete-milestone.md +166 -0
- package/commands/cp/execute-phase.md +220 -0
- package/commands/cp/map-codebase.md +211 -0
- package/commands/cp/new-milestone.md +136 -0
- package/commands/cp/new-project.md +132 -0
- package/commands/cp/plan-phase.md +195 -0
- package/commands/cp/progress.md +147 -0
- package/commands/cp/quick.md +104 -0
- package/commands/cp/resume.md +125 -0
- package/commands/cp/write-summary.md +33 -0
- package/docs/MIGRATION-v0.5.md +140 -0
- package/docs/architecture.md +189 -0
- package/docs/superpowers/plans/2026-05-20-v0-7-plan-16-01-design-md-infrastructure.md +1064 -0
- package/docs/superpowers/plans/2026-05-20-v0-7-plan-16-02-review-log-infrastructure.md +418 -0
- package/docs/superpowers/plans/2026-05-20-v0-7-plan-16-03-key-decisions-hard-block.md +295 -0
- package/docs/superpowers/specs/2026-05-20-generic-provider-harness-detection-design.md +380 -0
- package/docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md +400 -0
- package/docs/writing-providers.md +76 -0
- package/install/aider.js +204 -0
- package/install/claude.js +116 -0
- package/install/common.js +65 -0
- package/install/copilot.js +86 -0
- package/install/cursor.js +120 -0
- package/install/echo-provider.js +50 -0
- package/lib/codebase-mapper.js +169 -0
- package/lib/detect.js +280 -0
- package/lib/frontmatter.js +72 -0
- package/lib/gsd-compat.js +165 -0
- package/lib/import.js +543 -0
- package/lib/inbox.js +226 -0
- package/lib/lifecycle.js +929 -0
- package/lib/merge.js +157 -0
- package/lib/milestone.js +595 -0
- package/lib/paths.js +191 -0
- package/lib/provider.js +168 -0
- package/lib/roadmap.js +134 -0
- package/lib/state.js +99 -0
- package/lib/worktree.js +253 -0
- package/package.json +45 -0
- package/templates/DESIGN.md +78 -0
- package/templates/INBOX.md +13 -0
- package/templates/MILESTONE-CONTEXT.md +40 -0
- package/templates/MILESTONES.md +29 -0
- package/templates/PLAN.md +84 -0
- package/templates/PROJECT.md +43 -0
- package/templates/REVIEW-LOG.md +38 -0
- package/templates/ROADMAP.md +34 -0
- package/templates/STATE.md +78 -0
- package/templates/SUMMARY.md +75 -0
- package/templates/codebase/ARCHITECTURE.md +30 -0
- package/templates/codebase/CONCERNS.md +30 -0
- package/templates/codebase/CONVENTIONS.md +30 -0
- package/templates/codebase/INTEGRATIONS.md +30 -0
- package/templates/codebase/STACK.md +26 -0
- package/templates/codebase/STRUCTURE.md +32 -0
- package/templates/codebase/TESTING.md +39 -0
- package/templates/config.json +173 -0
- package/templates/phase-PLAN.md +32 -0
- package/templates/quick-PLAN.md +24 -0
- package/templates/quick-SUMMARY.md +25 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 16-design-capture-infrastructure
|
|
3
|
+
plan: "03"
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 3
|
|
6
|
+
depends_on: ["16-01"]
|
|
7
|
+
files_modified:
|
|
8
|
+
- lib/milestone.js
|
|
9
|
+
- bin/commands/write-summary.js
|
|
10
|
+
- commands/cp/write-summary.md
|
|
11
|
+
- test/unit-design.js
|
|
12
|
+
- .planning/phases/15-*/summaries (10 backfills, gitignored)
|
|
13
|
+
autonomous: true
|
|
14
|
+
requirements: []
|
|
15
|
+
user_setup: []
|
|
16
|
+
must_haves:
|
|
17
|
+
truths:
|
|
18
|
+
- "cp write-summary exits 2 with exact error message if key-decisions empty"
|
|
19
|
+
- "cp-write-summary skill instructs callers to populate key-decisions"
|
|
20
|
+
- "existing v0.6 SUMMARYs backfilled with key-decisions (dogfood)"
|
|
21
|
+
artifacts:
|
|
22
|
+
- "lib/milestone.writeSummary throws ValidationError on empty key-decisions"
|
|
23
|
+
- "test/unit-design.js extended with validation assertions"
|
|
24
|
+
- "Exit code 2 from CLI on bad input"
|
|
25
|
+
key_links:
|
|
26
|
+
- "docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md"
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<objective>
|
|
30
|
+
Plan 16-03: key-decisions hard-block (Phase 16, milestone v0.7 Design Capture).
|
|
31
|
+
|
|
32
|
+
Purpose: Prevent silent "key-decisions: []" SUMMARYs that erase decision
|
|
33
|
+
rationale. `cp write-summary` MUST exit 2 with the exact error message:
|
|
34
|
+
|
|
35
|
+
Error: 'key-decisions' is required and must have ≥1 entry. See spec at
|
|
36
|
+
docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md
|
|
37
|
+
|
|
38
|
+
The validation runs in `lib/milestone.writeSummary`; the CLI handler
|
|
39
|
+
propagates the throw as exit code 2.
|
|
40
|
+
|
|
41
|
+
Scope: validation + skill update + backfill 10 existing v0.6 dogfood SUMMARYs.
|
|
42
|
+
</objective>
|
|
43
|
+
|
|
44
|
+
<execution_context>
|
|
45
|
+
@.planning/config.json
|
|
46
|
+
</execution_context>
|
|
47
|
+
|
|
48
|
+
<context>
|
|
49
|
+
@.planning/PROJECT.md
|
|
50
|
+
@.planning/ROADMAP.md
|
|
51
|
+
@docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md
|
|
52
|
+
@.planning/phases/16-design-capture-infrastructure/DESIGN.md
|
|
53
|
+
</context>
|
|
54
|
+
|
|
55
|
+
<tasks>
|
|
56
|
+
|
|
57
|
+
# Implementation Plan — Bite-Sized Tasks
|
|
58
|
+
|
|
59
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development.
|
|
60
|
+
|
|
61
|
+
## File Structure
|
|
62
|
+
|
|
63
|
+
| File | Action | Responsibility |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `lib/milestone.js` | Modify | `writeSummary()` throws `ValidationError` if `key-decisions` missing or empty |
|
|
66
|
+
| `bin/commands/write-summary.js` | Modify | Catch `ValidationError`; print exact error message; `process.exit(2)` |
|
|
67
|
+
| `commands/cp/write-summary.md` (if exists) | Modify | Skill doc — note hard-block, recommend minimum 1 decision per plan |
|
|
68
|
+
| `test/unit-design.js` | Modify | Append validation tests (empty array, missing key, valid input, exit code via subprocess) |
|
|
69
|
+
| Existing v0.6 SUMMARY files | Backfill (.planning gitignored) | Add at least 1 `key-decisions` entry to each existing SUMMARY |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Task 1: Add validation in lib/milestone.writeSummary
|
|
74
|
+
|
|
75
|
+
**Files:**
|
|
76
|
+
- Modify: `lib/milestone.js`
|
|
77
|
+
- Modify: `test/unit-design.js`
|
|
78
|
+
|
|
79
|
+
- [ ] **Step 1: Inspect** — open `lib/milestone.js`, find `writeSummary` function. Note its exact signature and where it currently writes the YAML — validation must happen BEFORE write.
|
|
80
|
+
|
|
81
|
+
- [ ] **Step 2: Failing test** — append to `test/unit-design.js`:
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
section('lib/milestone: writeSummary validates key-decisions');
|
|
85
|
+
{
|
|
86
|
+
const milestoneLib = require('../lib/milestone');
|
|
87
|
+
const root = mktmp('ws-validate');
|
|
88
|
+
fs.mkdirSync(path.join(root, '.planning', 'phases', '50-test'), { recursive: true });
|
|
89
|
+
|
|
90
|
+
const empty = { phase: '50', plan: '01', 'key-decisions': [] };
|
|
91
|
+
let caught = null;
|
|
92
|
+
try { milestoneLib.writeSummary(root, '50-01', empty); }
|
|
93
|
+
catch (e) { caught = e; }
|
|
94
|
+
ok('empty key-decisions throws', caught !== null);
|
|
95
|
+
ok('error message mentions key-decisions',
|
|
96
|
+
caught && caught.message.includes("'key-decisions' is required"));
|
|
97
|
+
ok('error message references spec',
|
|
98
|
+
caught && caught.message.includes('docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md'));
|
|
99
|
+
|
|
100
|
+
const missing = { phase: '50', plan: '01' };
|
|
101
|
+
let caught2 = null;
|
|
102
|
+
try { milestoneLib.writeSummary(root, '50-01', missing); }
|
|
103
|
+
catch (e) { caught2 = e; }
|
|
104
|
+
ok('missing key-decisions throws', caught2 !== null);
|
|
105
|
+
|
|
106
|
+
const valid = { phase: '50', plan: '01', 'key-decisions': ['decision 1'] };
|
|
107
|
+
let caught3 = null;
|
|
108
|
+
try { milestoneLib.writeSummary(root, '50-01', valid); }
|
|
109
|
+
catch (e) { caught3 = e; }
|
|
110
|
+
ok('valid input does not throw', caught3 === null);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- [ ] **Step 3: Run** — `node test/unit-design.js` — expect new section fails.
|
|
115
|
+
|
|
116
|
+
- [ ] **Step 4: Implement validation** — in `lib/milestone.js`, add a `ValidationError` class (or use a tagged Error) and inject validation at the TOP of `writeSummary` body:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
class ValidationError extends Error {
|
|
120
|
+
constructor(message) { super(message); this.name = 'ValidationError'; this.code = 'EVALIDATION'; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// inside writeSummary, before any write:
|
|
124
|
+
const kd = data && data['key-decisions'];
|
|
125
|
+
if (!Array.isArray(kd) || kd.length === 0) {
|
|
126
|
+
throw new ValidationError(
|
|
127
|
+
"Error: 'key-decisions' is required and must have ≥1 entry. See spec at docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Export `ValidationError` from `lib/milestone.js` so the CLI handler can `instanceof`-check it.
|
|
133
|
+
|
|
134
|
+
- [ ] **Step 5: Run test** — `node test/unit-design.js` — expect all pass.
|
|
135
|
+
|
|
136
|
+
- [ ] **Step 6: Full suite** — `npm test`. If existing tests in `test/unit-libs.js`, `test/dryrun-*.js`, or `test/unit-lifecycle.js` call `writeSummary` with empty/missing key-decisions, those tests will FAIL. For each failing test, EITHER add a stub `'key-decisions': ['test decision']` to its input data, OR (if the test was specifically asserting empty-input behavior) update it to assert the new throw.
|
|
137
|
+
|
|
138
|
+
- [ ] **Step 7: Commit**
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
git add lib/milestone.js test/unit-design.js test/<any-other-modified-tests>
|
|
142
|
+
git commit -m "cp(16-03): writeSummary throws ValidationError on empty key-decisions
|
|
143
|
+
|
|
144
|
+
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Task 2: CLI handler exits 2 on validation error
|
|
150
|
+
|
|
151
|
+
**Files:**
|
|
152
|
+
- Modify: `bin/commands/write-summary.js`
|
|
153
|
+
- Modify: `test/unit-design.js`
|
|
154
|
+
|
|
155
|
+
- [ ] **Step 1: Read current handler** — open `bin/commands/write-summary.js`. Find where it calls `milestone.writeSummary(...)`. Wrap in try/catch.
|
|
156
|
+
|
|
157
|
+
- [ ] **Step 2: Add try/catch**
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
try {
|
|
161
|
+
milestone.writeSummary(root, planId, data);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
if (err && (err.name === 'ValidationError' || err.code === 'EVALIDATION')) {
|
|
164
|
+
process.stderr.write(err.message + '\n');
|
|
165
|
+
process.exit(2);
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- [ ] **Step 3: Subprocess test** — append to `test/unit-design.js`:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
section('CLI: cp write-summary exits 2 on empty key-decisions');
|
|
175
|
+
{
|
|
176
|
+
const { spawnSync } = require('child_process');
|
|
177
|
+
const root = mktmp('ws-cli');
|
|
178
|
+
fs.mkdirSync(path.join(root, '.planning', 'phases', '50-test'), { recursive: true });
|
|
179
|
+
const json = path.join(root, 'bad.json');
|
|
180
|
+
fs.writeFileSync(json, JSON.stringify({ phase: '50', plan: '01', 'key-decisions': [] }));
|
|
181
|
+
|
|
182
|
+
const cpBin = path.join(__dirname, '..', 'bin', 'cp.js');
|
|
183
|
+
const r = spawnSync(process.execPath, [cpBin, 'write-summary', '50-01', '--from', json], { cwd: root, encoding: 'utf8' });
|
|
184
|
+
ok('exit code is 2', r.status === 2);
|
|
185
|
+
ok('stderr includes key-decisions error',
|
|
186
|
+
(r.stderr || '').includes("'key-decisions' is required"));
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- [ ] **Step 4: Run** — `node test/unit-design.js` — expect all pass.
|
|
191
|
+
|
|
192
|
+
- [ ] **Step 5: Full suite** — `npm test`. All green.
|
|
193
|
+
|
|
194
|
+
- [ ] **Step 6: Commit**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
git add bin/commands/write-summary.js test/unit-design.js
|
|
198
|
+
git commit -m "cp(16-03): write-summary CLI exits 2 on ValidationError
|
|
199
|
+
|
|
200
|
+
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Task 3: Update cp-write-summary skill doc
|
|
206
|
+
|
|
207
|
+
**Files:**
|
|
208
|
+
- Modify: `commands/cp/write-summary.md` (if exists; else `.github/skills/cp-write-summary/SKILL.md`)
|
|
209
|
+
|
|
210
|
+
- [ ] **Step 1: Locate the skill** — `Get-ChildItem commands/cp/ | Where-Object Name -Match write-summary`.
|
|
211
|
+
|
|
212
|
+
- [ ] **Step 2: Add a "Required keys" section** at the top:
|
|
213
|
+
|
|
214
|
+
```markdown
|
|
215
|
+
## Required keys (v0.7 hard-block)
|
|
216
|
+
|
|
217
|
+
- `key-decisions`: **REQUIRED**. Array with ≥1 entry. Each entry is one
|
|
218
|
+
sentence describing a non-trivial decision made during the plan
|
|
219
|
+
(architecture, library choice, trade-off, deferred work, etc.).
|
|
220
|
+
Trivial / mechanical steps do not count.
|
|
221
|
+
|
|
222
|
+
The cp CLI exits with code 2 and prints the following exact message if
|
|
223
|
+
this constraint is violated:
|
|
224
|
+
|
|
225
|
+
Error: 'key-decisions' is required and must have ≥1 entry. See spec at
|
|
226
|
+
docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md
|
|
227
|
+
|
|
228
|
+
If a plan genuinely had no decisions worth recording (e.g. a typo fix),
|
|
229
|
+
note that explicitly: `key-decisions: ['mechanical edits only — no design decisions']`.
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
If the file doesn't exist, create it with that section plus a brief skeleton.
|
|
233
|
+
|
|
234
|
+
- [ ] **Step 3: Verify**
|
|
235
|
+
|
|
236
|
+
`node -e "const f=require('fs').readFileSync('commands/cp/write-summary.md','utf8'); if(!f.includes('Required keys')||!f.includes('key-decisions')){console.error('missing');process.exit(1);} console.log('OK');"`
|
|
237
|
+
|
|
238
|
+
- [ ] **Step 4: Commit**
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
git add commands/cp/write-summary.md
|
|
242
|
+
git commit -m "cp(16-03): document key-decisions hard-block in cp-write-summary skill
|
|
243
|
+
|
|
244
|
+
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Task 4: Backfill existing v0.6 SUMMARY files
|
|
250
|
+
|
|
251
|
+
**Files:** `.planning/phases/15-*/15-*-SUMMARY.md` and prior phases (10 files; all gitignored).
|
|
252
|
+
|
|
253
|
+
- [ ] **Step 1: List all existing SUMMARY files** — `Get-ChildItem .planning/phases -Recurse -Filter "*-SUMMARY.md" | Select-Object FullName`.
|
|
254
|
+
|
|
255
|
+
- [ ] **Step 2: For each file, check if `key-decisions:` is empty (`[]`) or missing**. If so, edit to add at least one entry summarizing the plan's actual decisions (look at the plan's commit messages and the SUMMARY body for context).
|
|
256
|
+
|
|
257
|
+
This is a manual / semi-manual task. A minimum acceptable entry is one sentence describing the plan's primary architectural or process decision. For purely mechanical plans (typo fix, version bump), use: `key-decisions: ['mechanical edits only — no design decisions']`.
|
|
258
|
+
|
|
259
|
+
- [ ] **Step 3: After backfill, verify by running** — `cp progress` (or scan summaries) — no SUMMARY file has empty `key-decisions`.
|
|
260
|
+
|
|
261
|
+
- [ ] **Step 4: No commit** — `.planning/` is gitignored. Document backfill in plan SUMMARY.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Task 5: Coverage gate + verification
|
|
266
|
+
|
|
267
|
+
- [ ] **Step 1:** `npm run coverage:ci` — exit 0; ≥85L / ≥75B.
|
|
268
|
+
|
|
269
|
+
- [ ] **Step 2: End-to-end verification** of v0.7:
|
|
270
|
+
- `node bin/cp.js scaffold-phase 99 --name smoke --dry-run` lists 4 actions
|
|
271
|
+
- `node bin/cp.js scaffold-milestone "v0.99 Smoke" --dry-run` lists 2 actions
|
|
272
|
+
- `echo '{"phase":"99","plan":"01"}' > bad.json; node bin/cp.js write-summary 99-01 --from bad.json` — exit 2, exact error message
|
|
273
|
+
- `echo '{"phase":"99","plan":"01","key-decisions":["dec1"]}' > good.json; node bin/cp.js write-summary 99-01 --from good.json` — exit 0
|
|
274
|
+
|
|
275
|
+
</tasks>
|
|
276
|
+
|
|
277
|
+
<verification>
|
|
278
|
+
- [ ] `npm test` — all 20 files `Failed: 0`
|
|
279
|
+
- [ ] `npm run coverage:ci` — ≥85L / ≥75B
|
|
280
|
+
- [ ] Empty key-decisions JSON → exit 2 + exact stderr message
|
|
281
|
+
- [ ] Valid key-decisions JSON → exit 0
|
|
282
|
+
- [ ] All existing SUMMARY files have ≥1 key-decisions entry
|
|
283
|
+
</verification>
|
|
284
|
+
|
|
285
|
+
<success_criteria>
|
|
286
|
+
- lib/milestone.writeSummary validates key-decisions; throws ValidationError
|
|
287
|
+
- bin/commands/write-summary.js catches and exits 2 with exact spec message
|
|
288
|
+
- cp-write-summary skill doc updated
|
|
289
|
+
- All v0.6 SUMMARYs backfilled
|
|
290
|
+
- v0.7 is feature-complete; ready for release
|
|
291
|
+
</success_criteria>
|
|
292
|
+
|
|
293
|
+
<output>
|
|
294
|
+
After completion: `cp write-summary 16-03 --from <json>` (with non-empty key-decisions!), then release v0.7.0.
|
|
295
|
+
</output>
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# v0.5 — Generic provider / harness detection
|
|
2
|
+
|
|
3
|
+
**Status:** Design (approved 2026-05-20)
|
|
4
|
+
**Milestone:** v0.5
|
|
5
|
+
**Brainstorm session:** 2026-05-20, paired with user @sli
|
|
6
|
+
**Spec author:** Copilot CLI (Claude Opus 4.7)
|
|
7
|
+
**Implementation provider:** Superpowers (`writing-plans` → `subagent-driven-development`)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Problem statement
|
|
12
|
+
|
|
13
|
+
`cp` (context-planning) v0.4.5 ships a working but fragile detection layer:
|
|
14
|
+
|
|
15
|
+
- Workflow provider detection is **literal string matching** against a 5-element list of hardcoded sentinel paths under 5 hardcoded base dirs.
|
|
16
|
+
- Only one real workflow provider (Superpowers) is shipped; the schema is "generic" in theory but never proved with a second provider.
|
|
17
|
+
- AI harnesses (Copilot CLI, Claude Code, Cursor, Aider) are not modeled at all — detection doesn't know which harness's plugin marketplace it's scanning, so:
|
|
18
|
+
- When a new harness adds a plugin marketplace at a new path, cp needs a code/template change.
|
|
19
|
+
- `cp doctor` can't tell the user *which* harness their Superpowers install came from.
|
|
20
|
+
- Brownfield `.planning/config.json` files snapshot the default sentinel list at init time and never refresh — so the v0.4.5 sentinel additions are invisible to existing projects.
|
|
21
|
+
|
|
22
|
+
The user-visible symptom that surfaced v0.4.5 (`cp doctor` reports Superpowers as missing under Copilot CLI) will keep recurring with every new harness or marketplace layout unless detection is restructured around an extensible model.
|
|
23
|
+
|
|
24
|
+
## 2. Goals
|
|
25
|
+
|
|
26
|
+
1. **Harnesses × providers cross-product detection.** A single config-only edit adds support for a new harness or a new provider; no `lib/*.js` changes required.
|
|
27
|
+
2. **Marketplace-wildcard support** (`installed-plugins/*/`) so forks of `superpowers-marketplace` (or any plugin distributor) work out of the box.
|
|
28
|
+
3. **`cp doctor` enumerates everything found** — all harnesses, all providers, where each was located, what the active configuration resolves to.
|
|
29
|
+
4. **Brownfield projects auto-heal** — first v0.5 invocation in a project init'd against v0.4.x silently merges new upstream defaults (sentinels, harnesses, new providers) into the local config and writes the result back, with a stderr notice.
|
|
30
|
+
5. **Schema generality proven** — ship a second built-in provider (a no-op `echo-provider` stub) so the assertion "the schema isn't Superpowers-shaped" is testable end-to-end.
|
|
31
|
+
|
|
32
|
+
## 3. Non-goals (v0.5)
|
|
33
|
+
|
|
34
|
+
- Full minimatch globbing (`**`, character classes). Trailing-`*` segment only.
|
|
35
|
+
- A real second workflow provider (BMAD / GSD-as-provider / etc.) — `echo-provider` is a schema test, not a product offering.
|
|
36
|
+
- `cp install <provider>` to auto-install Superpowers / others — out of scope.
|
|
37
|
+
- Phase-level workflow_provider override (e.g., "use Superpowers for plan, manual for execute").
|
|
38
|
+
- Telemetry / network calls — cp remains fully offline.
|
|
39
|
+
|
|
40
|
+
## 4. User-facing surface
|
|
41
|
+
|
|
42
|
+
### 4.1 Schema (`templates/config.json`, schema version 2)
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
"cp": {
|
|
46
|
+
"version": 2,
|
|
47
|
+
"workflow_provider": "superpowers",
|
|
48
|
+
|
|
49
|
+
"harnesses": {
|
|
50
|
+
"copilot": {
|
|
51
|
+
"description": "GitHub Copilot CLI",
|
|
52
|
+
"plugin_roots": ["~/.copilot/installed-plugins/*/"]
|
|
53
|
+
},
|
|
54
|
+
"claude": {
|
|
55
|
+
"description": "Claude Code",
|
|
56
|
+
"plugin_roots": ["~/.claude/plugins/*/", "~/.claude/skills/"]
|
|
57
|
+
},
|
|
58
|
+
"cursor": {
|
|
59
|
+
"description": "Cursor",
|
|
60
|
+
"plugin_roots": ["~/.cursor/extensions/*/"]
|
|
61
|
+
},
|
|
62
|
+
"aider": {
|
|
63
|
+
"description": "Aider (file-based, no plugin slot today)",
|
|
64
|
+
"plugin_roots": []
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
"providers": {
|
|
69
|
+
"superpowers": {
|
|
70
|
+
"description": "Jesse Vincent's Superpowers plugin (https://github.com/obra/superpowers)",
|
|
71
|
+
"plugin_shape": {
|
|
72
|
+
"dir_name": "superpowers",
|
|
73
|
+
"required_subdirs": [
|
|
74
|
+
"skills/writing-plans",
|
|
75
|
+
"skills/subagent-driven-development"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"detect": {
|
|
79
|
+
"any_of": [
|
|
80
|
+
".claude/plugins/superpowers",
|
|
81
|
+
".claude/skills/superpowers",
|
|
82
|
+
".github/skills/brainstorming",
|
|
83
|
+
".github/skills/writing-plans",
|
|
84
|
+
".github/skills/subagent-driven-development",
|
|
85
|
+
"installed-plugins/superpowers-marketplace/superpowers",
|
|
86
|
+
"installed-plugins/superpowers-marketplace/superpowers/skills/writing-plans",
|
|
87
|
+
"installed-plugins/superpowers-marketplace/superpowers/skills/subagent-driven-development"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
"skills": { "brainstorm": "brainstorming", "plan": "writing-plans", "execute": "subagent-driven-development", "execute_simple": "executing-plans", "review": "requesting-code-review", "receive_review": "receiving-code-review", "finish": "finishing-a-development-branch", "worktree": "using-git-worktrees", "tdd": "test-driven-development", "debug": "systematic-debugging", "verify": "verification-before-completion" }
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
"echo-provider": {
|
|
94
|
+
"description": "Schema-test stub. Echoes the role name. Not for end users.",
|
|
95
|
+
"plugin_shape": { "dir_name": "echo-provider", "required_subdirs": [] },
|
|
96
|
+
"skills": { "brainstorm": "echo", "plan": "echo", "execute": "echo", "execute_simple": "echo", "review": "echo", "receive_review": "echo", "finish": "echo", "worktree": "echo", "tdd": "echo", "debug": "echo", "verify": "echo" }
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
"manual": { "description": "Inline fallback...", "detect": { "always": true }, "skills": { ... unchanged ... }, "prompts": { ... unchanged ... } }
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
"behavior": { ... unchanged ... }
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Detection rule for a provider P:**
|
|
107
|
+
> Installed if either (a) for some harness H, expanding any of `H.plugin_roots` yields a child directory named `P.plugin_shape.dir_name` AND every entry in `P.plugin_shape.required_subdirs` exists under it; OR (b) any entry in `P.detect.any_of` is found via the legacy 5-base-dir search.
|
|
108
|
+
|
|
109
|
+
(a) is the preferred path. (b) is back-compat for hand-curated literal sentinels.
|
|
110
|
+
|
|
111
|
+
### 4.2 `cp doctor` output (sectioned)
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
cp v0.5.0
|
|
115
|
+
Repo root: C:\src\github\stock-analyze-hub\core-service-py
|
|
116
|
+
.planning/: present
|
|
117
|
+
Config: .planning\config.json (schema v2)
|
|
118
|
+
|
|
119
|
+
Harnesses detected:
|
|
120
|
+
✓ copilot ~/.copilot/installed-plugins/* (2 marketplaces, 4 plugins)
|
|
121
|
+
✓ claude ~/.claude/plugins/* (1 plugin), ~/.claude/skills/ (0 plugins)
|
|
122
|
+
✗ cursor (no plugins found at ~/.cursor/extensions/*)
|
|
123
|
+
— aider (file-based — no plugin slot)
|
|
124
|
+
|
|
125
|
+
Providers detected:
|
|
126
|
+
✓ superpowers via copilot @ installed-plugins/superpowers-marketplace/superpowers
|
|
127
|
+
via claude @ plugins/superpowers
|
|
128
|
+
✗ echo-provider (stub — install with `cp install echo-provider --local`)
|
|
129
|
+
✓ manual (always available)
|
|
130
|
+
|
|
131
|
+
Configured workflow_provider: superpowers [`cp config set workflow_provider <name>` to switch]
|
|
132
|
+
|
|
133
|
+
Roles → resolved skill:
|
|
134
|
+
✓ brainstorm → superpowers/brainstorming (via copilot)
|
|
135
|
+
✓ plan → superpowers/writing-plans (via copilot)
|
|
136
|
+
... (9 total)
|
|
137
|
+
|
|
138
|
+
GSD compatibility:
|
|
139
|
+
cp-aware config: ✓
|
|
140
|
+
shared files: .planning/PROJECT.md, .planning/ROADMAP.md, .planning/STATE.md, .planning/config.json
|
|
141
|
+
phase dirs: 6
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Flags**:
|
|
145
|
+
- `--json` emits the full `detectAllInstalled()` payload as machine-readable JSON (consumers: statusline, external tooling, CI).
|
|
146
|
+
- `--quiet` emits only the `Configured` line + role table (terse mode for CI / tight terminals).
|
|
147
|
+
|
|
148
|
+
**Exit codes**: `0` if configured provider resolves OR `behavior.fall_back_to_manual_if_provider_missing=true`; `1` otherwise.
|
|
149
|
+
|
|
150
|
+
### 4.3 `cp config refresh`
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
$ cp config refresh [--dry-run]
|
|
154
|
+
cp: would add 3 sentinels to providers.superpowers.detect.any_of
|
|
155
|
+
cp: would add provider 'echo-provider'
|
|
156
|
+
cp: would migrate schema v1 → v2
|
|
157
|
+
(no changes written — use without --dry-run to apply)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Idempotent. Second run is a no-op. Implementation: call `mergeCpDefaults` with `verbose: true`, print planned mutations, write back unless `--dry-run`.
|
|
161
|
+
|
|
162
|
+
### 4.4 Auto-heal at `loadConfig` time
|
|
163
|
+
|
|
164
|
+
First v0.5 invocation in any project with a `.planning/config.json` that lacks new defaults (sentinels, harnesses, new providers, schema bump) **silently merges and writes back**, with one stderr line:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
cp: refreshed .planning/config.json with 3 new sentinels, 1 new provider (echo-provider), schema v1 → v2
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Subsequent invocations are no-ops (idempotent merge).
|
|
171
|
+
|
|
172
|
+
## 5. Internal architecture
|
|
173
|
+
|
|
174
|
+
### 5.1 New module: `lib/detect.js` (~250 LOC target)
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
// Public API (also re-exported through lib/provider.js for back-compat)
|
|
178
|
+
detectAllInstalled(cfg) → DetectionReport
|
|
179
|
+
detectProviderAtAnyHarness(cfg, providerName) → ProviderHit | { installed: false }
|
|
180
|
+
expandRoot(rootSpec) → string[] // trailing-* glob expansion with tilde
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
type DetectionReport = {
|
|
185
|
+
harnesses: HarnessReport[],
|
|
186
|
+
providers: ProviderReport[],
|
|
187
|
+
}
|
|
188
|
+
type HarnessReport = {
|
|
189
|
+
name: string,
|
|
190
|
+
configured: boolean,
|
|
191
|
+
scannedRoots: { root: string, expanded: string[] }[],
|
|
192
|
+
pluginCount: number,
|
|
193
|
+
}
|
|
194
|
+
type ProviderReport = {
|
|
195
|
+
name: string,
|
|
196
|
+
installed: boolean,
|
|
197
|
+
hits: ProviderHit[], // empty if installed=false
|
|
198
|
+
}
|
|
199
|
+
type ProviderHit = {
|
|
200
|
+
via: string, // harness name, or '_anywhere' for legacy literal match
|
|
201
|
+
source: 'plugin_shape' | 'literal' | 'always',
|
|
202
|
+
evidence: string, // absolute path that satisfied detection
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Pure functions** — no I/O caching, no module-level state. Filesystem reads happen at call time. Tests monkey-patch `os.homedir` (same pattern as v0.4.5).
|
|
207
|
+
|
|
208
|
+
**`expandRoot` algorithm**:
|
|
209
|
+
1. Replace leading `~/` with `os.homedir() + '/'`. No env-var expansion.
|
|
210
|
+
2. If no `*` in the path → return `[absPath]` if exists else `[]`.
|
|
211
|
+
3. Else split on first `*`-segment, `readdirSync(parent)`, filter to dirs, append rest of path to each, recurse.
|
|
212
|
+
4. Trailing slashes normalized to platform sep.
|
|
213
|
+
|
|
214
|
+
### 5.2 Slimmed `lib/provider.js` (~100 LOC target, was 145)
|
|
215
|
+
|
|
216
|
+
Keeps:
|
|
217
|
+
- `loadConfig(root)`, `saveConfig(cfg, root)`, `configPath(root)`, `loadDefaults()`
|
|
218
|
+
- `cpGet(cfg, dotted, fallback)`, `cpSet(cfg, dotted, value)`
|
|
219
|
+
- `resolveSkill(role, root)` — now a thin wrapper that calls `detect.detectProviderAtAnyHarness` then maps to a skill name.
|
|
220
|
+
- `resolvePrompt(role, root)` — unchanged.
|
|
221
|
+
|
|
222
|
+
Removes (moves to `lib/detect.js`):
|
|
223
|
+
- `existsAnywhere(candidate)`
|
|
224
|
+
- `detectProvider(cfg, name)` — re-exported from detect.js as `detectProviderAtAnyHarness` for clarity; old export name kept as alias for one minor version.
|
|
225
|
+
|
|
226
|
+
### 5.3 New module: `lib/merge.js` (~150 LOC target)
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
mergeCpDefaults(raw, defaults, { verbose: false }) → { cfg, changed, summary, plannedChanges }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Pure function. No I/O. Caller decides whether to write the result.
|
|
233
|
+
|
|
234
|
+
**Merge rules** (purely additive — never delete user data):
|
|
235
|
+
|
|
236
|
+
| Key | Rule |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `cp.version` | `Math.max(raw, defaults)` |
|
|
239
|
+
| `cp.workflow_provider` | user wins (never overwrite) |
|
|
240
|
+
| `cp.harnesses[H]` | add missing H entirely; for existing H, deep-merge keys |
|
|
241
|
+
| `cp.harnesses[H].plugin_roots` | union (dedupe, preserve order) |
|
|
242
|
+
| `cp.providers[P]` | add missing P entirely |
|
|
243
|
+
| `cp.providers[P].detect.any_of` | union (dedupe) |
|
|
244
|
+
| `cp.providers[P].plugin_shape` | add if missing; never overwrite |
|
|
245
|
+
| `cp.providers[P].skills[role]` | add if missing; user wins on conflict |
|
|
246
|
+
| `cp.providers[P].prompts[role]` | add if missing; user wins on conflict |
|
|
247
|
+
| `cp.behavior[K]` | add if missing; user wins on conflict |
|
|
248
|
+
|
|
249
|
+
`summary` is a short human-readable string composed from `plannedChanges`. Example: `"3 new sentinels, 1 new provider (echo-provider), schema v1 → v2"`.
|
|
250
|
+
|
|
251
|
+
### 5.4 `loadConfig` change
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
function loadConfig(root) {
|
|
255
|
+
const p = configPath(root);
|
|
256
|
+
const defaults = loadDefaults();
|
|
257
|
+
if (!fs.existsSync(p)) return defaults;
|
|
258
|
+
|
|
259
|
+
const raw = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
260
|
+
|
|
261
|
+
if (!raw.cp) { // existing pure-GSD path
|
|
262
|
+
raw.cp = defaults.cp;
|
|
263
|
+
fs.writeFileSync(p, JSON.stringify(raw, null, 2) + '\n');
|
|
264
|
+
return raw;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const merged = mergeCpDefaults(raw, defaults);
|
|
268
|
+
if (merged.changed) {
|
|
269
|
+
fs.writeFileSync(p, JSON.stringify(merged.cfg, null, 2) + '\n');
|
|
270
|
+
process.stderr.write(`cp: refreshed .planning/config.json with ${merged.summary}\n`);
|
|
271
|
+
}
|
|
272
|
+
return merged.cfg;
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Stderr write is intentional — `cp doctor`'s output is parsed in tests / status-line; mutating stdout would break them. Stderr is the right channel for advisory notices.
|
|
277
|
+
|
|
278
|
+
### 5.5 `bin/cp.js` additions
|
|
279
|
+
|
|
280
|
+
- `cp doctor` rewritten to consume `detect.detectAllInstalled` and emit the sectioned format. New flags: `--json`, `--quiet`.
|
|
281
|
+
- `cp config refresh [--dry-run]` — new subcommand. Reads config, calls `mergeCpDefaults(verbose:true)`, prints planned changes, writes unless `--dry-run`.
|
|
282
|
+
- `cp install echo-provider --local` — new flag on existing `install` dispatcher. Plants `~/.cp/providers/echo-provider/skills/echo/SKILL.md` (single file, ~5 lines). Pure local install, no network.
|
|
283
|
+
|
|
284
|
+
## 6. Error handling
|
|
285
|
+
|
|
286
|
+
| Scenario | Behavior |
|
|
287
|
+
|---|---|
|
|
288
|
+
| Malformed `~/.copilot/installed-plugins/<x>` (file instead of dir) | Skipped by `readdirSync` + dir filter; no error surfaced |
|
|
289
|
+
| `cp.harnesses[H].plugin_roots` entry has multiple `*` | Each `*` segment expanded recursively; works as expected |
|
|
290
|
+
| `~/` expansion when `os.homedir()` returns empty | Skip the root; log a warning to stderr via `cp doctor` (once per invocation) |
|
|
291
|
+
| User-edited `cp.providers[P].plugin_shape.required_subdirs` is not an array | `detectProviderAtAnyHarness` returns `{installed: false, reason: 'malformed plugin_shape'}` and `cp doctor` shows `✗ P (config error: ...)` instead of crashing |
|
|
292
|
+
| `mergeCpDefaults` encounters a value-type conflict (e.g., user set `plugin_roots: "string"` instead of array) | User value preserved; one stderr warning; merge continues for other keys |
|
|
293
|
+
| `cp config refresh` against a missing `.planning/config.json` | Exit 1 with `cp: no .planning/config.json found — run \`cp init\` first` |
|
|
294
|
+
|
|
295
|
+
No `try/catch` around `fs.readFileSync` for config — let the parse error propagate (matches v0.4.x behavior). The merge function is wrapped in try/catch only at the `loadConfig` boundary; on merge failure, log to stderr and return the unmerged config (don't block the command).
|
|
296
|
+
|
|
297
|
+
## 7. Testing strategy
|
|
298
|
+
|
|
299
|
+
### Test fixtures
|
|
300
|
+
|
|
301
|
+
| Fixture name | Purpose |
|
|
302
|
+
|---|---|
|
|
303
|
+
| `host-copilot-only` | `~/.copilot/installed-plugins/superpowers-marketplace/superpowers/` exists with required subdirs |
|
|
304
|
+
| `host-claude-only` | `~/.claude/plugins/superpowers/` exists |
|
|
305
|
+
| `host-both` | Both layouts present (multi-harness detection) |
|
|
306
|
+
| `host-neither` | Empty home — manual fallback only |
|
|
307
|
+
| `host-stub-installed` | `~/.cp/providers/echo-provider/skills/echo/` exists |
|
|
308
|
+
| `host-malformed` | `~/.copilot/installed-plugins/junk-marketplace/superpowers/` is a file, not dir |
|
|
309
|
+
|
|
310
|
+
### Test files
|
|
311
|
+
|
|
312
|
+
| File | Coverage | Assertions |
|
|
313
|
+
|---|---|---|
|
|
314
|
+
| `test/unit-detect.js` (NEW) | `expandRoot` (10 cases incl. tilde, missing parent, mixed literals, multi-`*`); `detectProviderAtAnyHarness` against all 6 host fixtures; back-compat with legacy `any_of` literals; full `detectAllInstalled` shape check | ~40 |
|
|
315
|
+
| `test/unit-merge.js` (NEW) | Each merge rule (~11) × edge cases (user-empty / user-extra / user-conflict / schema v1→v2 / type mismatch); idempotency (apply twice == apply once); summary string format | ~50 |
|
|
316
|
+
| `test/dryrun-doctor.js` (NEW) | Spawn `bin/cp.js doctor` against temp roots, assert output sections; `--json` shape; `--quiet` shape; exit codes for missing-provider + fallback-disabled | ~25 |
|
|
317
|
+
| `test/dryrun-config-refresh.js` (NEW) | Brownfield fixtures (v0.4.4 config / v0.3.x config / pure-GSD / hand-rolled / empty-cp) go through refresh; assert diff; assert second run is no-op | ~25 |
|
|
318
|
+
| `test/unit-libs.js` (EXTEND) | Keep v0.4.5 sections; add smoke test that `provider.resolveSkill` still works through the new path | ~5 |
|
|
319
|
+
|
|
320
|
+
Total new: ~145. Baseline 751 → ~895.
|
|
321
|
+
|
|
322
|
+
All new tests follow the v0.4.5 isolation pattern: monkey-patch `os.homedir()` to a temp dir so the host machine state doesn't leak.
|
|
323
|
+
|
|
324
|
+
### Manual / dogfood verification
|
|
325
|
+
|
|
326
|
+
- Run `cp doctor` in this repo (`context-planning`) → expect all 4 harnesses, both superpowers + echo-provider listed.
|
|
327
|
+
- Run `cp doctor` in `StockAnalyzer/core-service-py` → expect identical output (proves brownfield auto-heal worked).
|
|
328
|
+
- Remove the local `installed-plugins/...` override from StockAnalyzer's config, run `cp doctor` again → auto-heal restores it, detection still works.
|
|
329
|
+
- `cp config refresh --dry-run` in a freshly init'd v0.4.x project → expect non-empty diff; without `--dry-run` → first run mutates, second run is no-op.
|
|
330
|
+
|
|
331
|
+
## 8. Rollout plan
|
|
332
|
+
|
|
333
|
+
Each phase ships as an internal pre-release; the final tag is `v0.5.0`.
|
|
334
|
+
|
|
335
|
+
| Phase | Tag | Scope | Ships when |
|
|
336
|
+
|---|---|---|---|
|
|
337
|
+
| 1. Schema + detection core | v0.5.0-alpha | Templates updated; `lib/detect.js` lands; `lib/provider.js` slimmed; existing tests still pass; `cp doctor` output unchanged externally (uses old code path) | `test/unit-detect.js` green |
|
|
338
|
+
| 2. `cp doctor` rewrite | v0.5.0-beta | Sectioned output; `--json`/`--quiet` flags; `test/dryrun-doctor.js` green | Manual eyeball OK |
|
|
339
|
+
| 3. Auto-heal + `cp config refresh` | v0.5.0-rc | `lib/merge.js` lands; `loadConfig` writes back on first encounter; new subcommand; `test/unit-merge.js` + `test/dryrun-config-refresh.js` green | Brownfield fixtures verified by hand |
|
|
340
|
+
| 4. Echo-provider stub + installer | v0.5.0 | `install/echo-provider.js` plants local files; `cp doctor` shows echo as detected after install; README example | End-to-end "switch workflow_provider to echo-provider" demo works |
|
|
341
|
+
| 5. Migration doc + CHANGELOG (no version) | — | `docs/MIGRATION-v0.5.md` (before/after configs); CHANGELOG; README update; GitHub release with summary | All above shipped |
|
|
342
|
+
|
|
343
|
+
## 9. Risks
|
|
344
|
+
|
|
345
|
+
| Risk | Likelihood | Mitigation |
|
|
346
|
+
|---|---|---|
|
|
347
|
+
| First-run auto-heal mutates user's config without their knowing | Medium | Loud stderr message; `cp config refresh --dry-run` documented in v0.4.5 → v0.5 upgrade notes; auto-heal is purely additive so worst case is extra entries the user can prune |
|
|
348
|
+
| Trailing-`*` semantics confuse users expecting `**` | Low | Documented explicitly in `cp doctor --help` and migration doc; trailing-`*` covers all known real-world cases |
|
|
349
|
+
| `cp.harnesses` block under `cp.*` confuses GSD users | Very low | GSD ignores unknown keys under the `cp` block by design (existing convention) |
|
|
350
|
+
| Echo-provider sets a precedent users misuse as a real provider | Low | `description` says "Not for end users"; `cp doctor` doesn't recommend switching to it; README example labels it a schema test |
|
|
351
|
+
| `~/.cp/providers/` directory convention conflicts with future cp-managed user state | Low | Document the convention in v0.5; if v0.6 needs cp-managed user state, put it under `~/.cp/state/` or `~/.cp/cache/` |
|
|
352
|
+
|
|
353
|
+
## 10. Out of scope (parked for v0.6+)
|
|
354
|
+
|
|
355
|
+
- Full minimatch globbing (`**`, character classes)
|
|
356
|
+
- A real second workflow provider (BMAD / GSD-as-provider integration)
|
|
357
|
+
- `cp install superpowers` (auto-install Superpowers via npm / curl)
|
|
358
|
+
- Phase-level workflow_provider override
|
|
359
|
+
- Telemetry / first-run survey
|
|
360
|
+
- Multi-OS testing matrix in CI (currently zero CI; that's a separate concern in `.planning/codebase/CONCERNS.md`)
|
|
361
|
+
- Refactoring `bin/cp.js` LOC growth (1100+ lines, hand-rolled argv per handler — open MEDIUM concern, not v0.5)
|
|
362
|
+
|
|
363
|
+
## 11. Open questions
|
|
364
|
+
|
|
365
|
+
None at spec-write time. All scoping decisions captured in the Q1-Q5 interactive brainstorm:
|
|
366
|
+
|
|
367
|
+
| Q | Decision |
|
|
368
|
+
|---|---|
|
|
369
|
+
| Harness scope | All four (Copilot, Claude, Cursor, Aider) modeled in schema |
|
|
370
|
+
| Glob support | Trailing-`*` segment only (zero-deps) |
|
|
371
|
+
| `cp doctor` output | Sectioned (full enumeration) |
|
|
372
|
+
| Brownfield merge | Auto-write on first load + explicit `cp config refresh` |
|
|
373
|
+
| Second provider | Schema-test stub (`echo-provider`) only |
|
|
374
|
+
|
|
375
|
+
## 12. References
|
|
376
|
+
|
|
377
|
+
- Triggering bug: `cp doctor` reports Superpowers missing under Copilot CLI marketplace install (fixed as a band-aid in v0.4.5, full structural fix is this milestone).
|
|
378
|
+
- Predecessor commit: `a168fbd` — `fix(v0.4.5): detect Superpowers via Copilot CLI marketplace install`
|
|
379
|
+
- Brownfield issue surfaced during v0.4.5 verification: `loadConfig` only merges defaults when `cp` block is entirely missing — root cause of the auto-heal requirement in this design.
|
|
380
|
+
- Architecture principle drawn from existing PROJECT.md: "Node-only runtime: keep zero-deps in `bin/`/`lib/`" — gates Q2 against minimatch.
|