@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,420 @@
|
|
|
1
|
+
// Migration writer format round-trip test suite
|
|
2
|
+
// Tests that format functions produce output that parses back correctly
|
|
3
|
+
// through parseRoadmap(), parsePlan(), parseSummary(), and parseRequirementCounts().
|
|
4
|
+
// Pure in-memory tests — no filesystem needed.
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
formatRoadmap,
|
|
8
|
+
formatPlan,
|
|
9
|
+
formatSliceSummary,
|
|
10
|
+
formatTaskSummary,
|
|
11
|
+
formatTaskPlan,
|
|
12
|
+
formatRequirements,
|
|
13
|
+
formatProject,
|
|
14
|
+
formatDecisions,
|
|
15
|
+
formatContext,
|
|
16
|
+
formatState,
|
|
17
|
+
} from '../migrate/writer.ts';
|
|
18
|
+
import {
|
|
19
|
+
parseRoadmap,
|
|
20
|
+
parsePlan,
|
|
21
|
+
parseSummary,
|
|
22
|
+
parseRequirementCounts,
|
|
23
|
+
} from '../files.ts';
|
|
24
|
+
import type {
|
|
25
|
+
GSDMilestone,
|
|
26
|
+
GSDSlice,
|
|
27
|
+
GSDTask,
|
|
28
|
+
GSDRequirement,
|
|
29
|
+
GSDSliceSummaryData,
|
|
30
|
+
GSDTaskSummaryData,
|
|
31
|
+
} from '../migrate/types.ts';
|
|
32
|
+
|
|
33
|
+
let passed = 0;
|
|
34
|
+
let failed = 0;
|
|
35
|
+
|
|
36
|
+
function assert(condition: boolean, message: string): void {
|
|
37
|
+
if (condition) {
|
|
38
|
+
passed++;
|
|
39
|
+
} else {
|
|
40
|
+
failed++;
|
|
41
|
+
console.error(`FAIL: ${message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assertEq(actual: unknown, expected: unknown, message: string): void {
|
|
46
|
+
const a = JSON.stringify(actual);
|
|
47
|
+
const e = JSON.stringify(expected);
|
|
48
|
+
if (a === e) {
|
|
49
|
+
passed++;
|
|
50
|
+
} else {
|
|
51
|
+
failed++;
|
|
52
|
+
console.error(`FAIL: ${message} — expected ${e}, got ${a}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Test Data Builders ────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
function makeTask(overrides: Partial<GSDTask> = {}): GSDTask {
|
|
59
|
+
return {
|
|
60
|
+
id: 'T01',
|
|
61
|
+
title: 'Setup Auth',
|
|
62
|
+
description: 'Implement authentication',
|
|
63
|
+
done: false,
|
|
64
|
+
estimate: '30m',
|
|
65
|
+
files: ['src/auth.ts'],
|
|
66
|
+
mustHaves: ['JWT support'],
|
|
67
|
+
summary: null,
|
|
68
|
+
...overrides,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function makeSlice(overrides: Partial<GSDSlice> = {}): GSDSlice {
|
|
73
|
+
return {
|
|
74
|
+
id: 'S01',
|
|
75
|
+
title: 'Auth System',
|
|
76
|
+
risk: 'medium' as const,
|
|
77
|
+
depends: [],
|
|
78
|
+
done: false,
|
|
79
|
+
demo: 'Login flow works end-to-end',
|
|
80
|
+
goal: 'Working authentication',
|
|
81
|
+
tasks: [makeTask()],
|
|
82
|
+
research: null,
|
|
83
|
+
summary: null,
|
|
84
|
+
...overrides,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function makeMilestone(overrides: Partial<GSDMilestone> = {}): GSDMilestone {
|
|
89
|
+
return {
|
|
90
|
+
id: 'M001',
|
|
91
|
+
title: 'Core Platform',
|
|
92
|
+
vision: 'Build the core platform',
|
|
93
|
+
successCriteria: ['All tests pass', 'Deploy to staging'],
|
|
94
|
+
slices: [makeSlice()],
|
|
95
|
+
research: null,
|
|
96
|
+
boundaryMap: [],
|
|
97
|
+
...overrides,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function makeSliceSummary(overrides: Partial<GSDSliceSummaryData> = {}): GSDSliceSummaryData {
|
|
102
|
+
return {
|
|
103
|
+
completedAt: '2026-03-10',
|
|
104
|
+
provides: ['auth-flow', 'jwt-tokens'],
|
|
105
|
+
keyFiles: ['src/auth.ts', 'src/middleware.ts'],
|
|
106
|
+
keyDecisions: ['Use JWT over sessions'],
|
|
107
|
+
patternsEstablished: ['Middleware pattern'],
|
|
108
|
+
duration: '2h',
|
|
109
|
+
whatHappened: 'Implemented full auth system with JWT.',
|
|
110
|
+
...overrides,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function makeTaskSummary(overrides: Partial<GSDTaskSummaryData> = {}): GSDTaskSummaryData {
|
|
115
|
+
return {
|
|
116
|
+
completedAt: '2026-03-09',
|
|
117
|
+
provides: ['auth-endpoint'],
|
|
118
|
+
keyFiles: ['src/auth.ts'],
|
|
119
|
+
duration: '45m',
|
|
120
|
+
whatHappened: 'Built the auth endpoint.',
|
|
121
|
+
...overrides,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
|
+
// Scenario A: Roadmap round-trip with 2 slices (1 done, 1 not)
|
|
127
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
128
|
+
|
|
129
|
+
{
|
|
130
|
+
const milestone = makeMilestone({
|
|
131
|
+
slices: [
|
|
132
|
+
makeSlice({
|
|
133
|
+
id: 'S01',
|
|
134
|
+
title: 'Auth System',
|
|
135
|
+
risk: 'high',
|
|
136
|
+
depends: [],
|
|
137
|
+
done: true,
|
|
138
|
+
demo: 'Login flow works',
|
|
139
|
+
}),
|
|
140
|
+
makeSlice({
|
|
141
|
+
id: 'S02',
|
|
142
|
+
title: 'Dashboard',
|
|
143
|
+
risk: 'low',
|
|
144
|
+
depends: ['S01'],
|
|
145
|
+
done: false,
|
|
146
|
+
demo: 'Dashboard renders data',
|
|
147
|
+
}),
|
|
148
|
+
],
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const output = formatRoadmap(milestone);
|
|
152
|
+
const parsed = parseRoadmap(output);
|
|
153
|
+
|
|
154
|
+
assertEq(parsed.title, 'M001: Core Platform', 'roadmap: title');
|
|
155
|
+
assertEq(parsed.vision, 'Build the core platform', 'roadmap: vision');
|
|
156
|
+
assertEq(parsed.successCriteria.length, 2, 'roadmap: successCriteria count');
|
|
157
|
+
assertEq(parsed.successCriteria[0], 'All tests pass', 'roadmap: successCriteria[0]');
|
|
158
|
+
assertEq(parsed.successCriteria[1], 'Deploy to staging', 'roadmap: successCriteria[1]');
|
|
159
|
+
assertEq(parsed.slices.length, 2, 'roadmap: slices count');
|
|
160
|
+
|
|
161
|
+
assertEq(parsed.slices[0].id, 'S01', 'roadmap: S01 id');
|
|
162
|
+
assertEq(parsed.slices[0].title, 'Auth System', 'roadmap: S01 title');
|
|
163
|
+
assertEq(parsed.slices[0].done, true, 'roadmap: S01 done');
|
|
164
|
+
assertEq(parsed.slices[0].risk, 'high', 'roadmap: S01 risk');
|
|
165
|
+
assertEq(parsed.slices[0].depends.length, 0, 'roadmap: S01 depends empty');
|
|
166
|
+
assertEq(parsed.slices[0].demo, 'Login flow works', 'roadmap: S01 demo');
|
|
167
|
+
|
|
168
|
+
assertEq(parsed.slices[1].id, 'S02', 'roadmap: S02 id');
|
|
169
|
+
assertEq(parsed.slices[1].title, 'Dashboard', 'roadmap: S02 title');
|
|
170
|
+
assertEq(parsed.slices[1].done, false, 'roadmap: S02 done');
|
|
171
|
+
assertEq(parsed.slices[1].risk, 'low', 'roadmap: S02 risk');
|
|
172
|
+
assertEq(parsed.slices[1].depends, ['S01'], 'roadmap: S02 depends');
|
|
173
|
+
assertEq(parsed.slices[1].demo, 'Dashboard renders data', 'roadmap: S02 demo');
|
|
174
|
+
|
|
175
|
+
assertEq(parsed.boundaryMap.length, 0, 'roadmap: boundaryMap empty');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
179
|
+
// Scenario B: Plan round-trip with 3 tasks (mixed done)
|
|
180
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
181
|
+
|
|
182
|
+
{
|
|
183
|
+
const slice = makeSlice({
|
|
184
|
+
id: 'S01',
|
|
185
|
+
title: 'Auth System',
|
|
186
|
+
goal: 'Working authentication system',
|
|
187
|
+
demo: 'Login works with valid credentials',
|
|
188
|
+
tasks: [
|
|
189
|
+
makeTask({ id: 'T01', title: 'Setup Models', done: true, estimate: '15m', description: 'Define user model' }),
|
|
190
|
+
makeTask({ id: 'T02', title: 'Build Endpoints', done: false, estimate: '30m', description: 'REST API endpoints' }),
|
|
191
|
+
makeTask({ id: 'T03', title: 'Write Tests', done: true, estimate: '20m', description: 'Unit and integration tests' }),
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const output = formatPlan(slice);
|
|
196
|
+
const parsed = parsePlan(output);
|
|
197
|
+
|
|
198
|
+
assertEq(parsed.id, 'S01', 'plan: id');
|
|
199
|
+
assertEq(parsed.title, 'Auth System', 'plan: title');
|
|
200
|
+
assertEq(parsed.goal, 'Working authentication system', 'plan: goal');
|
|
201
|
+
assertEq(parsed.demo, 'Login works with valid credentials', 'plan: demo');
|
|
202
|
+
assertEq(parsed.tasks.length, 3, 'plan: tasks count');
|
|
203
|
+
|
|
204
|
+
assertEq(parsed.tasks[0].id, 'T01', 'plan: T01 id');
|
|
205
|
+
assertEq(parsed.tasks[0].title, 'Setup Models', 'plan: T01 title');
|
|
206
|
+
assertEq(parsed.tasks[0].done, true, 'plan: T01 done');
|
|
207
|
+
assertEq(parsed.tasks[0].estimate, '15m', 'plan: T01 estimate');
|
|
208
|
+
|
|
209
|
+
assertEq(parsed.tasks[1].id, 'T02', 'plan: T02 id');
|
|
210
|
+
assertEq(parsed.tasks[1].done, false, 'plan: T02 done');
|
|
211
|
+
assertEq(parsed.tasks[1].estimate, '30m', 'plan: T02 estimate');
|
|
212
|
+
|
|
213
|
+
assertEq(parsed.tasks[2].id, 'T03', 'plan: T03 id');
|
|
214
|
+
assertEq(parsed.tasks[2].done, true, 'plan: T03 done');
|
|
215
|
+
assertEq(parsed.tasks[2].estimate, '20m', 'plan: T03 estimate');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
219
|
+
// Scenario C: Slice summary round-trip with full data
|
|
220
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
221
|
+
|
|
222
|
+
{
|
|
223
|
+
const slice = makeSlice({
|
|
224
|
+
id: 'S01',
|
|
225
|
+
title: 'Auth System',
|
|
226
|
+
done: true,
|
|
227
|
+
summary: makeSliceSummary(),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const output = formatSliceSummary(slice, 'M001');
|
|
231
|
+
const parsed = parseSummary(output);
|
|
232
|
+
|
|
233
|
+
assertEq(parsed.frontmatter.id, 'S01', 'sliceSummary: id');
|
|
234
|
+
assertEq(parsed.frontmatter.parent, 'M001', 'sliceSummary: parent');
|
|
235
|
+
assertEq(parsed.frontmatter.milestone, 'M001', 'sliceSummary: milestone');
|
|
236
|
+
assertEq(parsed.frontmatter.provides, ['auth-flow', 'jwt-tokens'], 'sliceSummary: provides');
|
|
237
|
+
assertEq(parsed.frontmatter.requires.length, 0, 'sliceSummary: requires empty');
|
|
238
|
+
assertEq(parsed.frontmatter.affects.length, 0, 'sliceSummary: affects empty');
|
|
239
|
+
assertEq(parsed.frontmatter.key_files, ['src/auth.ts', 'src/middleware.ts'], 'sliceSummary: key_files');
|
|
240
|
+
assertEq(parsed.frontmatter.key_decisions, ['Use JWT over sessions'], 'sliceSummary: key_decisions');
|
|
241
|
+
assertEq(parsed.frontmatter.patterns_established, ['Middleware pattern'], 'sliceSummary: patterns_established');
|
|
242
|
+
assertEq(parsed.frontmatter.duration, '2h', 'sliceSummary: duration');
|
|
243
|
+
assertEq(parsed.frontmatter.completed_at, '2026-03-10', 'sliceSummary: completed_at');
|
|
244
|
+
assertEq(parsed.frontmatter.verification_result, 'passed', 'sliceSummary: verification_result');
|
|
245
|
+
assertEq(parsed.frontmatter.blocker_discovered, false, 'sliceSummary: blocker_discovered');
|
|
246
|
+
assert(parsed.whatHappened.includes('Implemented full auth system'), 'sliceSummary: whatHappened content');
|
|
247
|
+
assertEq(parsed.title, 'S01: Auth System', 'sliceSummary: title');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
251
|
+
// Scenario D: Task summary round-trip
|
|
252
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
253
|
+
|
|
254
|
+
{
|
|
255
|
+
const task = makeTask({
|
|
256
|
+
id: 'T01',
|
|
257
|
+
title: 'Setup Auth',
|
|
258
|
+
done: true,
|
|
259
|
+
summary: makeTaskSummary(),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const output = formatTaskSummary(task, 'S01', 'M001');
|
|
263
|
+
const parsed = parseSummary(output);
|
|
264
|
+
|
|
265
|
+
assertEq(parsed.frontmatter.id, 'T01', 'taskSummary: id');
|
|
266
|
+
assertEq(parsed.frontmatter.parent, 'S01', 'taskSummary: parent');
|
|
267
|
+
assertEq(parsed.frontmatter.milestone, 'M001', 'taskSummary: milestone');
|
|
268
|
+
assertEq(parsed.frontmatter.provides, ['auth-endpoint'], 'taskSummary: provides');
|
|
269
|
+
assertEq(parsed.frontmatter.key_files, ['src/auth.ts'], 'taskSummary: key_files');
|
|
270
|
+
assertEq(parsed.frontmatter.duration, '45m', 'taskSummary: duration');
|
|
271
|
+
assertEq(parsed.frontmatter.completed_at, '2026-03-09', 'taskSummary: completed_at');
|
|
272
|
+
assert(parsed.whatHappened.includes('Built the auth endpoint'), 'taskSummary: whatHappened content');
|
|
273
|
+
assertEq(parsed.title, 'T01: Setup Auth', 'taskSummary: title');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
277
|
+
// Scenario E: Requirements round-trip with mixed statuses
|
|
278
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
279
|
+
|
|
280
|
+
{
|
|
281
|
+
const requirements: GSDRequirement[] = [
|
|
282
|
+
{ id: 'R001', title: 'Auth Required', class: 'core-capability', status: 'active', description: 'Must have auth', source: 'spec', primarySlice: 'S01' },
|
|
283
|
+
{ id: 'R002', title: 'Logging', class: 'observability', status: 'active', description: 'Must log', source: 'spec', primarySlice: 'S02' },
|
|
284
|
+
{ id: 'R003', title: 'OAuth Support', class: 'core-capability', status: 'validated', description: 'OAuth working', source: 'testing', primarySlice: 'S01' },
|
|
285
|
+
{ id: 'R004', title: 'Dark Mode', class: 'ui', status: 'deferred', description: 'Nice to have', source: 'feedback', primarySlice: 'none' },
|
|
286
|
+
{ id: 'R005', title: 'Legacy API', class: 'compat', status: 'out-of-scope', description: 'Dropped', source: 'decision', primarySlice: 'none' },
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
const output = formatRequirements(requirements);
|
|
290
|
+
const counts = parseRequirementCounts(output);
|
|
291
|
+
|
|
292
|
+
assertEq(counts.active, 2, 'requirements: active count');
|
|
293
|
+
assertEq(counts.validated, 1, 'requirements: validated count');
|
|
294
|
+
assertEq(counts.deferred, 1, 'requirements: deferred count');
|
|
295
|
+
assertEq(counts.outOfScope, 1, 'requirements: outOfScope count');
|
|
296
|
+
assertEq(counts.total, 5, 'requirements: total count');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
300
|
+
// Scenario F: Edge cases
|
|
301
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
302
|
+
|
|
303
|
+
// F1: Empty vision → fallback text
|
|
304
|
+
{
|
|
305
|
+
const milestone = makeMilestone({ vision: '' });
|
|
306
|
+
const output = formatRoadmap(milestone);
|
|
307
|
+
const parsed = parseRoadmap(output);
|
|
308
|
+
assertEq(parsed.vision, '(migrated project)', 'edge: empty vision fallback');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// F2: Empty successCriteria → empty array
|
|
312
|
+
{
|
|
313
|
+
const milestone = makeMilestone({ successCriteria: [] });
|
|
314
|
+
const output = formatRoadmap(milestone);
|
|
315
|
+
const parsed = parseRoadmap(output);
|
|
316
|
+
assertEq(parsed.successCriteria.length, 0, 'edge: empty successCriteria');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// F3: Empty tasks → empty array in parsed plan
|
|
320
|
+
{
|
|
321
|
+
const slice = makeSlice({ tasks: [] });
|
|
322
|
+
const output = formatPlan(slice);
|
|
323
|
+
const parsed = parsePlan(output);
|
|
324
|
+
assertEq(parsed.tasks.length, 0, 'edge: empty tasks');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// F4: Null summary → empty string from formatSliceSummary
|
|
328
|
+
{
|
|
329
|
+
const slice = makeSlice({ summary: null });
|
|
330
|
+
const output = formatSliceSummary(slice, 'M001');
|
|
331
|
+
assertEq(output, '', 'edge: null summary returns empty string');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// F5: Done=true checkbox in roadmap
|
|
335
|
+
{
|
|
336
|
+
const milestone = makeMilestone({
|
|
337
|
+
slices: [makeSlice({ id: 'S01', done: true })],
|
|
338
|
+
});
|
|
339
|
+
const output = formatRoadmap(milestone);
|
|
340
|
+
const parsed = parseRoadmap(output);
|
|
341
|
+
assertEq(parsed.slices[0].done, true, 'edge: done checkbox true');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// F6: Done=false checkbox in roadmap
|
|
345
|
+
{
|
|
346
|
+
const milestone = makeMilestone({
|
|
347
|
+
slices: [makeSlice({ id: 'S01', done: false })],
|
|
348
|
+
});
|
|
349
|
+
const output = formatRoadmap(milestone);
|
|
350
|
+
const parsed = parseRoadmap(output);
|
|
351
|
+
assertEq(parsed.slices[0].done, false, 'edge: done checkbox false');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// F7: Null task summary → empty string from formatTaskSummary
|
|
355
|
+
{
|
|
356
|
+
const task = makeTask({ summary: null });
|
|
357
|
+
const output = formatTaskSummary(task, 'S01', 'M001');
|
|
358
|
+
assertEq(output, '', 'edge: null task summary returns empty string');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// F8: Empty requirements → all zeros
|
|
362
|
+
{
|
|
363
|
+
const output = formatRequirements([]);
|
|
364
|
+
const counts = parseRequirementCounts(output);
|
|
365
|
+
assertEq(counts.total, 0, 'edge: empty requirements total 0');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// F9: formatProject with empty content → produces valid stub
|
|
369
|
+
{
|
|
370
|
+
const output = formatProject('');
|
|
371
|
+
assert(output.includes('# Project'), 'edge: empty project has heading');
|
|
372
|
+
assert(output.length > 10, 'edge: empty project not blank');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// F10: formatProject with existing content → passes through
|
|
376
|
+
{
|
|
377
|
+
const content = '# My Project\n\nDescription here.\n';
|
|
378
|
+
const output = formatProject(content);
|
|
379
|
+
assertEq(output, content, 'edge: project passthrough');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// F11: formatDecisions with empty content → produces valid stub
|
|
383
|
+
{
|
|
384
|
+
const output = formatDecisions('');
|
|
385
|
+
assert(output.includes('# Decisions'), 'edge: empty decisions has heading');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// F12: formatContext produces valid content
|
|
389
|
+
{
|
|
390
|
+
const output = formatContext('M001');
|
|
391
|
+
assert(output.includes('M001'), 'edge: context mentions milestone');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// F13: formatState produces valid content
|
|
395
|
+
{
|
|
396
|
+
const milestones = [makeMilestone({
|
|
397
|
+
slices: [
|
|
398
|
+
makeSlice({ done: true }),
|
|
399
|
+
makeSlice({ id: 'S02', done: false }),
|
|
400
|
+
],
|
|
401
|
+
})];
|
|
402
|
+
const output = formatState(milestones);
|
|
403
|
+
assert(output.includes('1/2'), 'edge: state shows slice progress');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// F14: Task with no estimate → no est backtick in plan
|
|
407
|
+
{
|
|
408
|
+
const slice = makeSlice({
|
|
409
|
+
tasks: [makeTask({ id: 'T01', title: 'Quick Fix', estimate: '' })],
|
|
410
|
+
});
|
|
411
|
+
const output = formatPlan(slice);
|
|
412
|
+
const parsed = parsePlan(output);
|
|
413
|
+
assertEq(parsed.tasks[0].id, 'T01', 'edge: task no estimate id');
|
|
414
|
+
assertEq(parsed.tasks[0].estimate, '', 'edge: task no estimate empty');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
418
|
+
|
|
419
|
+
console.log(`\nResults: ${passed} passed, ${failed} failed`);
|
|
420
|
+
if (failed > 0) process.exit(1);
|