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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +454 -0
  3. package/bin/commands/_helpers.js +53 -0
  4. package/bin/commands/_usage.js +67 -0
  5. package/bin/commands/capture.js +46 -0
  6. package/bin/commands/codebase-status.js +41 -0
  7. package/bin/commands/complete-milestone.js +57 -0
  8. package/bin/commands/config.js +70 -0
  9. package/bin/commands/doctor.js +139 -0
  10. package/bin/commands/gsd-import.js +90 -0
  11. package/bin/commands/inbox.js +81 -0
  12. package/bin/commands/index.js +33 -0
  13. package/bin/commands/init.js +87 -0
  14. package/bin/commands/install.js +43 -0
  15. package/bin/commands/scaffold-codebase.js +53 -0
  16. package/bin/commands/scaffold-milestone.js +58 -0
  17. package/bin/commands/scaffold-phase.js +65 -0
  18. package/bin/commands/status.js +42 -0
  19. package/bin/commands/statusline.js +108 -0
  20. package/bin/commands/tick.js +49 -0
  21. package/bin/commands/version.js +9 -0
  22. package/bin/commands/worktree.js +218 -0
  23. package/bin/commands/write-summary.js +54 -0
  24. package/bin/cp.cmd +2 -0
  25. package/bin/cp.js +54 -0
  26. package/commands/cp/capture.md +107 -0
  27. package/commands/cp/complete-milestone.md +166 -0
  28. package/commands/cp/execute-phase.md +220 -0
  29. package/commands/cp/map-codebase.md +211 -0
  30. package/commands/cp/new-milestone.md +136 -0
  31. package/commands/cp/new-project.md +132 -0
  32. package/commands/cp/plan-phase.md +195 -0
  33. package/commands/cp/progress.md +147 -0
  34. package/commands/cp/quick.md +104 -0
  35. package/commands/cp/resume.md +125 -0
  36. package/commands/cp/write-summary.md +33 -0
  37. package/docs/MIGRATION-v0.5.md +140 -0
  38. package/docs/architecture.md +189 -0
  39. package/docs/superpowers/plans/2026-05-20-v0-7-plan-16-01-design-md-infrastructure.md +1064 -0
  40. package/docs/superpowers/plans/2026-05-20-v0-7-plan-16-02-review-log-infrastructure.md +418 -0
  41. package/docs/superpowers/plans/2026-05-20-v0-7-plan-16-03-key-decisions-hard-block.md +295 -0
  42. package/docs/superpowers/specs/2026-05-20-generic-provider-harness-detection-design.md +380 -0
  43. package/docs/superpowers/specs/2026-05-20-v0-7-design-capture-design.md +400 -0
  44. package/docs/writing-providers.md +76 -0
  45. package/install/aider.js +204 -0
  46. package/install/claude.js +116 -0
  47. package/install/common.js +65 -0
  48. package/install/copilot.js +86 -0
  49. package/install/cursor.js +120 -0
  50. package/install/echo-provider.js +50 -0
  51. package/lib/codebase-mapper.js +169 -0
  52. package/lib/detect.js +280 -0
  53. package/lib/frontmatter.js +72 -0
  54. package/lib/gsd-compat.js +165 -0
  55. package/lib/import.js +543 -0
  56. package/lib/inbox.js +226 -0
  57. package/lib/lifecycle.js +929 -0
  58. package/lib/merge.js +157 -0
  59. package/lib/milestone.js +595 -0
  60. package/lib/paths.js +191 -0
  61. package/lib/provider.js +168 -0
  62. package/lib/roadmap.js +134 -0
  63. package/lib/state.js +99 -0
  64. package/lib/worktree.js +253 -0
  65. package/package.json +45 -0
  66. package/templates/DESIGN.md +78 -0
  67. package/templates/INBOX.md +13 -0
  68. package/templates/MILESTONE-CONTEXT.md +40 -0
  69. package/templates/MILESTONES.md +29 -0
  70. package/templates/PLAN.md +84 -0
  71. package/templates/PROJECT.md +43 -0
  72. package/templates/REVIEW-LOG.md +38 -0
  73. package/templates/ROADMAP.md +34 -0
  74. package/templates/STATE.md +78 -0
  75. package/templates/SUMMARY.md +75 -0
  76. package/templates/codebase/ARCHITECTURE.md +30 -0
  77. package/templates/codebase/CONCERNS.md +30 -0
  78. package/templates/codebase/CONVENTIONS.md +30 -0
  79. package/templates/codebase/INTEGRATIONS.md +30 -0
  80. package/templates/codebase/STACK.md +26 -0
  81. package/templates/codebase/STRUCTURE.md +32 -0
  82. package/templates/codebase/TESTING.md +39 -0
  83. package/templates/config.json +173 -0
  84. package/templates/phase-PLAN.md +32 -0
  85. package/templates/quick-PLAN.md +24 -0
  86. 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 };