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
package/lib/merge.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Additive config merge for brownfield auto-heal (v0.5).
|
|
5
|
+
*
|
|
6
|
+
* When a project's `.planning/config.json` was init'd against an older
|
|
7
|
+
* template (e.g., v0.4.x), `mergeCpDefaults` silently augments it with
|
|
8
|
+
* any new upstream keys (harnesses, providers, sentinels, schema bump)
|
|
9
|
+
* without overwriting user-customised values.
|
|
10
|
+
*
|
|
11
|
+
* Rules:
|
|
12
|
+
* - Never delete user data.
|
|
13
|
+
* - User values win on key conflicts.
|
|
14
|
+
* - Arrays (like detect.any_of and plugin_roots) are unioned (dedupe).
|
|
15
|
+
* - Objects are deep-merged with user-wins precedence.
|
|
16
|
+
* - cp.version is set to max(user, defaults).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Merge upstream cp defaults into a user config.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} raw - The full parsed config.json (with .cp block).
|
|
23
|
+
* @param {object} defaults - The full default config.json from templates/.
|
|
24
|
+
* @param {object} opts - { verbose: false }
|
|
25
|
+
* @returns {{ cfg: object, changed: boolean, summary: string, plannedChanges: string[] }}
|
|
26
|
+
*/
|
|
27
|
+
function mergeCpDefaults(raw, defaults, opts = {}) {
|
|
28
|
+
const user = raw.cp || {};
|
|
29
|
+
const def = defaults.cp || {};
|
|
30
|
+
const changes = [];
|
|
31
|
+
|
|
32
|
+
// Deep clone raw to avoid mutating the input
|
|
33
|
+
const cfg = JSON.parse(JSON.stringify(raw));
|
|
34
|
+
cfg.cp = cfg.cp || {};
|
|
35
|
+
|
|
36
|
+
// 1. Schema version: max(user, defaults)
|
|
37
|
+
const userVersion = typeof cfg.cp.version === 'number' ? cfg.cp.version : 1;
|
|
38
|
+
const defVersion = typeof def.version === 'number' ? def.version : 1;
|
|
39
|
+
if (defVersion > userVersion) {
|
|
40
|
+
cfg.cp.version = defVersion;
|
|
41
|
+
changes.push(`schema v${userVersion} → v${defVersion}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. workflow_provider: user wins (never overwrite)
|
|
45
|
+
if (!cfg.cp.workflow_provider && def.workflow_provider) {
|
|
46
|
+
cfg.cp.workflow_provider = def.workflow_provider;
|
|
47
|
+
changes.push(`set workflow_provider to '${def.workflow_provider}'`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 3. Harnesses: add missing, deep-merge existing
|
|
51
|
+
if (def.harnesses) {
|
|
52
|
+
cfg.cp.harnesses = cfg.cp.harnesses || {};
|
|
53
|
+
for (const [name, defHarness] of Object.entries(def.harnesses)) {
|
|
54
|
+
if (!cfg.cp.harnesses[name]) {
|
|
55
|
+
cfg.cp.harnesses[name] = JSON.parse(JSON.stringify(defHarness));
|
|
56
|
+
changes.push(`new harness '${name}'`);
|
|
57
|
+
} else {
|
|
58
|
+
// Deep-merge: user keys win, union plugin_roots
|
|
59
|
+
const userH = cfg.cp.harnesses[name];
|
|
60
|
+
if (!userH.description && defHarness.description) {
|
|
61
|
+
userH.description = defHarness.description;
|
|
62
|
+
}
|
|
63
|
+
if (defHarness.plugin_roots) {
|
|
64
|
+
userH.plugin_roots = unionArrays(
|
|
65
|
+
userH.plugin_roots || [],
|
|
66
|
+
defHarness.plugin_roots
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 4. Providers: add missing blocks, merge detect.any_of + plugin_shape + skills
|
|
74
|
+
if (def.providers) {
|
|
75
|
+
cfg.cp.providers = cfg.cp.providers || {};
|
|
76
|
+
for (const [name, defProvider] of Object.entries(def.providers)) {
|
|
77
|
+
if (!cfg.cp.providers[name]) {
|
|
78
|
+
cfg.cp.providers[name] = JSON.parse(JSON.stringify(defProvider));
|
|
79
|
+
changes.push(`new provider '${name}'`);
|
|
80
|
+
} else {
|
|
81
|
+
const userP = cfg.cp.providers[name];
|
|
82
|
+
|
|
83
|
+
// detect.any_of: union (dedupe)
|
|
84
|
+
if (defProvider.detect && defProvider.detect.any_of) {
|
|
85
|
+
userP.detect = userP.detect || {};
|
|
86
|
+
const before = (userP.detect.any_of || []).length;
|
|
87
|
+
userP.detect.any_of = unionArrays(
|
|
88
|
+
userP.detect.any_of || [],
|
|
89
|
+
defProvider.detect.any_of
|
|
90
|
+
);
|
|
91
|
+
const added = userP.detect.any_of.length - before;
|
|
92
|
+
if (added > 0) {
|
|
93
|
+
changes.push(`${added} new sentinel(s) for '${name}'`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// plugin_shape: add if missing, never overwrite
|
|
98
|
+
if (defProvider.plugin_shape && !userP.plugin_shape) {
|
|
99
|
+
userP.plugin_shape = JSON.parse(JSON.stringify(defProvider.plugin_shape));
|
|
100
|
+
changes.push(`plugin_shape for '${name}'`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// skills: add missing keys, user wins on conflict
|
|
104
|
+
if (defProvider.skills) {
|
|
105
|
+
userP.skills = userP.skills || {};
|
|
106
|
+
for (const [role, defSkill] of Object.entries(defProvider.skills)) {
|
|
107
|
+
if (!(role in userP.skills)) {
|
|
108
|
+
userP.skills[role] = defSkill;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// prompts: add missing keys, user wins on conflict
|
|
114
|
+
if (defProvider.prompts) {
|
|
115
|
+
userP.prompts = userP.prompts || {};
|
|
116
|
+
for (const [role, defPrompt] of Object.entries(defProvider.prompts)) {
|
|
117
|
+
if (!(role in userP.prompts)) {
|
|
118
|
+
userP.prompts[role] = defPrompt;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 5. Behavior: add missing keys, user wins
|
|
127
|
+
if (def.behavior) {
|
|
128
|
+
cfg.cp.behavior = cfg.cp.behavior || {};
|
|
129
|
+
for (const [key, defVal] of Object.entries(def.behavior)) {
|
|
130
|
+
if (!(key in cfg.cp.behavior)) {
|
|
131
|
+
cfg.cp.behavior[key] = defVal;
|
|
132
|
+
changes.push(`behavior.${key} = ${defVal}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const changed = changes.length > 0;
|
|
138
|
+
const summary = changed ? changes.join(', ') : 'already up to date';
|
|
139
|
+
return { cfg, changed, summary, plannedChanges: changes };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Union two arrays, deduping by JSON stringification for primitives.
|
|
144
|
+
*/
|
|
145
|
+
function unionArrays(a, b) {
|
|
146
|
+
const set = new Set(a.map(String));
|
|
147
|
+
const result = [...a];
|
|
148
|
+
for (const item of b) {
|
|
149
|
+
if (!set.has(String(item))) {
|
|
150
|
+
set.add(String(item));
|
|
151
|
+
result.push(item);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = { mergeCpDefaults, unionArrays };
|