@tuan_son.dinh/gsd 2.6.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 (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +453 -0
  3. package/dist/app-paths.d.ts +4 -0
  4. package/dist/app-paths.js +6 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +269 -0
  7. package/dist/loader.d.ts +2 -0
  8. package/dist/loader.js +70 -0
  9. package/dist/logo.d.ts +16 -0
  10. package/dist/logo.js +25 -0
  11. package/dist/onboarding.d.ts +43 -0
  12. package/dist/onboarding.js +418 -0
  13. package/dist/pi-migration.d.ts +14 -0
  14. package/dist/pi-migration.js +57 -0
  15. package/dist/resource-loader.d.ts +22 -0
  16. package/dist/resource-loader.js +60 -0
  17. package/dist/tool-bootstrap.d.ts +4 -0
  18. package/dist/tool-bootstrap.js +74 -0
  19. package/dist/wizard.d.ts +7 -0
  20. package/dist/wizard.js +25 -0
  21. package/package.json +60 -0
  22. package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
  23. package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
  24. package/pkg/dist/modes/interactive/theme/dark.json +85 -0
  25. package/pkg/dist/modes/interactive/theme/light.json +84 -0
  26. package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
  27. package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
  28. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  29. package/pkg/dist/modes/interactive/theme/theme.js +949 -0
  30. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
  31. package/pkg/package.json +8 -0
  32. package/scripts/postinstall.js +127 -0
  33. package/src/resources/GSD-WORKFLOW.md +661 -0
  34. package/src/resources/agents/researcher.md +29 -0
  35. package/src/resources/agents/scout.md +56 -0
  36. package/src/resources/agents/worker.md +31 -0
  37. package/src/resources/extensions/ask-user-questions.ts +249 -0
  38. package/src/resources/extensions/bg-shell/index.ts +2808 -0
  39. package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  40. package/src/resources/extensions/browser-tools/core.js +1057 -0
  41. package/src/resources/extensions/browser-tools/index.ts +4989 -0
  42. package/src/resources/extensions/browser-tools/package.json +20 -0
  43. package/src/resources/extensions/context7/index.ts +428 -0
  44. package/src/resources/extensions/context7/package.json +11 -0
  45. package/src/resources/extensions/get-secrets-from-user.ts +352 -0
  46. package/src/resources/extensions/google-search/index.ts +323 -0
  47. package/src/resources/extensions/google-search/package.json +9 -0
  48. package/src/resources/extensions/gsd/activity-log.ts +69 -0
  49. package/src/resources/extensions/gsd/auto.ts +2744 -0
  50. package/src/resources/extensions/gsd/commands.ts +313 -0
  51. package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
  52. package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
  53. package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
  54. package/src/resources/extensions/gsd/doctor.ts +690 -0
  55. package/src/resources/extensions/gsd/files.ts +732 -0
  56. package/src/resources/extensions/gsd/git-service.ts +597 -0
  57. package/src/resources/extensions/gsd/gitignore.ts +168 -0
  58. package/src/resources/extensions/gsd/guided-flow.ts +817 -0
  59. package/src/resources/extensions/gsd/index.ts +558 -0
  60. package/src/resources/extensions/gsd/metrics.ts +374 -0
  61. package/src/resources/extensions/gsd/migrate/command.ts +218 -0
  62. package/src/resources/extensions/gsd/migrate/index.ts +42 -0
  63. package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
  64. package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
  65. package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
  66. package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
  67. package/src/resources/extensions/gsd/migrate/types.ts +370 -0
  68. package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
  69. package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
  70. package/src/resources/extensions/gsd/observability-validator.ts +408 -0
  71. package/src/resources/extensions/gsd/package.json +11 -0
  72. package/src/resources/extensions/gsd/paths.ts +308 -0
  73. package/src/resources/extensions/gsd/preferences.ts +757 -0
  74. package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
  75. package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
  76. package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
  77. package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
  78. package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
  79. package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
  80. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
  81. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
  82. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
  83. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
  84. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
  85. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
  86. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
  87. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
  88. package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
  89. package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
  90. package/src/resources/extensions/gsd/prompts/queue.md +85 -0
  91. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
  92. package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
  93. package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
  94. package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
  95. package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
  96. package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
  97. package/src/resources/extensions/gsd/prompts/system.md +187 -0
  98. package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
  99. package/src/resources/extensions/gsd/session-forensics.ts +487 -0
  100. package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
  101. package/src/resources/extensions/gsd/state.ts +460 -0
  102. package/src/resources/extensions/gsd/templates/context.md +76 -0
  103. package/src/resources/extensions/gsd/templates/decisions.md +8 -0
  104. package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
  105. package/src/resources/extensions/gsd/templates/plan.md +131 -0
  106. package/src/resources/extensions/gsd/templates/preferences.md +24 -0
  107. package/src/resources/extensions/gsd/templates/project.md +31 -0
  108. package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
  109. package/src/resources/extensions/gsd/templates/requirements.md +81 -0
  110. package/src/resources/extensions/gsd/templates/research.md +46 -0
  111. package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
  112. package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
  113. package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
  114. package/src/resources/extensions/gsd/templates/state.md +19 -0
  115. package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
  116. package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
  117. package/src/resources/extensions/gsd/templates/uat.md +54 -0
  118. package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
  119. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
  120. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
  121. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
  122. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
  123. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
  124. package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
  125. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
  126. package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
  127. package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
  128. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
  129. package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
  130. package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
  131. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
  132. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
  133. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
  134. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
  135. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
  136. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
  137. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
  138. package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
  139. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
  140. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
  141. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
  142. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
  143. package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
  144. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
  145. package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
  146. package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
  147. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
  148. package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
  149. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
  150. package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
  151. package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
  152. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
  153. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
  154. package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
  155. package/src/resources/extensions/gsd/types.ts +159 -0
  156. package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
  157. package/src/resources/extensions/gsd/workspace-index.ts +203 -0
  158. package/src/resources/extensions/gsd/worktree-command.ts +845 -0
  159. package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
  160. package/src/resources/extensions/gsd/worktree.ts +183 -0
  161. package/src/resources/extensions/mac-tools/index.ts +852 -0
  162. package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  163. package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  164. package/src/resources/extensions/mcporter/index.ts +429 -0
  165. package/src/resources/extensions/remote-questions/config.ts +81 -0
  166. package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
  167. package/src/resources/extensions/remote-questions/format.ts +163 -0
  168. package/src/resources/extensions/remote-questions/manager.ts +192 -0
  169. package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
  170. package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
  171. package/src/resources/extensions/remote-questions/status.ts +31 -0
  172. package/src/resources/extensions/remote-questions/store.ts +77 -0
  173. package/src/resources/extensions/remote-questions/types.ts +75 -0
  174. package/src/resources/extensions/search-the-web/cache.ts +78 -0
  175. package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
  176. package/src/resources/extensions/search-the-web/format.ts +258 -0
  177. package/src/resources/extensions/search-the-web/http.ts +238 -0
  178. package/src/resources/extensions/search-the-web/index.ts +65 -0
  179. package/src/resources/extensions/search-the-web/native-search.ts +157 -0
  180. package/src/resources/extensions/search-the-web/provider.ts +118 -0
  181. package/src/resources/extensions/search-the-web/tavily.ts +116 -0
  182. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
  183. package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
  184. package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
  185. package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
  186. package/src/resources/extensions/shared/confirm-ui.ts +126 -0
  187. package/src/resources/extensions/shared/interview-ui.ts +613 -0
  188. package/src/resources/extensions/shared/next-action-ui.ts +197 -0
  189. package/src/resources/extensions/shared/progress-widget.ts +282 -0
  190. package/src/resources/extensions/shared/terminal.ts +23 -0
  191. package/src/resources/extensions/shared/thinking-widget.ts +107 -0
  192. package/src/resources/extensions/shared/ui.ts +400 -0
  193. package/src/resources/extensions/shared/wizard-ui.ts +551 -0
  194. package/src/resources/extensions/slash-commands/audit.ts +88 -0
  195. package/src/resources/extensions/slash-commands/clear.ts +10 -0
  196. package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
  197. package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
  198. package/src/resources/extensions/slash-commands/index.ts +12 -0
  199. package/src/resources/extensions/subagent/agents.ts +126 -0
  200. package/src/resources/extensions/subagent/index.ts +1020 -0
  201. package/src/resources/extensions/voice/index.ts +195 -0
  202. package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
  203. package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
  204. package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  205. package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  206. package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  207. package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  208. package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  209. package/src/resources/skills/frontend-design/SKILL.md +45 -0
  210. package/src/resources/skills/swiftui/SKILL.md +208 -0
  211. package/src/resources/skills/swiftui/references/animations.md +921 -0
  212. package/src/resources/skills/swiftui/references/architecture.md +1561 -0
  213. package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
  214. package/src/resources/skills/swiftui/references/navigation.md +1492 -0
  215. package/src/resources/skills/swiftui/references/networking-async.md +214 -0
  216. package/src/resources/skills/swiftui/references/performance.md +1706 -0
  217. package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
  218. package/src/resources/skills/swiftui/references/state-management.md +1443 -0
  219. package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
  220. package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
  221. package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  222. package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
  223. package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  224. package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  225. package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  226. package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
  227. package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
@@ -0,0 +1,443 @@
1
+ // Unit tests for T02: validator and per-file parsers
2
+ // Tests these independently of the T03 orchestrator (parsePlanningDirectory).
3
+
4
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { tmpdir } from 'node:os';
7
+
8
+ import { validatePlanningDirectory } from '../migrate/validator.ts';
9
+ import {
10
+ parseOldRoadmap,
11
+ parseOldPlan,
12
+ parseOldSummary,
13
+ parseOldRequirements,
14
+ parseOldProject,
15
+ parseOldState,
16
+ parseOldConfig,
17
+ } from '../migrate/parsers.ts';
18
+
19
+ let passed = 0;
20
+ let failed = 0;
21
+
22
+ function assert(condition: boolean, message: string): void {
23
+ if (condition) {
24
+ passed++;
25
+ } else {
26
+ failed++;
27
+ console.error(` FAIL: ${message}`);
28
+ }
29
+ }
30
+
31
+ function assertEq<T>(actual: T, expected: T, message: string): void {
32
+ if (JSON.stringify(actual) === JSON.stringify(expected)) {
33
+ passed++;
34
+ } else {
35
+ failed++;
36
+ console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
37
+ }
38
+ }
39
+
40
+ function createFixtureBase(): string {
41
+ return mkdtempSync(join(tmpdir(), 'gsd-migrate-t02-'));
42
+ }
43
+ function createPlanningDir(base: string): string {
44
+ const dir = join(base, '.planning');
45
+ mkdirSync(dir, { recursive: true });
46
+ return dir;
47
+ }
48
+ function cleanup(base: string): void {
49
+ rmSync(base, { recursive: true, force: true });
50
+ }
51
+
52
+ // ─── Sample Fixtures ───────────────────────────────────────────────────────
53
+
54
+ const SAMPLE_ROADMAP = `# Project Roadmap
55
+
56
+ ## Phases
57
+
58
+ - [x] 29 — Auth System
59
+ - [ ] 30 — Dashboard
60
+ - [ ] 31 — Notifications
61
+ `;
62
+
63
+ const SAMPLE_PROJECT = `# My Project
64
+
65
+ A sample project for testing the migration parser.
66
+ `;
67
+
68
+ const SAMPLE_MILESTONE_SECTIONED_ROADMAP = `# Project Roadmap
69
+
70
+ ## v2.0 — Foundation
71
+
72
+ <details>
73
+ <summary>Completed</summary>
74
+
75
+ - [x] 01 — Project Setup
76
+ - [x] 02 — Database Schema
77
+
78
+ </details>
79
+
80
+ ## v2.5 — Features
81
+
82
+ - [x] 29 — Auth System
83
+ - [ ] 30 — Dashboard
84
+ - [ ] 31 — Notifications
85
+ `;
86
+
87
+ const SAMPLE_PLAN_XML = `---
88
+ phase: "29-auth-system"
89
+ plan: "01"
90
+ type: "implementation"
91
+ wave: 1
92
+ depends_on: []
93
+ files_modified: [src/auth.ts, src/login.ts]
94
+ autonomous: true
95
+ must_haves:
96
+ truths:
97
+ - Users can log in
98
+ artifacts:
99
+ - src/auth.ts
100
+ key_links: []
101
+ ---
102
+
103
+ # 29-01: Implement Auth
104
+
105
+ <objective>
106
+ Build the authentication system with JWT tokens and session management.
107
+ </objective>
108
+
109
+ <tasks>
110
+ <task>Create auth middleware</task>
111
+ <task>Add login endpoint</task>
112
+ <task>Add logout endpoint</task>
113
+ </tasks>
114
+
115
+ <context>
116
+ The project needs authentication before any other features can be built.
117
+ Auth tokens use JWT with RS256 signing.
118
+ </context>
119
+
120
+ <verification>
121
+ - Login returns valid JWT
122
+ - Middleware rejects invalid tokens
123
+ - Logout invalidates session
124
+ </verification>
125
+
126
+ <success_criteria>
127
+ All auth endpoints respond correctly and tokens are validated.
128
+ </success_criteria>
129
+ `;
130
+
131
+ const SAMPLE_SUMMARY = `---
132
+ phase: "29-auth-system"
133
+ plan: "01"
134
+ subsystem: "auth"
135
+ tags:
136
+ - authentication
137
+ - security
138
+ requires: []
139
+ provides:
140
+ - auth-middleware
141
+ - jwt-validation
142
+ affects:
143
+ - api-routes
144
+ tech-stack:
145
+ - jsonwebtoken
146
+ - express
147
+ key-files:
148
+ - src/auth.ts
149
+ - src/middleware/auth.ts
150
+ key-decisions:
151
+ - Use RS256 for JWT signing
152
+ - Store refresh tokens in DB
153
+ patterns-established:
154
+ - Middleware-based auth
155
+ duration: "2h"
156
+ completed: "2026-01-15"
157
+ ---
158
+
159
+ # 29-01: Auth Implementation Summary
160
+
161
+ Authentication system implemented with JWT tokens.
162
+ `;
163
+
164
+ const SAMPLE_REQUIREMENTS = `# Requirements
165
+
166
+ ## Active
167
+
168
+ ### R001 — User Authentication
169
+ - Status: active
170
+ - Description: Users must be able to log in.
171
+
172
+ ### R002 — Dashboard View
173
+ - Status: active
174
+ - Description: Main dashboard page.
175
+
176
+ ## Validated
177
+
178
+ ### R003 — Session Management
179
+ - Status: validated
180
+ - Description: Sessions expire after 24h.
181
+
182
+ ## Deferred
183
+
184
+ ### R004 — OAuth Support
185
+ - Status: deferred
186
+ - Description: Third-party login.
187
+ `;
188
+
189
+ const SAMPLE_STATE = `# State
190
+
191
+ **Current Phase:** 30-dashboard
192
+ **Status:** in-progress
193
+ `;
194
+
195
+ async function main(): Promise<void> {
196
+
197
+ // ═══════════════════════════════════════════════════════════════════════
198
+ // Validator Tests
199
+ // ═══════════════════════════════════════════════════════════════════════
200
+
201
+ console.log('\n=== Validator: missing directory → fatal ===');
202
+ {
203
+ const base = createFixtureBase();
204
+ try {
205
+ const result = await validatePlanningDirectory(join(base, 'nonexistent'));
206
+ assertEq(result.valid, false, 'missing dir: validation fails');
207
+ assert(result.issues.length > 0, 'missing dir: has issues');
208
+ assert(result.issues.some(i => i.severity === 'fatal'), 'missing dir: has fatal issue');
209
+ } finally {
210
+ cleanup(base);
211
+ }
212
+ }
213
+
214
+ console.log('\n=== Validator: missing ROADMAP.md → warning (not fatal) ===');
215
+ {
216
+ const base = createFixtureBase();
217
+ try {
218
+ const planning = createPlanningDir(base);
219
+ writeFileSync(join(planning, 'PROJECT.md'), SAMPLE_PROJECT);
220
+ const result = await validatePlanningDirectory(planning);
221
+ assertEq(result.valid, true, 'no roadmap: validation still passes');
222
+ assert(result.issues.some(i => i.severity === 'warning' && i.file.includes('ROADMAP')), 'no roadmap: warning issue mentions ROADMAP');
223
+ } finally {
224
+ cleanup(base);
225
+ }
226
+ }
227
+
228
+ console.log('\n=== Validator: missing PROJECT.md → warning ===');
229
+ {
230
+ const base = createFixtureBase();
231
+ try {
232
+ const planning = createPlanningDir(base);
233
+ writeFileSync(join(planning, 'ROADMAP.md'), SAMPLE_ROADMAP);
234
+ const result = await validatePlanningDirectory(planning);
235
+ assertEq(result.valid, true, 'no project: validation passes (warning only)');
236
+ assert(result.issues.some(i => i.severity === 'warning' && i.file.includes('PROJECT')), 'no project: warning issue mentions PROJECT');
237
+ } finally {
238
+ cleanup(base);
239
+ }
240
+ }
241
+
242
+ console.log('\n=== Validator: complete directory → valid with no issues ===');
243
+ {
244
+ const base = createFixtureBase();
245
+ try {
246
+ const planning = createPlanningDir(base);
247
+ writeFileSync(join(planning, 'ROADMAP.md'), SAMPLE_ROADMAP);
248
+ writeFileSync(join(planning, 'PROJECT.md'), SAMPLE_PROJECT);
249
+ writeFileSync(join(planning, 'REQUIREMENTS.md'), SAMPLE_REQUIREMENTS);
250
+ writeFileSync(join(planning, 'STATE.md'), SAMPLE_STATE);
251
+ mkdirSync(join(planning, 'phases'), { recursive: true });
252
+ const result = await validatePlanningDirectory(planning);
253
+ assertEq(result.valid, true, 'complete dir: validation passes');
254
+ assertEq(result.issues.length, 0, 'complete dir: no issues');
255
+ } finally {
256
+ cleanup(base);
257
+ }
258
+ }
259
+
260
+ // ═══════════════════════════════════════════════════════════════════════
261
+ // Roadmap Parser Tests
262
+ // ═══════════════════════════════════════════════════════════════════════
263
+
264
+ console.log('\n=== parseOldRoadmap: flat format ===');
265
+ {
266
+ const roadmap = parseOldRoadmap(SAMPLE_ROADMAP);
267
+ assertEq(roadmap.milestones.length, 0, 'flat roadmap: no milestone sections');
268
+ assertEq(roadmap.phases.length, 3, 'flat roadmap: 3 phases');
269
+ assertEq(roadmap.phases[0].number, 29, 'flat roadmap: first phase number');
270
+ assertEq(roadmap.phases[0].title, 'Auth System', 'flat roadmap: first phase title');
271
+ assertEq(roadmap.phases[0].done, true, 'flat roadmap: first phase done');
272
+ assertEq(roadmap.phases[1].done, false, 'flat roadmap: second phase not done');
273
+ }
274
+
275
+ console.log('\n=== parseOldRoadmap: milestone-sectioned with <details> ===');
276
+ {
277
+ const roadmap = parseOldRoadmap(SAMPLE_MILESTONE_SECTIONED_ROADMAP);
278
+ assert(roadmap.milestones.length >= 2, 'ms roadmap: has milestone sections');
279
+
280
+ const v20 = roadmap.milestones.find(m => m.id.includes('2.0'));
281
+ assert(v20 !== undefined, 'ms roadmap: v2.0 found');
282
+ assertEq(v20?.collapsed, true, 'ms roadmap: v2.0 collapsed');
283
+ assert((v20?.phases.length ?? 0) >= 2, 'ms roadmap: v2.0 has phases');
284
+ assert(v20?.phases.every(p => p.done) ?? false, 'ms roadmap: v2.0 all done');
285
+
286
+ const v25 = roadmap.milestones.find(m => m.id.includes('2.5'));
287
+ assert(v25 !== undefined, 'ms roadmap: v2.5 found');
288
+ assertEq(v25?.collapsed, false, 'ms roadmap: v2.5 not collapsed');
289
+ assert((v25?.phases.length ?? 0) >= 3, 'ms roadmap: v2.5 has 3 phases');
290
+
291
+ const p29 = v25?.phases.find(p => p.number === 29);
292
+ assertEq(p29?.done, true, 'ms roadmap: phase 29 done');
293
+ const p30 = v25?.phases.find(p => p.number === 30);
294
+ assertEq(p30?.done, false, 'ms roadmap: phase 30 not done');
295
+ }
296
+
297
+ // ═══════════════════════════════════════════════════════════════════════
298
+ // Plan Parser Tests
299
+ // ═══════════════════════════════════════════════════════════════════════
300
+
301
+ console.log('\n=== parseOldPlan: XML-in-markdown ===');
302
+ {
303
+ const plan = parseOldPlan(SAMPLE_PLAN_XML, '29-01-PLAN.md', '01');
304
+ assert(plan.objective.includes('authentication'), 'plan: objective extracted');
305
+ assertEq(plan.tasks.length, 3, 'plan: 3 tasks');
306
+ assert(plan.tasks[0].includes('auth middleware'), 'plan: first task content');
307
+ assert(plan.context.includes('JWT'), 'plan: context extracted');
308
+ assert(plan.verification.includes('Login returns'), 'plan: verification extracted');
309
+ assert(plan.successCriteria.includes('endpoints respond'), 'plan: success criteria extracted');
310
+
311
+ // Frontmatter
312
+ assertEq(plan.frontmatter.phase, '29-auth-system', 'plan fm: phase');
313
+ assertEq(plan.frontmatter.plan, '01', 'plan fm: plan');
314
+ assertEq(plan.frontmatter.type, 'implementation', 'plan fm: type');
315
+ assertEq(plan.frontmatter.wave, 1, 'plan fm: wave');
316
+ assertEq(plan.frontmatter.autonomous, true, 'plan fm: autonomous');
317
+ assert(plan.frontmatter.files_modified.length >= 2, 'plan fm: files_modified');
318
+ assert(plan.frontmatter.must_haves !== null, 'plan fm: must_haves parsed');
319
+ assert((plan.frontmatter.must_haves?.truths.length ?? 0) >= 1, 'plan fm: must_haves truths');
320
+ assert((plan.frontmatter.must_haves?.artifacts.length ?? 0) >= 1, 'plan fm: must_haves artifacts');
321
+ }
322
+
323
+ console.log('\n=== parseOldPlan: plain markdown (no XML tags) ===');
324
+ {
325
+ const plainPlan = `# 001: Fix Login Bug
326
+
327
+ ## Description
328
+
329
+ Fix the login button not responding on mobile.
330
+
331
+ ## Steps
332
+
333
+ 1. Debug click handler
334
+ 2. Fix event propagation
335
+ `;
336
+ const plan = parseOldPlan(plainPlan, '001-PLAN.md', '001');
337
+ assertEq(plan.objective, '', 'plain plan: no objective (no XML)');
338
+ assertEq(plan.tasks.length, 0, 'plain plan: no tasks (no XML)');
339
+ assertEq(plan.frontmatter.phase, '', 'plain plan: no frontmatter phase');
340
+ }
341
+
342
+ // ═══════════════════════════════════════════════════════════════════════
343
+ // Summary Parser Tests
344
+ // ═══════════════════════════════════════════════════════════════════════
345
+
346
+ console.log('\n=== parseOldSummary: YAML frontmatter ===');
347
+ {
348
+ const summary = parseOldSummary(SAMPLE_SUMMARY, '29-01-SUMMARY.md', '01');
349
+ assertEq(summary.frontmatter.phase, '29-auth-system', 'summary fm: phase');
350
+ assertEq(summary.frontmatter.plan, '01', 'summary fm: plan');
351
+ assertEq(summary.frontmatter.subsystem, 'auth', 'summary fm: subsystem');
352
+ assertEq(summary.frontmatter.tags, ['authentication', 'security'], 'summary fm: tags');
353
+ assertEq(summary.frontmatter.provides, ['auth-middleware', 'jwt-validation'], 'summary fm: provides');
354
+ assertEq(summary.frontmatter.affects, ['api-routes'], 'summary fm: affects');
355
+ assertEq(summary.frontmatter['tech-stack'], ['jsonwebtoken', 'express'], 'summary fm: tech-stack');
356
+ assertEq(summary.frontmatter['key-files'], ['src/auth.ts', 'src/middleware/auth.ts'], 'summary fm: key-files');
357
+ assertEq(summary.frontmatter['key-decisions'], ['Use RS256 for JWT signing', 'Store refresh tokens in DB'], 'summary fm: key-decisions');
358
+ assertEq(summary.frontmatter['patterns-established'], ['Middleware-based auth'], 'summary fm: patterns-established');
359
+ assertEq(summary.frontmatter.duration, '2h', 'summary fm: duration');
360
+ assertEq(summary.frontmatter.completed, '2026-01-15', 'summary fm: completed');
361
+ assert(summary.body.includes('Auth Implementation Summary'), 'summary: body content present');
362
+ }
363
+
364
+ // ═══════════════════════════════════════════════════════════════════════
365
+ // Requirements Parser Tests
366
+ // ═══════════════════════════════════════════════════════════════════════
367
+
368
+ console.log('\n=== parseOldRequirements ===');
369
+ {
370
+ const reqs = parseOldRequirements(SAMPLE_REQUIREMENTS);
371
+ assertEq(reqs.length, 4, 'requirements: 4 entries');
372
+ assertEq(reqs[0].id, 'R001', 'req 0: id');
373
+ assertEq(reqs[0].title, 'User Authentication', 'req 0: title');
374
+ assertEq(reqs[0].status, 'active', 'req 0: status');
375
+ assert(reqs[0].description.includes('log in'), 'req 0: description');
376
+ assertEq(reqs[2].id, 'R003', 'req 2: id');
377
+ assertEq(reqs[2].status, 'validated', 'req 2: status');
378
+ assertEq(reqs[3].id, 'R004', 'req 3: id');
379
+ assertEq(reqs[3].status, 'deferred', 'req 3: status');
380
+ }
381
+
382
+ // ═══════════════════════════════════════════════════════════════════════
383
+ // State Parser Tests
384
+ // ═══════════════════════════════════════════════════════════════════════
385
+
386
+ console.log('\n=== parseOldState ===');
387
+ {
388
+ const state = parseOldState(SAMPLE_STATE);
389
+ assert(state.currentPhase?.includes('30') ?? false, 'state: current phase includes 30');
390
+ assertEq(state.status, 'in-progress', 'state: status');
391
+ assert(state.raw === SAMPLE_STATE, 'state: raw preserved');
392
+ }
393
+
394
+ // ═══════════════════════════════════════════════════════════════════════
395
+ // Config Parser Tests
396
+ // ═══════════════════════════════════════════════════════════════════════
397
+
398
+ console.log('\n=== parseOldConfig: valid JSON ===');
399
+ {
400
+ const config = parseOldConfig('{"projectName":"test","version":"1.0"}');
401
+ assert(config !== null, 'config: parsed');
402
+ assertEq(config?.projectName, 'test', 'config: projectName');
403
+ }
404
+
405
+ console.log('\n=== parseOldConfig: invalid JSON → null ===');
406
+ {
407
+ const config = parseOldConfig('not json at all {{{');
408
+ assertEq(config, null, 'config: invalid JSON returns null');
409
+ }
410
+
411
+ console.log('\n=== parseOldConfig: non-object JSON → null ===');
412
+ {
413
+ const config = parseOldConfig('"just a string"');
414
+ assertEq(config, null, 'config: non-object returns null');
415
+ }
416
+
417
+ // ═══════════════════════════════════════════════════════════════════════
418
+ // Project Parser Tests
419
+ // ═══════════════════════════════════════════════════════════════════════
420
+
421
+ console.log('\n=== parseOldProject ===');
422
+ {
423
+ const project = parseOldProject(SAMPLE_PROJECT);
424
+ assertEq(project, SAMPLE_PROJECT, 'project: returns raw content');
425
+ }
426
+
427
+ // ═══════════════════════════════════════════════════════════════════════
428
+ // Results
429
+ // ═══════════════════════════════════════════════════════════════════════
430
+
431
+ console.log(`\n${'='.repeat(40)}`);
432
+ console.log(`Results: ${passed} passed, ${failed} failed`);
433
+ if (failed > 0) {
434
+ process.exit(1);
435
+ } else {
436
+ console.log('All tests passed ✓');
437
+ }
438
+ }
439
+
440
+ main().catch((error) => {
441
+ console.error(error);
442
+ process.exit(1);
443
+ });