@bhargavvc/sdd-cc 1.30.0 → 1.35.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 (242) hide show
  1. package/README.ja-JP.md +144 -110
  2. package/README.ko-KR.md +143 -107
  3. package/README.md +183 -112
  4. package/README.pt-BR.md +90 -52
  5. package/README.zh-CN.md +141 -101
  6. package/agents/sdd-advisor-researcher.md +23 -0
  7. package/agents/sdd-ai-researcher.md +133 -0
  8. package/agents/sdd-code-fixer.md +516 -0
  9. package/agents/sdd-code-reviewer.md +355 -0
  10. package/agents/sdd-codebase-mapper.md +3 -3
  11. package/agents/sdd-debugger.md +17 -5
  12. package/agents/sdd-doc-verifier.md +201 -0
  13. package/agents/sdd-doc-writer.md +602 -0
  14. package/agents/sdd-domain-researcher.md +153 -0
  15. package/agents/sdd-eval-auditor.md +164 -0
  16. package/agents/sdd-eval-planner.md +154 -0
  17. package/agents/sdd-executor.md +87 -4
  18. package/agents/sdd-framework-selector.md +160 -0
  19. package/agents/sdd-intel-updater.md +314 -0
  20. package/agents/sdd-nyquist-auditor.md +1 -1
  21. package/agents/sdd-phase-researcher.md +71 -4
  22. package/agents/sdd-plan-checker.md +100 -6
  23. package/agents/sdd-planner.md +145 -206
  24. package/agents/sdd-project-researcher.md +25 -2
  25. package/agents/sdd-research-synthesizer.md +3 -3
  26. package/agents/sdd-roadmapper.md +6 -6
  27. package/agents/sdd-security-auditor.md +128 -0
  28. package/agents/sdd-ui-auditor.md +43 -3
  29. package/agents/sdd-ui-checker.md +5 -5
  30. package/agents/sdd-ui-researcher.md +27 -4
  31. package/agents/sdd-user-profiler.md +2 -2
  32. package/agents/sdd-verifier.md +142 -22
  33. package/bin/install.js +2151 -551
  34. package/commands/sdd/add-backlog.md +5 -5
  35. package/commands/sdd/add-tests.md +2 -2
  36. package/commands/sdd/ai-integration-phase.md +36 -0
  37. package/commands/sdd/analyze-dependencies.md +34 -0
  38. package/commands/sdd/audit-fix.md +33 -0
  39. package/commands/sdd/autonomous.md +7 -2
  40. package/commands/sdd/cleanup.md +5 -0
  41. package/commands/sdd/code-review-fix.md +52 -0
  42. package/commands/sdd/code-review.md +55 -0
  43. package/commands/sdd/complete-milestone.md +6 -6
  44. package/commands/sdd/debug.md +22 -9
  45. package/commands/sdd/discuss-phase.md +7 -2
  46. package/commands/sdd/do.md +1 -1
  47. package/commands/sdd/docs-update.md +48 -0
  48. package/commands/sdd/eval-review.md +32 -0
  49. package/commands/sdd/execute-phase.md +4 -0
  50. package/commands/sdd/explore.md +27 -0
  51. package/commands/sdd/fast.md +2 -2
  52. package/commands/sdd/from-sdd2.md +45 -0
  53. package/commands/sdd/help.md +2 -0
  54. package/commands/sdd/import.md +36 -0
  55. package/commands/sdd/intel.md +179 -0
  56. package/commands/sdd/join-discord.md +2 -1
  57. package/commands/sdd/manager.md +1 -0
  58. package/commands/sdd/map-codebase.md +3 -3
  59. package/commands/sdd/new-milestone.md +1 -1
  60. package/commands/sdd/new-project.md +5 -1
  61. package/commands/sdd/new-workspace.md +1 -1
  62. package/commands/sdd/next.md +2 -0
  63. package/commands/sdd/plan-milestone-gaps.md +2 -2
  64. package/commands/sdd/plan-phase.md +6 -1
  65. package/commands/sdd/plant-seed.md +1 -1
  66. package/commands/sdd/profile-user.md +1 -1
  67. package/commands/sdd/quick.md +5 -3
  68. package/commands/sdd/reapply-patches.md +230 -42
  69. package/commands/sdd/research-phase.md +3 -3
  70. package/commands/sdd/review-backlog.md +1 -0
  71. package/commands/sdd/review.md +6 -3
  72. package/commands/sdd/scan.md +26 -0
  73. package/commands/sdd/secure-phase.md +35 -0
  74. package/commands/sdd/ship.md +1 -1
  75. package/commands/sdd/thread.md +5 -5
  76. package/commands/sdd/undo.md +34 -0
  77. package/commands/sdd/verify-work.md +1 -1
  78. package/commands/sdd/workstreams.md +17 -11
  79. package/hooks/dist/sdd-check-update.js +33 -8
  80. package/hooks/dist/sdd-context-monitor.js +17 -8
  81. package/hooks/dist/sdd-phase-boundary.sh +27 -0
  82. package/hooks/dist/sdd-prompt-guard.js +1 -0
  83. package/hooks/dist/sdd-read-guard.js +82 -0
  84. package/hooks/dist/sdd-session-state.sh +33 -0
  85. package/hooks/dist/sdd-statusline.js +137 -15
  86. package/hooks/dist/sdd-validate-commit.sh +47 -0
  87. package/hooks/dist/sdd-workflow-guard.js +4 -4
  88. package/hooks/sdd-check-update.js +139 -0
  89. package/hooks/sdd-context-monitor.js +165 -0
  90. package/hooks/sdd-phase-boundary.sh +27 -0
  91. package/hooks/sdd-prompt-guard.js +97 -0
  92. package/hooks/sdd-read-guard.js +82 -0
  93. package/hooks/sdd-session-state.sh +33 -0
  94. package/hooks/sdd-statusline.js +241 -0
  95. package/hooks/sdd-validate-commit.sh +47 -0
  96. package/hooks/sdd-workflow-guard.js +94 -0
  97. package/package.json +3 -3
  98. package/scripts/build-hooks.js +18 -7
  99. package/scripts/prompt-injection-scan.sh +1 -0
  100. package/scripts/rebrand-gsd-to-sdd.sh +221 -220
  101. package/scripts/run-tests.cjs +5 -1
  102. package/scripts/sync-upstream.sh +1 -1
  103. package/sdd/bin/lib/commands.cjs +79 -17
  104. package/sdd/bin/lib/config.cjs +90 -48
  105. package/sdd/bin/lib/core.cjs +452 -87
  106. package/sdd/bin/lib/docs.cjs +267 -0
  107. package/sdd/bin/lib/frontmatter.cjs +381 -336
  108. package/sdd/bin/lib/init.cjs +110 -16
  109. package/sdd/bin/lib/intel.cjs +660 -0
  110. package/sdd/bin/lib/learnings.cjs +378 -0
  111. package/sdd/bin/lib/milestone.cjs +42 -11
  112. package/sdd/bin/lib/model-profiles.cjs +17 -15
  113. package/sdd/bin/lib/phase.cjs +367 -288
  114. package/sdd/bin/lib/profile-output.cjs +106 -10
  115. package/sdd/bin/lib/roadmap.cjs +146 -115
  116. package/sdd/bin/lib/schema-detect.cjs +238 -0
  117. package/sdd/bin/lib/sdd2-import.cjs +511 -0
  118. package/sdd/bin/lib/security.cjs +124 -3
  119. package/sdd/bin/lib/state.cjs +648 -264
  120. package/sdd/bin/lib/template.cjs +8 -4
  121. package/sdd/bin/lib/verify.cjs +209 -28
  122. package/sdd/bin/lib/workstream.cjs +7 -3
  123. package/sdd/bin/sdd-tools.cjs +184 -12
  124. package/sdd/contexts/dev.md +21 -0
  125. package/sdd/contexts/research.md +22 -0
  126. package/sdd/contexts/review.md +22 -0
  127. package/sdd/references/agent-contracts.md +79 -0
  128. package/sdd/references/ai-evals.md +156 -0
  129. package/sdd/references/ai-frameworks.md +186 -0
  130. package/sdd/references/artifact-types.md +113 -0
  131. package/sdd/references/common-bug-patterns.md +114 -0
  132. package/sdd/references/context-budget.md +49 -0
  133. package/sdd/references/continuation-format.md +25 -25
  134. package/sdd/references/domain-probes.md +125 -0
  135. package/sdd/references/few-shot-examples/plan-checker.md +73 -0
  136. package/sdd/references/few-shot-examples/verifier.md +109 -0
  137. package/sdd/references/gate-prompts.md +100 -0
  138. package/sdd/references/gates.md +70 -0
  139. package/sdd/references/git-integration.md +1 -1
  140. package/sdd/references/ios-scaffold.md +123 -0
  141. package/sdd/references/model-profile-resolution.md +2 -0
  142. package/sdd/references/model-profiles.md +24 -18
  143. package/sdd/references/planner-gap-closure.md +62 -0
  144. package/sdd/references/planner-reviews.md +39 -0
  145. package/sdd/references/planner-revision.md +87 -0
  146. package/sdd/references/planning-config.md +252 -0
  147. package/sdd/references/revision-loop.md +97 -0
  148. package/sdd/references/thinking-models-debug.md +44 -0
  149. package/sdd/references/thinking-models-execution.md +50 -0
  150. package/sdd/references/thinking-models-planning.md +62 -0
  151. package/sdd/references/thinking-models-research.md +50 -0
  152. package/sdd/references/thinking-models-verification.md +55 -0
  153. package/sdd/references/thinking-partner.md +96 -0
  154. package/sdd/references/ui-brand.md +4 -4
  155. package/sdd/references/universal-anti-patterns.md +63 -0
  156. package/sdd/references/verification-overrides.md +227 -0
  157. package/sdd/references/workstream-flag.md +56 -3
  158. package/sdd/templates/AI-SPEC.md +246 -0
  159. package/sdd/templates/DEBUG.md +1 -1
  160. package/sdd/templates/SECURITY.md +61 -0
  161. package/sdd/templates/UAT.md +4 -4
  162. package/sdd/templates/VALIDATION.md +4 -4
  163. package/sdd/templates/claude-md.md +32 -9
  164. package/sdd/templates/config.json +4 -0
  165. package/sdd/templates/debug-subagent-prompt.md +1 -1
  166. package/sdd/templates/dev-preferences.md +1 -1
  167. package/sdd/templates/discovery.md +2 -2
  168. package/sdd/templates/phase-prompt.md +1 -1
  169. package/sdd/templates/planner-subagent-prompt.md +3 -3
  170. package/sdd/templates/project.md +1 -1
  171. package/sdd/templates/research.md +1 -1
  172. package/sdd/templates/state.md +2 -2
  173. package/sdd/workflows/add-phase.md +8 -8
  174. package/sdd/workflows/add-tests.md +12 -9
  175. package/sdd/workflows/add-todo.md +5 -3
  176. package/sdd/workflows/ai-integration-phase.md +284 -0
  177. package/sdd/workflows/analyze-dependencies.md +96 -0
  178. package/sdd/workflows/audit-fix.md +157 -0
  179. package/sdd/workflows/audit-milestone.md +11 -11
  180. package/sdd/workflows/audit-uat.md +2 -2
  181. package/sdd/workflows/autonomous.md +195 -27
  182. package/sdd/workflows/check-todos.md +12 -10
  183. package/sdd/workflows/cleanup.md +2 -0
  184. package/sdd/workflows/code-review-fix.md +497 -0
  185. package/sdd/workflows/code-review.md +515 -0
  186. package/sdd/workflows/complete-milestone.md +56 -22
  187. package/sdd/workflows/diagnose-issues.md +10 -3
  188. package/sdd/workflows/discovery-phase.md +5 -3
  189. package/sdd/workflows/discuss-phase-assumptions.md +24 -6
  190. package/sdd/workflows/discuss-phase-power.md +291 -0
  191. package/sdd/workflows/discuss-phase.md +173 -21
  192. package/sdd/workflows/do.md +23 -21
  193. package/sdd/workflows/docs-update.md +1155 -0
  194. package/sdd/workflows/eval-review.md +155 -0
  195. package/sdd/workflows/execute-phase.md +594 -38
  196. package/sdd/workflows/execute-plan.md +67 -96
  197. package/sdd/workflows/explore.md +139 -0
  198. package/sdd/workflows/fast.md +5 -5
  199. package/sdd/workflows/forensics.md +2 -2
  200. package/sdd/workflows/health.md +4 -4
  201. package/sdd/workflows/help.md +122 -119
  202. package/sdd/workflows/import.md +276 -0
  203. package/sdd/workflows/inbox.md +387 -0
  204. package/sdd/workflows/insert-phase.md +7 -7
  205. package/sdd/workflows/list-phase-assumptions.md +4 -4
  206. package/sdd/workflows/list-workspaces.md +2 -2
  207. package/sdd/workflows/manager.md +35 -32
  208. package/sdd/workflows/map-codebase.md +7 -5
  209. package/sdd/workflows/milestone-summary.md +2 -2
  210. package/sdd/workflows/new-milestone.md +17 -9
  211. package/sdd/workflows/new-project.md +50 -25
  212. package/sdd/workflows/new-workspace.md +7 -5
  213. package/sdd/workflows/next.md +67 -11
  214. package/sdd/workflows/note.md +9 -7
  215. package/sdd/workflows/pause-work.md +75 -12
  216. package/sdd/workflows/plan-milestone-gaps.md +8 -8
  217. package/sdd/workflows/plan-phase.md +294 -42
  218. package/sdd/workflows/plant-seed.md +6 -3
  219. package/sdd/workflows/pr-branch.md +42 -14
  220. package/sdd/workflows/profile-user.md +9 -7
  221. package/sdd/workflows/progress.md +45 -45
  222. package/sdd/workflows/quick.md +195 -47
  223. package/sdd/workflows/remove-phase.md +6 -6
  224. package/sdd/workflows/remove-workspace.md +3 -1
  225. package/sdd/workflows/research-phase.md +2 -2
  226. package/sdd/workflows/resume-project.md +12 -12
  227. package/sdd/workflows/review.md +109 -9
  228. package/sdd/workflows/scan.md +102 -0
  229. package/sdd/workflows/secure-phase.md +166 -0
  230. package/sdd/workflows/session-report.md +2 -2
  231. package/sdd/workflows/settings.md +38 -12
  232. package/sdd/workflows/ship.md +21 -9
  233. package/sdd/workflows/stats.md +1 -1
  234. package/sdd/workflows/transition.md +23 -23
  235. package/sdd/workflows/ui-phase.md +15 -7
  236. package/sdd/workflows/ui-review.md +29 -4
  237. package/sdd/workflows/undo.md +314 -0
  238. package/sdd/workflows/update.md +171 -20
  239. package/sdd/workflows/validate-phase.md +6 -4
  240. package/sdd/workflows/verify-phase.md +210 -6
  241. package/sdd/workflows/verify-work.md +83 -9
  242. package/sdd/commands/sdd/workstreams.md +0 -63
@@ -0,0 +1,511 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * sdd2-import — Reverse migration from SDD-2 (.sdd/) to SDD v1 (.planning/)
5
+ *
6
+ * Reads a SDD-2 project directory structure and produces a complete
7
+ * .planning/ artifact tree in SDD v1 format.
8
+ *
9
+ * SDD-2 hierarchy: Milestone → Slice → Task
10
+ * SDD v1 hierarchy: Milestone (in ROADMAP.md) → Phase → Plan
11
+ *
12
+ * Mapping rules:
13
+ * - Slices are numbered sequentially across all milestones (01, 02, …)
14
+ * - Tasks within a slice become plans (01-01, 01-02, …)
15
+ * - Completed slices ([x] in ROADMAP) → [x] phases in ROADMAP.md
16
+ * - Tasks with a SUMMARY file → SUMMARY.md written
17
+ * - Slice RESEARCH.md → phase XX-RESEARCH.md
18
+ */
19
+
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+
23
+ // ─── Utilities ──────────────────────────────────────────────────────────────
24
+
25
+ function readOptional(filePath) {
26
+ try { return fs.readFileSync(filePath, 'utf8'); } catch { return null; }
27
+ }
28
+
29
+ function zeroPad(n, width = 2) {
30
+ return String(n).padStart(width, '0');
31
+ }
32
+
33
+ function slugify(title) {
34
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
35
+ }
36
+
37
+ // ─── SDD-2 Parser ───────────────────────────────────────────────────────────
38
+
39
+ /**
40
+ * Find the .sdd/ directory starting from a project root.
41
+ * Returns the absolute path or null if not found.
42
+ */
43
+ function findSdd2Root(startPath) {
44
+ if (path.basename(startPath) === '.sdd' && fs.existsSync(startPath)) {
45
+ return startPath;
46
+ }
47
+ const candidate = path.join(startPath, '.sdd');
48
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
49
+ return candidate;
50
+ }
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Parse the ## Slices section from a SDD-2 milestone ROADMAP.md.
56
+ * Each slice entry looks like:
57
+ * - [x] **S01: Title** `risk:medium` `depends:[S00]`
58
+ */
59
+ function parseSlicesFromRoadmap(content) {
60
+ const slices = [];
61
+ const sectionMatch = content.match(/## Slices\n([\s\S]*?)(?:\n## |\n# |$)/);
62
+ if (!sectionMatch) return slices;
63
+
64
+ for (const line of sectionMatch[1].split('\n')) {
65
+ const m = line.match(/^- \[([x ])\]\s+\*\*(\w+):\s*([^*]+)\*\*/);
66
+ if (!m) continue;
67
+ slices.push({ done: m[1] === 'x', id: m[2].trim(), title: m[3].trim() });
68
+ }
69
+ return slices;
70
+ }
71
+
72
+ /**
73
+ * Parse the milestone title from the first heading in a SDD-2 ROADMAP.md.
74
+ * Format: # M001: Title
75
+ */
76
+ function parseMilestoneTitle(content) {
77
+ const m = content.match(/^# \w+:\s*(.+)/m);
78
+ return m ? m[1].trim() : null;
79
+ }
80
+
81
+ /**
82
+ * Parse a task title from a SDD-2 T##-PLAN.md.
83
+ * Format: # T01: Title
84
+ */
85
+ function parseTaskTitle(content, fallback) {
86
+ const m = content.match(/^# \w+:\s*(.+)/m);
87
+ return m ? m[1].trim() : fallback;
88
+ }
89
+
90
+ /**
91
+ * Parse the ## Description body from a SDD-2 task plan.
92
+ */
93
+ function parseTaskDescription(content) {
94
+ const m = content.match(/## Description\n+([\s\S]+?)(?:\n## |\n# |$)/);
95
+ return m ? m[1].trim() : '';
96
+ }
97
+
98
+ /**
99
+ * Parse ## Must-Haves items from a SDD-2 task plan.
100
+ */
101
+ function parseTaskMustHaves(content) {
102
+ const m = content.match(/## Must-Haves\n+([\s\S]+?)(?:\n## |\n# |$)/);
103
+ if (!m) return [];
104
+ return m[1].split('\n')
105
+ .map(l => l.match(/^- \[[ x]\]\s*(.+)/))
106
+ .filter(Boolean)
107
+ .map(match => match[1].trim());
108
+ }
109
+
110
+ /**
111
+ * Read all task plan files from a SDD-2 tasks/ directory.
112
+ */
113
+ function readTasksDir(tasksDir) {
114
+ if (!fs.existsSync(tasksDir)) return [];
115
+
116
+ return fs.readdirSync(tasksDir)
117
+ .filter(f => f.endsWith('-PLAN.md'))
118
+ .sort()
119
+ .map(tf => {
120
+ const tid = tf.replace('-PLAN.md', '');
121
+ const plan = readOptional(path.join(tasksDir, tf));
122
+ const summary = readOptional(path.join(tasksDir, `${tid}-SUMMARY.md`));
123
+ return {
124
+ id: tid,
125
+ title: plan ? parseTaskTitle(plan, tid) : tid,
126
+ description: plan ? parseTaskDescription(plan) : '',
127
+ mustHaves: plan ? parseTaskMustHaves(plan) : [],
128
+ plan,
129
+ summary,
130
+ done: !!summary,
131
+ };
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Parse a complete SDD-2 .sdd/ directory into a structured representation.
137
+ */
138
+ function parseSdd2(sddDir) {
139
+ const data = {
140
+ projectContent: readOptional(path.join(sddDir, 'PROJECT.md')),
141
+ requirements: readOptional(path.join(sddDir, 'REQUIREMENTS.md')),
142
+ milestones: [],
143
+ };
144
+
145
+ const milestonesBase = path.join(sddDir, 'milestones');
146
+ if (!fs.existsSync(milestonesBase)) return data;
147
+
148
+ const milestoneIds = fs.readdirSync(milestonesBase)
149
+ .filter(d => fs.statSync(path.join(milestonesBase, d)).isDirectory())
150
+ .sort();
151
+
152
+ for (const mid of milestoneIds) {
153
+ const mDir = path.join(milestonesBase, mid);
154
+ const roadmapContent = readOptional(path.join(mDir, `${mid}-ROADMAP.md`));
155
+ const slicesDir = path.join(mDir, 'slices');
156
+
157
+ const sliceInfos = roadmapContent ? parseSlicesFromRoadmap(roadmapContent) : [];
158
+
159
+ const slices = sliceInfos.map(info => {
160
+ const sDir = path.join(slicesDir, info.id);
161
+ const hasSDir = fs.existsSync(sDir);
162
+ return {
163
+ id: info.id,
164
+ title: info.title,
165
+ done: info.done,
166
+ plan: hasSDir ? readOptional(path.join(sDir, `${info.id}-PLAN.md`)) : null,
167
+ summary: hasSDir ? readOptional(path.join(sDir, `${info.id}-SUMMARY.md`)) : null,
168
+ research: hasSDir ? readOptional(path.join(sDir, `${info.id}-RESEARCH.md`)) : null,
169
+ context: hasSDir ? readOptional(path.join(sDir, `${info.id}-CONTEXT.md`)) : null,
170
+ tasks: hasSDir ? readTasksDir(path.join(sDir, 'tasks')) : [],
171
+ };
172
+ });
173
+
174
+ data.milestones.push({
175
+ id: mid,
176
+ title: roadmapContent ? (parseMilestoneTitle(roadmapContent) ?? mid) : mid,
177
+ research: readOptional(path.join(mDir, `${mid}-RESEARCH.md`)),
178
+ slices,
179
+ });
180
+ }
181
+
182
+ return data;
183
+ }
184
+
185
+ // ─── Artifact Builders ──────────────────────────────────────────────────────
186
+
187
+ /**
188
+ * Build a SDD v1 PLAN.md from a SDD-2 task.
189
+ */
190
+ function buildPlanMd(task, phasePrefix, planPrefix, phaseSlug, milestoneTitle) {
191
+ const lines = [
192
+ '---',
193
+ `phase: "${phasePrefix}"`,
194
+ `plan: "${planPrefix}"`,
195
+ 'type: "implementation"',
196
+ '---',
197
+ '',
198
+ '<objective>',
199
+ task.title,
200
+ '</objective>',
201
+ '',
202
+ '<context>',
203
+ `Phase: ${phasePrefix} (${phaseSlug}) — Milestone: ${milestoneTitle}`,
204
+ ];
205
+
206
+ if (task.description) {
207
+ lines.push('', task.description);
208
+ }
209
+
210
+ lines.push('</context>');
211
+
212
+ if (task.mustHaves.length > 0) {
213
+ lines.push('', '<must_haves>');
214
+ for (const mh of task.mustHaves) {
215
+ lines.push(`- ${mh}`);
216
+ }
217
+ lines.push('</must_haves>');
218
+ }
219
+
220
+ return lines.join('\n') + '\n';
221
+ }
222
+
223
+ /**
224
+ * Build a SDD v1 SUMMARY.md from a SDD-2 task summary.
225
+ * Strips the SDD-2 frontmatter and preserves the body.
226
+ */
227
+ function buildSummaryMd(task, phasePrefix, planPrefix) {
228
+ const raw = task.summary || '';
229
+ // Strip SDD-2 frontmatter block (--- ... ---) if present
230
+ const bodyMatch = raw.match(/^---[\s\S]*?---\n+([\s\S]*)$/);
231
+ const body = bodyMatch ? bodyMatch[1].trim() : raw.trim();
232
+
233
+ return [
234
+ '---',
235
+ `phase: "${phasePrefix}"`,
236
+ `plan: "${planPrefix}"`,
237
+ '---',
238
+ '',
239
+ body || 'Task completed (migrated from SDD-2).',
240
+ '',
241
+ ].join('\n');
242
+ }
243
+
244
+ /**
245
+ * Build a SDD v1 XX-CONTEXT.md from a SDD-2 slice.
246
+ */
247
+ function buildContextMd(slice, phasePrefix) {
248
+ const lines = [
249
+ `# Phase ${phasePrefix} Context`,
250
+ '',
251
+ `Migrated from SDD-2 slice ${slice.id}: ${slice.title}`,
252
+ ];
253
+
254
+ const extra = slice.context || '';
255
+ if (extra.trim()) {
256
+ lines.push('', extra.trim());
257
+ }
258
+
259
+ return lines.join('\n') + '\n';
260
+ }
261
+
262
+ /**
263
+ * Build the SDD v1 ROADMAP.md with milestone-sectioned format.
264
+ */
265
+ function buildRoadmapMd(milestones, phaseMap) {
266
+ const lines = ['# Roadmap', ''];
267
+
268
+ for (const milestone of milestones) {
269
+ lines.push(`## ${milestone.id}: ${milestone.title}`, '');
270
+ const mPhases = phaseMap.filter(p => p.milestoneId === milestone.id);
271
+ for (const { slice, phaseNum } of mPhases) {
272
+ const prefix = zeroPad(phaseNum);
273
+ const slug = slugify(slice.title);
274
+ const check = slice.done ? 'x' : ' ';
275
+ lines.push(`- [${check}] **Phase ${prefix}: ${slug}** — ${slice.title}`);
276
+ }
277
+ lines.push('');
278
+ }
279
+
280
+ return lines.join('\n');
281
+ }
282
+
283
+ /**
284
+ * Build the SDD v1 STATE.md reflecting the current position in the project.
285
+ */
286
+ function buildStateMd(phaseMap) {
287
+ const currentEntry = phaseMap.find(p => !p.slice.done);
288
+ const totalPhases = phaseMap.length;
289
+ const donePhases = phaseMap.filter(p => p.slice.done).length;
290
+ const pct = totalPhases > 0 ? Math.round((donePhases / totalPhases) * 100) : 0;
291
+
292
+ const currentPhaseNum = currentEntry ? zeroPad(currentEntry.phaseNum) : zeroPad(totalPhases);
293
+ const currentSlug = currentEntry ? slugify(currentEntry.slice.title) : 'complete';
294
+ const status = currentEntry ? 'Ready to plan' : 'All phases complete';
295
+
296
+ const filled = Math.round(pct / 10);
297
+ const bar = `[${'█'.repeat(filled)}${'░'.repeat(10 - filled)}]`;
298
+ const today = new Date().toISOString().split('T')[0];
299
+
300
+ return [
301
+ '# Project State',
302
+ '',
303
+ '## Project Reference',
304
+ '',
305
+ 'See: .planning/PROJECT.md',
306
+ '',
307
+ `**Current focus:** Phase ${currentPhaseNum} (${currentSlug})`,
308
+ '',
309
+ '## Current Position',
310
+ '',
311
+ `Phase: ${currentPhaseNum} of ${zeroPad(totalPhases)} (${currentSlug})`,
312
+ `Status: ${status}`,
313
+ `Last activity: ${today} — Migrated from SDD-2`,
314
+ '',
315
+ `Progress: ${bar} ${pct}%`,
316
+ '',
317
+ '## Accumulated Context',
318
+ '',
319
+ '### Decisions',
320
+ '',
321
+ 'Migrated from SDD-2. Review PROJECT.md for key decisions.',
322
+ '',
323
+ '### Blockers/Concerns',
324
+ '',
325
+ 'None.',
326
+ '',
327
+ '## Session Continuity',
328
+ '',
329
+ `Last session: ${today}`,
330
+ 'Stopped at: Migration from SDD-2 completed',
331
+ 'Resume file: None',
332
+ '',
333
+ ].join('\n');
334
+ }
335
+
336
+ // ─── Transformer ─────────────────────────────────────────────────────────────
337
+
338
+ /**
339
+ * Convert parsed SDD-2 data into a map of relative path → file content.
340
+ * All paths are relative to the .planning/ root.
341
+ */
342
+ function buildPlanningArtifacts(sdd2Data) {
343
+ const artifacts = new Map();
344
+
345
+ // Passthrough files
346
+ artifacts.set('PROJECT.md', sdd2Data.projectContent || '# Project\n\n(Migrated from SDD-2)\n');
347
+ if (sdd2Data.requirements) {
348
+ artifacts.set('REQUIREMENTS.md', sdd2Data.requirements);
349
+ }
350
+
351
+ // Minimal valid v1 config
352
+ artifacts.set('config.json', JSON.stringify({ version: 1 }, null, 2) + '\n');
353
+
354
+ // Build sequential phase map: flatten Milestones → Slices into numbered phases
355
+ const phaseMap = [];
356
+ let phaseNum = 1;
357
+ for (const milestone of sdd2Data.milestones) {
358
+ for (const slice of milestone.slices) {
359
+ phaseMap.push({ milestoneId: milestone.id, milestoneTitle: milestone.title, slice, phaseNum });
360
+ phaseNum++;
361
+ }
362
+ }
363
+
364
+ artifacts.set('ROADMAP.md', buildRoadmapMd(sdd2Data.milestones, phaseMap));
365
+ artifacts.set('STATE.md', buildStateMd(phaseMap));
366
+
367
+ for (const { slice, phaseNum, milestoneTitle } of phaseMap) {
368
+ const prefix = zeroPad(phaseNum);
369
+ const slug = slugify(slice.title);
370
+ const dir = `phases/${prefix}-${slug}`;
371
+
372
+ artifacts.set(`${dir}/${prefix}-CONTEXT.md`, buildContextMd(slice, prefix));
373
+
374
+ if (slice.research) {
375
+ artifacts.set(`${dir}/${prefix}-RESEARCH.md`, slice.research);
376
+ }
377
+
378
+ for (let i = 0; i < slice.tasks.length; i++) {
379
+ const task = slice.tasks[i];
380
+ const planPrefix = zeroPad(i + 1);
381
+
382
+ artifacts.set(
383
+ `${dir}/${prefix}-${planPrefix}-PLAN.md`,
384
+ buildPlanMd(task, prefix, planPrefix, slug, milestoneTitle)
385
+ );
386
+
387
+ if (task.done && task.summary) {
388
+ artifacts.set(
389
+ `${dir}/${prefix}-${planPrefix}-SUMMARY.md`,
390
+ buildSummaryMd(task, prefix, planPrefix)
391
+ );
392
+ }
393
+ }
394
+ }
395
+
396
+ return artifacts;
397
+ }
398
+
399
+ // ─── Preview ─────────────────────────────────────────────────────────────────
400
+
401
+ /**
402
+ * Format a dry-run preview string for display before writing.
403
+ */
404
+ function buildPreview(sdd2Data, artifacts) {
405
+ const lines = ['Preview — files that will be created in .planning/:'];
406
+
407
+ for (const rel of artifacts.keys()) {
408
+ lines.push(` ${rel}`);
409
+ }
410
+
411
+ const totalSlices = sdd2Data.milestones.reduce((s, m) => s + m.slices.length, 0);
412
+ const doneSlices = sdd2Data.milestones.reduce((s, m) => s + m.slices.filter(sl => sl.done).length, 0);
413
+ const allTasks = sdd2Data.milestones.flatMap(m => m.slices.flatMap(sl => sl.tasks));
414
+ const doneTasks = allTasks.filter(t => t.done).length;
415
+
416
+ lines.push('');
417
+ lines.push(`Milestones: ${sdd2Data.milestones.length}`);
418
+ lines.push(`Phases (slices): ${totalSlices} (${doneSlices} completed)`);
419
+ lines.push(`Plans (tasks): ${allTasks.length} (${doneTasks} completed)`);
420
+ lines.push('');
421
+ lines.push('Cannot migrate automatically:');
422
+ lines.push(' - SDD-2 cost/token ledger (no v1 equivalent)');
423
+ lines.push(' - SDD-2 database state (rebuilt from files on first /sdd-health)');
424
+ lines.push(' - VS Code extension state');
425
+
426
+ return lines.join('\n');
427
+ }
428
+
429
+ // ─── Writer ───────────────────────────────────────────────────────────────────
430
+
431
+ /**
432
+ * Write all artifacts to the .planning/ directory.
433
+ */
434
+ function writePlanningDir(artifacts, planningRoot) {
435
+ for (const [rel, content] of artifacts) {
436
+ const absPath = path.join(planningRoot, rel);
437
+ fs.mkdirSync(path.dirname(absPath), { recursive: true });
438
+ fs.writeFileSync(absPath, content, 'utf8');
439
+ }
440
+ }
441
+
442
+ // ─── Command Handler ──────────────────────────────────────────────────────────
443
+
444
+ /**
445
+ * Entry point called from sdd-tools.cjs.
446
+ * Supports: --force, --dry-run, --path <dir>
447
+ */
448
+ function cmdFromSdd2(args, cwd, raw) {
449
+ const { output, error } = require('./core.cjs');
450
+
451
+ const force = args.includes('--force');
452
+ const dryRun = args.includes('--dry-run');
453
+
454
+ const pathIdx = args.indexOf('--path');
455
+ const projectDir = pathIdx >= 0 && args[pathIdx + 1]
456
+ ? path.resolve(cwd, args[pathIdx + 1])
457
+ : cwd;
458
+
459
+ const sddDir = findSdd2Root(projectDir);
460
+ if (!sddDir) {
461
+ return output({ success: false, error: `No .sdd/ directory found in ${projectDir}` }, raw);
462
+ }
463
+
464
+ const planningRoot = path.join(path.dirname(sddDir), '.planning');
465
+ if (fs.existsSync(planningRoot) && !force) {
466
+ return output({
467
+ success: false,
468
+ error: `.planning/ already exists at ${planningRoot}. Pass --force to overwrite.`,
469
+ }, raw);
470
+ }
471
+
472
+ const sdd2Data = parseSdd2(sddDir);
473
+ const artifacts = buildPlanningArtifacts(sdd2Data);
474
+ const preview = buildPreview(sdd2Data, artifacts);
475
+
476
+ if (dryRun) {
477
+ return output({ success: true, dryRun: true, preview }, raw);
478
+ }
479
+
480
+ writePlanningDir(artifacts, planningRoot);
481
+
482
+ return output({
483
+ success: true,
484
+ planningDir: planningRoot,
485
+ filesWritten: artifacts.size,
486
+ milestones: sdd2Data.milestones.length,
487
+ preview,
488
+ }, raw);
489
+ }
490
+
491
+ module.exports = {
492
+ findSdd2Root,
493
+ parseSdd2,
494
+ buildPlanningArtifacts,
495
+ buildPreview,
496
+ writePlanningDir,
497
+ cmdFromSdd2,
498
+ // Exported for unit tests
499
+ parseSlicesFromRoadmap,
500
+ parseMilestoneTitle,
501
+ parseTaskTitle,
502
+ parseTaskDescription,
503
+ parseTaskMustHaves,
504
+ buildPlanMd,
505
+ buildSummaryMd,
506
+ buildContextMd,
507
+ buildRoadmapMd,
508
+ buildStateMd,
509
+ slugify,
510
+ zeroPad,
511
+ };
@@ -152,6 +152,25 @@ const INJECTION_PATTERNS = [
152
152
  /(?:run|execute|call|invoke)\s+(?:the\s+)?(?:bash|shell|exec|spawn)\s+(?:tool|command)/i,
153
153
  ];
154
154
 
155
+ /**
156
+ * Layer 2: Encoding-obfuscation patterns with custom finding messages.
157
+ * Each entry: { pattern: RegExp, message: string }
158
+ */
159
+ const OBFUSCATION_PATTERN_ENTRIES = [
160
+ {
161
+ pattern: /\b(\w\s){4,}\w\b/,
162
+ message: 'Character-spacing obfuscation pattern detected (e.g. "i g n o r e")',
163
+ },
164
+ {
165
+ pattern: /<\/?(system|human|assistant|user)\s*>/i,
166
+ message: 'Delimiter injection pattern: <system>/<assistant>/<user> tag detected',
167
+ },
168
+ {
169
+ pattern: /0x[0-9a-fA-F]{16,}/,
170
+ message: 'Long hex sequence detected — possible encoded payload',
171
+ },
172
+ ];
173
+
155
174
  /**
156
175
  * Scan text for potential prompt injection patterns.
157
176
  * Returns an array of findings (empty = clean).
@@ -174,6 +193,13 @@ function scanForInjection(text, opts = {}) {
174
193
  }
175
194
  }
176
195
 
196
+ // Layer 2: encoding-obfuscation patterns with custom messages
197
+ for (const entry of OBFUSCATION_PATTERN_ENTRIES) {
198
+ if (entry.pattern.test(text)) {
199
+ findings.push(entry.message);
200
+ }
201
+ }
202
+
177
203
  if (opts.strict) {
178
204
  // Check for suspicious Unicode that could hide instructions
179
205
  // (zero-width chars, RTL override, homoglyph attacks)
@@ -181,9 +207,17 @@ function scanForInjection(text, opts = {}) {
181
207
  findings.push('Contains suspicious zero-width or invisible Unicode characters');
182
208
  }
183
209
 
184
- // Check for extremely long strings that could be prompt stuffing
185
- if (text.length > 50000) {
186
- findings.push(`Suspicious text length: ${text.length} chars (potential prompt stuffing)`);
210
+ // Layer 1: Unicode tag block U+E0000–U+E007F (2025 supply-chain attack vector)
211
+ // These characters are invisible and can embed hidden instructions
212
+ if (/[\uDB40\uDC00-\uDB40\uDC7F]/u.test(text) || /[\u{E0000}-\u{E007F}]/u.test(text)) {
213
+ findings.push('Contains Unicode tag block characters (U+E0000–E007F) — invisible instruction injection vector');
214
+ }
215
+
216
+ // Check for extremely long strings that could be prompt stuffing.
217
+ // Normalize CRLF → LF before measuring so Windows checkouts don't inflate the count.
218
+ const normalizedLength = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').length;
219
+ if (normalizedLength > 50000) {
220
+ findings.push(`Suspicious text length: ${normalizedLength} chars (potential prompt stuffing)`);
187
221
  }
188
222
  }
189
223
 
@@ -359,6 +393,87 @@ function validateFieldName(field) {
359
393
  return { valid: false, error: `Invalid field name: "${field}"` };
360
394
  }
361
395
 
396
+ // ─── Layer 3: Structural Schema Validation ───────────────────────────────────
397
+
398
+ const KNOWN_VALID_TAGS = new Set([
399
+ 'objective', 'process', 'step', 'success_criteria', 'critical_rules',
400
+ 'available_agent_types', 'purpose', 'required_reading',
401
+ ]);
402
+
403
+ /**
404
+ * Validate the XML structure of a prompt file.
405
+ * For agent/workflow files, flags any XML tag not in the known-valid set.
406
+ *
407
+ * @param {string} text - The file content to validate
408
+ * @param {'agent'|'workflow'|'unknown'} fileType - The type of prompt file
409
+ * @returns {{ valid: boolean, violations: string[] }}
410
+ */
411
+ function validatePromptStructure(text, fileType) {
412
+ if (!text || typeof text !== 'string') {
413
+ return { valid: true, violations: [] };
414
+ }
415
+
416
+ if (fileType !== 'agent' && fileType !== 'workflow') {
417
+ return { valid: true, violations: [] };
418
+ }
419
+
420
+ const violations = [];
421
+ const tagRegex = /<([A-Za-z][A-Za-z0-9_-]*)/g;
422
+ let match;
423
+ while ((match = tagRegex.exec(text)) !== null) {
424
+ const tag = match[1].toLowerCase();
425
+ if (!KNOWN_VALID_TAGS.has(tag)) {
426
+ violations.push(`Unknown XML tag in ${fileType} file: <${tag}>`);
427
+ }
428
+ }
429
+
430
+ return { valid: violations.length === 0, violations };
431
+ }
432
+
433
+ // ─── Layer 4: Paragraph-Level Entropy Anomaly Detection ─────────────────────
434
+
435
+ function shannonEntropy(text) {
436
+ if (!text || text.length === 0) return 0;
437
+ const freq = {};
438
+ for (const ch of text) {
439
+ freq[ch] = (freq[ch] || 0) + 1;
440
+ }
441
+ const len = text.length;
442
+ let entropy = 0;
443
+ for (const count of Object.values(freq)) {
444
+ const p = count / len;
445
+ entropy -= p * Math.log2(p);
446
+ }
447
+ return entropy;
448
+ }
449
+
450
+ /**
451
+ * Scan text for paragraphs with anomalously high Shannon entropy.
452
+ *
453
+ * @param {string} text - The text to scan
454
+ * @returns {{ clean: boolean, findings: string[] }}
455
+ */
456
+ function scanEntropyAnomalies(text) {
457
+ if (!text || typeof text !== 'string') {
458
+ return { clean: true, findings: [] };
459
+ }
460
+
461
+ const findings = [];
462
+ const paragraphs = text.split(/\n\n+/);
463
+
464
+ for (const para of paragraphs) {
465
+ if (para.length <= 50) continue;
466
+ const entropy = shannonEntropy(para);
467
+ if (entropy > 5.5) {
468
+ findings.push(
469
+ `High-entropy paragraph detected (${entropy.toFixed(2)} bits/char) — possible encoded payload`
470
+ );
471
+ }
472
+ }
473
+
474
+ return { clean: findings.length === 0, findings };
475
+ }
476
+
362
477
  module.exports = {
363
478
  // Path safety
364
479
  validatePath,
@@ -379,4 +494,10 @@ module.exports = {
379
494
  // Input validation
380
495
  validatePhaseNumber,
381
496
  validateFieldName,
497
+
498
+ // Structural validation (Layer 3)
499
+ validatePromptStructure,
500
+
501
+ // Entropy anomaly detection (Layer 4)
502
+ scanEntropyAnomalies,
382
503
  };