@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,318 @@
1
+ // Migration writer integration test
2
+ // Writes a complete .gsd tree to a temp dir, verifies file existence,
3
+ // parses key files, and asserts deriveState() returns coherent state.
4
+ // Also tests generatePreview() for correct counts.
5
+
6
+ import { mkdtempSync, existsSync, readFileSync, rmSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { tmpdir } from 'node:os';
9
+
10
+ import { writeGSDDirectory } from '../migrate/writer.ts';
11
+ import { generatePreview } from '../migrate/preview.ts';
12
+ import { parseRoadmap, parsePlan, parseSummary } from '../files.ts';
13
+ import { deriveState } from '../state.ts';
14
+ import type {
15
+ GSDProject,
16
+ GSDMilestone,
17
+ GSDSlice,
18
+ GSDTask,
19
+ GSDRequirement,
20
+ } from '../migrate/types.ts';
21
+
22
+ let passed = 0;
23
+ let failed = 0;
24
+
25
+ function assert(condition: boolean, message: string): void {
26
+ if (condition) {
27
+ passed++;
28
+ } else {
29
+ failed++;
30
+ console.error(` FAIL: ${message}`);
31
+ }
32
+ }
33
+
34
+ function assertEq<T>(actual: T, expected: T, message: string): void {
35
+ if (JSON.stringify(actual) === JSON.stringify(expected)) {
36
+ passed++;
37
+ } else {
38
+ failed++;
39
+ console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
40
+ }
41
+ }
42
+
43
+ // ─── Fixture Builders ──────────────────────────────────────────────────────
44
+
45
+ function makeTask(id: string, title: string, done: boolean, hasSummary: boolean): GSDTask {
46
+ return {
47
+ id,
48
+ title,
49
+ description: `Description for ${title}`,
50
+ done,
51
+ estimate: done ? '1h' : '',
52
+ files: [`src/${id.toLowerCase()}.ts`],
53
+ mustHaves: [`${title} works correctly`],
54
+ summary: hasSummary ? {
55
+ completedAt: '2026-01-15',
56
+ provides: [`${id.toLowerCase()}-feature`],
57
+ keyFiles: [`src/${id.toLowerCase()}.ts`],
58
+ duration: '1h',
59
+ whatHappened: `Implemented ${title} successfully.`,
60
+ } : null,
61
+ };
62
+ }
63
+
64
+ function makeSlice(
65
+ id: string, title: string, done: boolean,
66
+ tasks: GSDTask[], depends: string[],
67
+ hasSummary: boolean,
68
+ ): GSDSlice {
69
+ return {
70
+ id,
71
+ title,
72
+ risk: 'medium' as const,
73
+ depends,
74
+ done,
75
+ demo: `Demo for ${title}`,
76
+ goal: `Goal for ${title}`,
77
+ tasks,
78
+ research: null,
79
+ summary: hasSummary ? {
80
+ completedAt: '2026-01-15',
81
+ provides: [`${id.toLowerCase()}-capability`],
82
+ keyFiles: tasks.map(t => `src/${t.id.toLowerCase()}.ts`),
83
+ keyDecisions: ['Used standard patterns'],
84
+ patternsEstablished: ['Integration pattern'],
85
+ duration: '2h',
86
+ whatHappened: `Completed ${title} with all tasks done.`,
87
+ } : null,
88
+ };
89
+ }
90
+
91
+ function buildIncompleteProject(): GSDProject {
92
+ const t01 = makeTask('T01', 'Setup Database', true, true);
93
+ const t02 = makeTask('T02', 'Add Auth Middleware', true, true);
94
+ const s01 = makeSlice('S01', 'Auth Foundation', true, [t01, t02], [], true);
95
+
96
+ const t03 = makeTask('T03', 'Build Dashboard UI', false, false);
97
+ const s02 = makeSlice('S02', 'Dashboard', false, [t03], ['S01'], false);
98
+
99
+ const milestone: GSDMilestone = {
100
+ id: 'M001',
101
+ title: 'MVP Launch',
102
+ vision: 'Ship the minimum viable product',
103
+ successCriteria: ['Users can log in', 'Dashboard renders data'],
104
+ slices: [s01, s02],
105
+ research: '# Research\n\nMarket analysis for MVP features.\n',
106
+ boundaryMap: [],
107
+ };
108
+
109
+ const requirements: GSDRequirement[] = [
110
+ { id: 'R001', title: 'User Authentication', class: 'core-capability', status: 'validated', description: 'Users must authenticate.', source: 'stakeholder', primarySlice: 'S01' },
111
+ { id: 'R002', title: 'Dashboard View', class: 'core-capability', status: 'active', description: 'Dashboard shows data.', source: 'stakeholder', primarySlice: 'S02' },
112
+ { id: 'R003', title: 'Export to PDF', class: 'nice-to-have', status: 'deferred', description: 'PDF export.', source: 'inferred', primarySlice: 'none yet' },
113
+ { id: 'R004', title: 'Legacy Reports', class: 'deprecated', status: 'out-of-scope', description: 'Old reporting.', source: 'inferred', primarySlice: 'none yet' },
114
+ ];
115
+
116
+ return {
117
+ milestones: [milestone],
118
+ projectContent: '# My Project\n\nA test project for migration.\n',
119
+ requirements,
120
+ decisionsContent: '',
121
+ };
122
+ }
123
+
124
+ function buildCompleteProject(): GSDProject {
125
+ const t01 = makeTask('T01', 'Only Task', true, true);
126
+ const s01 = makeSlice('S01', 'Only Slice', true, [t01], [], true);
127
+
128
+ const milestone: GSDMilestone = {
129
+ id: 'M001',
130
+ title: 'Complete Milestone',
131
+ vision: 'Everything done',
132
+ successCriteria: ['All done'],
133
+ slices: [s01],
134
+ research: null,
135
+ boundaryMap: [],
136
+ };
137
+
138
+ return {
139
+ milestones: [milestone],
140
+ projectContent: '# Done Project\n',
141
+ requirements: [],
142
+ decisionsContent: '# Decisions\n\n| ID | Decision | Rationale | Date |\n',
143
+ };
144
+ }
145
+
146
+ // ═══════════════════════════════════════════════════════════════════════════
147
+ // Tests
148
+ // ═══════════════════════════════════════════════════════════════════════════
149
+
150
+ async function main(): Promise<void> {
151
+
152
+ // ─── Scenario 1: Incomplete project ────────────────────────────────────
153
+ console.log('\n=== Scenario 1: Incomplete project — write, parse, deriveState ===');
154
+ {
155
+ const base = mkdtempSync(join(tmpdir(), 'gsd-writer-int-'));
156
+ try {
157
+ const project = buildIncompleteProject();
158
+ const result = await writeGSDDirectory(project, base);
159
+
160
+ // (a) Key files exist
161
+ console.log(' --- file existence ---');
162
+ const gsd = join(base, '.gsd');
163
+ const m = join(gsd, 'milestones', 'M001');
164
+
165
+ assert(existsSync(join(m, 'M001-ROADMAP.md')), 'incomplete: M001-ROADMAP.md exists');
166
+ assert(existsSync(join(m, 'M001-CONTEXT.md')), 'incomplete: M001-CONTEXT.md exists');
167
+ assert(existsSync(join(m, 'M001-RESEARCH.md')), 'incomplete: M001-RESEARCH.md exists');
168
+ assert(existsSync(join(m, 'slices', 'S01', 'S01-PLAN.md')), 'incomplete: S01-PLAN.md exists');
169
+ assert(existsSync(join(m, 'slices', 'S02', 'S02-PLAN.md')), 'incomplete: S02-PLAN.md exists');
170
+ assert(existsSync(join(m, 'slices', 'S01', 'S01-SUMMARY.md')), 'incomplete: S01-SUMMARY.md exists');
171
+ assert(!existsSync(join(m, 'slices', 'S02', 'S02-SUMMARY.md')), 'incomplete: S02-SUMMARY.md NOT written (null)');
172
+ assert(existsSync(join(gsd, 'REQUIREMENTS.md')), 'incomplete: REQUIREMENTS.md exists');
173
+ assert(existsSync(join(gsd, 'PROJECT.md')), 'incomplete: PROJECT.md exists');
174
+ assert(existsSync(join(gsd, 'DECISIONS.md')), 'incomplete: DECISIONS.md exists');
175
+ assert(existsSync(join(gsd, 'STATE.md')), 'incomplete: STATE.md exists');
176
+
177
+ // Task files
178
+ assert(existsSync(join(m, 'slices', 'S01', 'tasks', 'T01-PLAN.md')), 'incomplete: T01-PLAN.md exists');
179
+ assert(existsSync(join(m, 'slices', 'S01', 'tasks', 'T01-SUMMARY.md')), 'incomplete: T01-SUMMARY.md exists');
180
+ assert(existsSync(join(m, 'slices', 'S01', 'tasks', 'T02-PLAN.md')), 'incomplete: T02-PLAN.md exists (auth task)');
181
+ assert(existsSync(join(m, 'slices', 'S01', 'tasks', 'T02-SUMMARY.md')), 'incomplete: T02-SUMMARY.md exists (auth task)');
182
+ assert(existsSync(join(m, 'slices', 'S02', 'tasks', 'T03-PLAN.md')), 'incomplete: T03-PLAN.md exists');
183
+ assert(!existsSync(join(m, 'slices', 'S02', 'tasks', 'T03-SUMMARY.md')), 'incomplete: T03-SUMMARY.md NOT written (null)');
184
+
185
+ // WrittenFiles counts
186
+ console.log(' --- WrittenFiles counts ---');
187
+ assertEq(result.counts.roadmaps, 1, 'incomplete: WrittenFiles roadmaps count');
188
+ assertEq(result.counts.plans, 2, 'incomplete: WrittenFiles plans count');
189
+ assertEq(result.counts.taskPlans, 3, 'incomplete: WrittenFiles taskPlans count');
190
+ assertEq(result.counts.taskSummaries, 2, 'incomplete: WrittenFiles taskSummaries count');
191
+ assertEq(result.counts.sliceSummaries, 1, 'incomplete: WrittenFiles sliceSummaries count');
192
+ assertEq(result.counts.research, 1, 'incomplete: WrittenFiles research count');
193
+ assertEq(result.counts.requirements, 1, 'incomplete: WrittenFiles requirements count');
194
+ assertEq(result.counts.contexts, 1, 'incomplete: WrittenFiles contexts count');
195
+
196
+ // (b) parseRoadmap on written roadmap
197
+ console.log(' --- parseRoadmap ---');
198
+ const roadmapContent = readFileSync(join(m, 'M001-ROADMAP.md'), 'utf-8');
199
+ const roadmap = parseRoadmap(roadmapContent);
200
+ assertEq(roadmap.slices.length, 2, 'incomplete: roadmap has 2 slices');
201
+ assert(roadmap.slices[0].done === true, 'incomplete: roadmap S01 is done');
202
+ assert(roadmap.slices[1].done === false, 'incomplete: roadmap S02 is not done');
203
+ assertEq(roadmap.slices[0].id, 'S01', 'incomplete: roadmap slice 0 id');
204
+ assertEq(roadmap.slices[1].id, 'S02', 'incomplete: roadmap slice 1 id');
205
+
206
+ // (c) parsePlan on S01 plan
207
+ console.log(' --- parsePlan S01 ---');
208
+ const s01PlanContent = readFileSync(join(m, 'slices', 'S01', 'S01-PLAN.md'), 'utf-8');
209
+ const s01Plan = parsePlan(s01PlanContent);
210
+ assertEq(s01Plan.tasks.length, 2, 'incomplete: S01 plan has 2 tasks');
211
+ assert(s01Plan.tasks[0].done === true, 'incomplete: S01 T01 is done');
212
+ assert(s01Plan.tasks[1].done === true, 'incomplete: S01 T02 is done');
213
+
214
+ // (d) parseSummary on S01 summary
215
+ console.log(' --- parseSummary S01 ---');
216
+ const s01SummaryContent = readFileSync(join(m, 'slices', 'S01', 'S01-SUMMARY.md'), 'utf-8');
217
+ const s01Summary = parseSummary(s01SummaryContent);
218
+ assert(
219
+ (s01Summary.frontmatter.key_files as string[]).length > 0,
220
+ 'incomplete: S01 summary has key_files',
221
+ );
222
+ assert(
223
+ (s01Summary.frontmatter.provides as string[]).length > 0,
224
+ 'incomplete: S01 summary has provides',
225
+ );
226
+
227
+ // (e) deriveState
228
+ console.log(' --- deriveState ---');
229
+ const state = await deriveState(base);
230
+ assertEq(state.phase, 'executing', 'incomplete: deriveState phase is executing');
231
+ assert(state.activeMilestone !== null, 'incomplete: deriveState has activeMilestone');
232
+ assertEq(state.activeMilestone!.id, 'M001', 'incomplete: deriveState activeMilestone is M001');
233
+ assert(state.activeSlice !== null, 'incomplete: deriveState has activeSlice');
234
+ assertEq(state.activeSlice!.id, 'S02', 'incomplete: deriveState activeSlice is S02');
235
+ assert(state.activeTask !== null, 'incomplete: deriveState has activeTask');
236
+ assertEq(state.activeTask!.id, 'T03', 'incomplete: deriveState activeTask is T03');
237
+ assert(state.progress.slices !== undefined, 'incomplete: deriveState has slices progress');
238
+ assertEq(state.progress.slices!.done, 1, 'incomplete: deriveState slices done count');
239
+ assertEq(state.progress.slices!.total, 2, 'incomplete: deriveState slices total count');
240
+ assert(state.progress.tasks !== undefined, 'incomplete: deriveState has tasks progress');
241
+ // S02 has 1 task, 0 done (only active slice tasks counted)
242
+ assertEq(state.progress.tasks!.done, 0, 'incomplete: deriveState tasks done (in active slice)');
243
+ assertEq(state.progress.tasks!.total, 1, 'incomplete: deriveState tasks total (in active slice)');
244
+ // Requirements
245
+ assertEq(state.requirements.active, 1, 'incomplete: deriveState requirements active');
246
+ assertEq(state.requirements.validated, 1, 'incomplete: deriveState requirements validated');
247
+ assertEq(state.requirements.deferred, 1, 'incomplete: deriveState requirements deferred');
248
+ assertEq(state.requirements.outOfScope, 1, 'incomplete: deriveState requirements outOfScope');
249
+
250
+ // (f) generatePreview
251
+ console.log(' --- generatePreview ---');
252
+ const preview = generatePreview(project);
253
+ assertEq(preview.milestoneCount, 1, 'incomplete: preview milestoneCount');
254
+ assertEq(preview.totalSlices, 2, 'incomplete: preview totalSlices');
255
+ assertEq(preview.totalTasks, 3, 'incomplete: preview totalTasks');
256
+ assertEq(preview.doneSlices, 1, 'incomplete: preview doneSlices');
257
+ assertEq(preview.doneTasks, 2, 'incomplete: preview doneTasks');
258
+ assertEq(preview.sliceCompletionPct, 50, 'incomplete: preview sliceCompletionPct');
259
+ assertEq(preview.taskCompletionPct, 67, 'incomplete: preview taskCompletionPct');
260
+ assertEq(preview.requirements.active, 1, 'incomplete: preview requirements active');
261
+ assertEq(preview.requirements.validated, 1, 'incomplete: preview requirements validated');
262
+ assertEq(preview.requirements.deferred, 1, 'incomplete: preview requirements deferred');
263
+ assertEq(preview.requirements.outOfScope, 1, 'incomplete: preview requirements outOfScope');
264
+ assertEq(preview.requirements.total, 4, 'incomplete: preview requirements total');
265
+
266
+ } finally {
267
+ rmSync(base, { recursive: true, force: true });
268
+ }
269
+ }
270
+
271
+ // ─── Scenario 2: Fully complete project ────────────────────────────────
272
+ console.log('\n=== Scenario 2: Fully complete project — deriveState phase ===');
273
+ {
274
+ const base = mkdtempSync(join(tmpdir(), 'gsd-writer-int-complete-'));
275
+ try {
276
+ const project = buildCompleteProject();
277
+ await writeGSDDirectory(project, base);
278
+
279
+ // Null research should NOT produce a file
280
+ const m = join(base, '.gsd', 'milestones', 'M001');
281
+ assert(!existsSync(join(m, 'M001-RESEARCH.md')), 'complete: M001-RESEARCH.md NOT written (null)');
282
+ // No REQUIREMENTS.md since empty requirements
283
+ assert(!existsSync(join(base, '.gsd', 'REQUIREMENTS.md')), 'complete: REQUIREMENTS.md NOT written (empty)');
284
+
285
+ // deriveState: all slices done, all tasks done — needs milestone summary for 'complete'
286
+ // Without milestone summary, it should be 'completing-milestone' or 'summarizing'
287
+ const state = await deriveState(base);
288
+ // All slices are done in roadmap. Milestone summary doesn't exist.
289
+ // deriveState should return 'completing-milestone' since all slices done but no milestone summary.
290
+ assertEq(state.phase, 'completing-milestone', 'complete: deriveState phase is completing-milestone');
291
+ assert(state.activeMilestone !== null, 'complete: deriveState has activeMilestone');
292
+ assertEq(state.activeMilestone!.id, 'M001', 'complete: deriveState activeMilestone is M001');
293
+
294
+ // generatePreview for complete project
295
+ const preview = generatePreview(project);
296
+ assertEq(preview.milestoneCount, 1, 'complete: preview milestoneCount');
297
+ assertEq(preview.totalSlices, 1, 'complete: preview totalSlices');
298
+ assertEq(preview.doneSlices, 1, 'complete: preview doneSlices');
299
+ assertEq(preview.totalTasks, 1, 'complete: preview totalTasks');
300
+ assertEq(preview.doneTasks, 1, 'complete: preview doneTasks');
301
+ assertEq(preview.sliceCompletionPct, 100, 'complete: preview sliceCompletionPct');
302
+ assertEq(preview.taskCompletionPct, 100, 'complete: preview taskCompletionPct');
303
+ assertEq(preview.requirements.total, 0, 'complete: preview requirements total');
304
+
305
+ } finally {
306
+ rmSync(base, { recursive: true, force: true });
307
+ }
308
+ }
309
+
310
+ // ─── Results ─────────────────────────────────────────────────────────────
311
+ console.log(`\n${passed + failed} assertions: ${passed} passed, ${failed} failed`);
312
+ if (failed > 0) process.exit(1);
313
+ }
314
+
315
+ main().catch((err) => {
316
+ console.error('Unhandled error:', err);
317
+ process.exit(1);
318
+ });