@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.
- package/LICENSE +21 -0
- package/README.md +453 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +269 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +70 -0
- package/dist/logo.d.ts +16 -0
- package/dist/logo.js +25 -0
- package/dist/onboarding.d.ts +43 -0
- package/dist/onboarding.js +418 -0
- package/dist/pi-migration.d.ts +14 -0
- package/dist/pi-migration.js +57 -0
- package/dist/resource-loader.d.ts +22 -0
- package/dist/resource-loader.js +60 -0
- package/dist/tool-bootstrap.d.ts +4 -0
- package/dist/tool-bootstrap.js +74 -0
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.js +25 -0
- package/package.json +60 -0
- package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
- package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +127 -0
- package/src/resources/GSD-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +249 -0
- package/src/resources/extensions/bg-shell/index.ts +2808 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4989 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/google-search/index.ts +323 -0
- package/src/resources/extensions/google-search/package.json +9 -0
- package/src/resources/extensions/gsd/activity-log.ts +69 -0
- package/src/resources/extensions/gsd/auto.ts +2744 -0
- package/src/resources/extensions/gsd/commands.ts +313 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
- package/src/resources/extensions/gsd/doctor.ts +690 -0
- package/src/resources/extensions/gsd/files.ts +732 -0
- package/src/resources/extensions/gsd/git-service.ts +597 -0
- package/src/resources/extensions/gsd/gitignore.ts +168 -0
- package/src/resources/extensions/gsd/guided-flow.ts +817 -0
- package/src/resources/extensions/gsd/index.ts +558 -0
- package/src/resources/extensions/gsd/metrics.ts +374 -0
- package/src/resources/extensions/gsd/migrate/command.ts +218 -0
- package/src/resources/extensions/gsd/migrate/index.ts +42 -0
- package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
- package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
- package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
- package/src/resources/extensions/gsd/migrate/types.ts +370 -0
- package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
- package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
- package/src/resources/extensions/gsd/observability-validator.ts +408 -0
- package/src/resources/extensions/gsd/package.json +11 -0
- package/src/resources/extensions/gsd/paths.ts +308 -0
- package/src/resources/extensions/gsd/preferences.ts +757 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
- package/src/resources/extensions/gsd/prompts/queue.md +85 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
- package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
- package/src/resources/extensions/gsd/prompts/system.md +187 -0
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
- package/src/resources/extensions/gsd/session-forensics.ts +487 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
- package/src/resources/extensions/gsd/state.ts +460 -0
- package/src/resources/extensions/gsd/templates/context.md +76 -0
- package/src/resources/extensions/gsd/templates/decisions.md +8 -0
- package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/gsd/templates/plan.md +131 -0
- package/src/resources/extensions/gsd/templates/preferences.md +24 -0
- package/src/resources/extensions/gsd/templates/project.md +31 -0
- package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
- package/src/resources/extensions/gsd/templates/requirements.md +81 -0
- package/src/resources/extensions/gsd/templates/research.md +46 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
- package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
- package/src/resources/extensions/gsd/templates/state.md +19 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
- package/src/resources/extensions/gsd/templates/uat.md +54 -0
- package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
- package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
- package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
- package/src/resources/extensions/gsd/types.ts +159 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
- package/src/resources/extensions/gsd/workspace-index.ts +203 -0
- package/src/resources/extensions/gsd/worktree-command.ts +845 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
- package/src/resources/extensions/gsd/worktree.ts +183 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/mcporter/index.ts +429 -0
- package/src/resources/extensions/remote-questions/config.ts +81 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
- package/src/resources/extensions/remote-questions/format.ts +163 -0
- package/src/resources/extensions/remote-questions/manager.ts +192 -0
- package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
- package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
- package/src/resources/extensions/remote-questions/status.ts +31 -0
- package/src/resources/extensions/remote-questions/store.ts +77 -0
- package/src/resources/extensions/remote-questions/types.ts +75 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +65 -0
- package/src/resources/extensions/search-the-web/native-search.ts +157 -0
- package/src/resources/extensions/search-the-web/provider.ts +118 -0
- package/src/resources/extensions/search-the-web/tavily.ts +116 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +613 -0
- package/src/resources/extensions/shared/next-action-ui.ts +197 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/terminal.ts +23 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +88 -0
- package/src/resources/extensions/slash-commands/clear.ts +10 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1020 -0
- package/src/resources/extensions/voice/index.ts +195 -0
- package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- 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
|
+
});
|