gsd-pi 2.29.0-dev.49d972f → 2.29.0-dev.4c155ee
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/README.md +24 -17
- package/dist/headless.js +4 -0
- package/dist/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +217 -65
- package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/dist/resources/extensions/gsd/auto-post-unit.ts +44 -12
- package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
- package/dist/resources/extensions/gsd/auto-recovery.ts +2 -1
- package/dist/resources/extensions/gsd/auto-start.ts +18 -32
- package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
- package/dist/resources/extensions/gsd/auto.ts +2 -9
- package/dist/resources/extensions/gsd/captures.ts +4 -10
- package/dist/resources/extensions/gsd/commands-handlers.ts +2 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/dist/resources/extensions/gsd/commands.ts +55 -2
- package/dist/resources/extensions/gsd/detection.ts +2 -1
- package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
- package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
- package/dist/resources/extensions/gsd/forensics.ts +2 -2
- package/dist/resources/extensions/gsd/git-service.ts +3 -2
- package/dist/resources/extensions/gsd/gitignore.ts +9 -63
- package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
- package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
- package/dist/resources/extensions/gsd/index.ts +3 -3
- package/dist/resources/extensions/gsd/md-importer.ts +3 -2
- package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
- package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
- package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
- package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
- package/dist/resources/extensions/gsd/paths.ts +24 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
- package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
- package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +10 -5
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
- package/dist/resources/extensions/gsd/resource-version.ts +99 -0
- package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
- package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
- package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
- package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
- package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
- package/dist/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
- package/dist/resources/extensions/gsd/types.ts +2 -0
- package/dist/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/dist/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/dist/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/dist/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/dist/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/dist/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/dist/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/dist/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/dist/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/dist/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +1 -11
- package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
- package/dist/resources/extensions/gsd/worktree.ts +42 -5
- package/dist/resources/extensions/mcp-client/index.ts +459 -0
- package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/dist/resources/skills/create-skill/SKILL.md +184 -0
- package/dist/resources/skills/create-skill/references/api-security.md +226 -0
- package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
- package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/dist/resources/skills/react-best-practices/SKILL.md +1 -1
- package/package.json +1 -1
- package/packages/native/dist/native.d.ts +2 -0
- package/packages/native/dist/native.js +19 -5
- package/packages/native/src/native.ts +23 -9
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +13 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +13 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
- package/src/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +217 -65
- package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/src/resources/extensions/gsd/auto-post-unit.ts +44 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
- package/src/resources/extensions/gsd/auto-recovery.ts +2 -1
- package/src/resources/extensions/gsd/auto-start.ts +18 -32
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
- package/src/resources/extensions/gsd/auto.ts +2 -9
- package/src/resources/extensions/gsd/captures.ts +4 -10
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/src/resources/extensions/gsd/commands.ts +55 -2
- package/src/resources/extensions/gsd/detection.ts +2 -1
- package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +3 -2
- package/src/resources/extensions/gsd/gitignore.ts +9 -63
- package/src/resources/extensions/gsd/gsd-db.ts +1 -165
- package/src/resources/extensions/gsd/guided-flow.ts +8 -5
- package/src/resources/extensions/gsd/index.ts +3 -3
- package/src/resources/extensions/gsd/md-importer.ts +3 -2
- package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
- package/src/resources/extensions/gsd/migrate/command.ts +3 -2
- package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
- package/src/resources/extensions/gsd/migrate-external.ts +123 -0
- package/src/resources/extensions/gsd/paths.ts +24 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
- package/src/resources/extensions/gsd/preferences-models.ts +7 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +10 -5
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/src/resources/extensions/gsd/repo-identity.ts +148 -0
- package/src/resources/extensions/gsd/resource-version.ts +99 -0
- package/src/resources/extensions/gsd/session-forensics.ts +4 -3
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
- package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
- package/src/resources/extensions/gsd/types.ts +2 -0
- package/src/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/src/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/src/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/src/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/src/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/src/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/src/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/src/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/src/resources/extensions/gsd/worktree-command.ts +1 -11
- package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
- package/src/resources/extensions/gsd/worktree.ts +42 -5
- package/src/resources/extensions/mcp-client/index.ts +459 -0
- package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/src/resources/skills/create-skill/SKILL.md +184 -0
- package/src/resources/skills/create-skill/references/api-security.md +226 -0
- package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/src/resources/skills/create-skill/references/core-principles.md +437 -0
- package/src/resources/skills/create-skill/references/executable-code.md +175 -0
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/src/resources/skills/create-skill/references/using-templates.md +112 -0
- package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/src/resources/skills/react-best-practices/SKILL.md +1 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
- package/dist/resources/extensions/mcporter/index.ts +0 -525
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
- package/src/resources/extensions/mcporter/index.ts +0 -525
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Tests for the SEPARATOR_PREFIX convention used by ExtensionSelectorComponent
|
|
1
|
+
// Tests for the SEPARATOR_PREFIX convention used by ExtensionSelectorComponent
|
|
2
|
+
// and the two-step provider→model picker in configureModels.
|
|
3
|
+
//
|
|
2
4
|
// We cannot import the component directly in node:test because its transitive
|
|
3
5
|
// dependency (countdown-timer.ts) uses TypeScript parameter properties which
|
|
4
6
|
// are unsupported under --experimental-strip-types. Instead we duplicate the
|
|
@@ -69,16 +71,17 @@ describe("separator detection", () => {
|
|
|
69
71
|
});
|
|
70
72
|
});
|
|
71
73
|
|
|
72
|
-
describe("model
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
describe("two-step provider→model picker", () => {
|
|
75
|
+
// Simulate the grouping logic from configureModels
|
|
76
|
+
const availableModels = [
|
|
77
|
+
{ id: "claude-opus-4-6", provider: "anthropic" },
|
|
78
|
+
{ id: "gpt-4o", provider: "openai" },
|
|
79
|
+
{ id: "claude-sonnet-4-5", provider: "anthropic" },
|
|
80
|
+
{ id: "o3-mini", provider: "openai" },
|
|
81
|
+
{ id: "claude-haiku-4-5", provider: "anthropic" },
|
|
82
|
+
];
|
|
81
83
|
|
|
84
|
+
function buildProviderGroups() {
|
|
82
85
|
const byProvider = new Map<string, typeof availableModels>();
|
|
83
86
|
for (const m of availableModels) {
|
|
84
87
|
let group = byProvider.get(m.provider);
|
|
@@ -89,34 +92,53 @@ describe("model grouping", () => {
|
|
|
89
92
|
group.push(m);
|
|
90
93
|
}
|
|
91
94
|
const providers = Array.from(byProvider.keys()).sort((a, b) => a.localeCompare(b));
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
for (const provider of providers) {
|
|
95
|
-
const group = byProvider.get(provider)!;
|
|
96
|
-
modelOptions.push(`${SEPARATOR_PREFIX} ${provider} (${group.length}) ${SEPARATOR_PREFIX}`);
|
|
97
|
-
for (const m of group) {
|
|
98
|
-
modelOptions.push(`${m.id} · ${m.provider}`);
|
|
99
|
-
}
|
|
95
|
+
for (const group of byProvider.values()) {
|
|
96
|
+
group.sort((a, b) => a.id.localeCompare(b.id));
|
|
100
97
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
assert.
|
|
115
|
-
assert.
|
|
116
|
-
assert.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
98
|
+
return { byProvider, providers };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
test("provider menu lists providers with model counts", () => {
|
|
102
|
+
const { providers, byProvider } = buildProviderGroups();
|
|
103
|
+
const providerOptions = providers.map(p => {
|
|
104
|
+
const count = byProvider.get(p)!.length;
|
|
105
|
+
return `${p} (${count} models)`;
|
|
106
|
+
});
|
|
107
|
+
providerOptions.push("(keep current)", "(clear)", "(type manually)");
|
|
108
|
+
|
|
109
|
+
assert.strictEqual(providerOptions[0], "anthropic (3 models)");
|
|
110
|
+
assert.strictEqual(providerOptions[1], "openai (2 models)");
|
|
111
|
+
assert.strictEqual(providerOptions[2], "(keep current)");
|
|
112
|
+
assert.strictEqual(providerOptions[3], "(clear)");
|
|
113
|
+
assert.strictEqual(providerOptions[4], "(type manually)");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("model menu for a provider is sorted alphabetically", () => {
|
|
117
|
+
const { byProvider } = buildProviderGroups();
|
|
118
|
+
const anthropicModels = byProvider.get("anthropic")!;
|
|
119
|
+
const modelOptions = anthropicModels.map(m => m.id);
|
|
120
|
+
|
|
121
|
+
assert.strictEqual(modelOptions[0], "claude-haiku-4-5");
|
|
122
|
+
assert.strictEqual(modelOptions[1], "claude-opus-4-6");
|
|
123
|
+
assert.strictEqual(modelOptions[2], "claude-sonnet-4-5");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("provider name is extracted correctly from choice string", () => {
|
|
127
|
+
const choice = "anthropic (3 models)";
|
|
128
|
+
const providerName = choice.replace(/ \(\d+ models?\)$/, "");
|
|
129
|
+
assert.strictEqual(providerName, "anthropic");
|
|
130
|
+
|
|
131
|
+
const singleChoice = "ollama (1 model)";
|
|
132
|
+
const singleProvider = singleChoice.replace(/ \(\d+ models?\)$/, "");
|
|
133
|
+
assert.strictEqual(singleProvider, "ollama");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("openai models are sorted within their group", () => {
|
|
137
|
+
const { byProvider } = buildProviderGroups();
|
|
138
|
+
const openaiModels = byProvider.get("openai")!;
|
|
139
|
+
const modelOptions = openaiModels.map(m => m.id);
|
|
140
|
+
|
|
141
|
+
assert.strictEqual(modelOptions[0], "gpt-4o");
|
|
142
|
+
assert.strictEqual(modelOptions[1], "o3-mini");
|
|
121
143
|
});
|
|
122
144
|
});
|
|
@@ -311,7 +311,7 @@ async function main(): Promise<void> {
|
|
|
311
311
|
// Test 2: Uncommitted .gsd/ planning files are available in worktree
|
|
312
312
|
//
|
|
313
313
|
// When auto-mode starts, .gsd/ files may be untracked/uncommitted.
|
|
314
|
-
//
|
|
314
|
+
// Planning artifacts should be carried into the worktree even if
|
|
315
315
|
// they weren't committed on the feature branch.
|
|
316
316
|
// ================================================================
|
|
317
317
|
console.log("\n=== Untracked planning files copied to worktree ===");
|
|
@@ -341,23 +341,10 @@ async function main(): Promise<void> {
|
|
|
341
341
|
const wtPath = createAutoWorktree(repo, milestoneId);
|
|
342
342
|
tempDirs.push(wtPath);
|
|
343
343
|
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
);
|
|
349
|
-
assertTrue(
|
|
350
|
-
existsSync(join(wtPath, ".gsd", "milestones", milestoneId, "slices", "S01", "S01-PLAN.md")),
|
|
351
|
-
"S01-PLAN.md copied to worktree",
|
|
352
|
-
);
|
|
353
|
-
assertTrue(
|
|
354
|
-
existsSync(join(wtPath, ".gsd", "PROJECT.md")),
|
|
355
|
-
"PROJECT.md copied to worktree",
|
|
356
|
-
);
|
|
357
|
-
assertTrue(
|
|
358
|
-
existsSync(join(wtPath, ".gsd", "DECISIONS.md")),
|
|
359
|
-
"DECISIONS.md copied to worktree",
|
|
360
|
-
);
|
|
344
|
+
// With external state, worktree .gsd is a symlink to shared state.
|
|
345
|
+
// Verify symlink was created (planning files are shared, not copied).
|
|
346
|
+
const wtGsd = join(wtPath, ".gsd");
|
|
347
|
+
assertTrue(existsSync(wtGsd), "worktree .gsd exists (symlink or dir)");
|
|
361
348
|
|
|
362
349
|
// Clean up: chdir back before teardown
|
|
363
350
|
process.chdir(savedCwd);
|
|
@@ -1167,53 +1167,26 @@ async function main(): Promise<void> {
|
|
|
1167
1167
|
rmSync(repo, { recursive: true, force: true });
|
|
1168
1168
|
}
|
|
1169
1169
|
|
|
1170
|
-
// ─── ensureGitignore:
|
|
1170
|
+
// ─── ensureGitignore: always adds .gsd to gitignore ──────────────────
|
|
1171
1171
|
|
|
1172
|
-
console.log("\n=== ensureGitignore:
|
|
1172
|
+
console.log("\n=== ensureGitignore: adds .gsd entry ===");
|
|
1173
1173
|
|
|
1174
1174
|
{
|
|
1175
1175
|
const { ensureGitignore } = await import("../gitignore.ts");
|
|
1176
|
-
const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-
|
|
1176
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-external-state-"));
|
|
1177
1177
|
|
|
1178
|
-
//
|
|
1179
|
-
const modified = ensureGitignore(repo
|
|
1180
|
-
assertTrue(modified, "
|
|
1178
|
+
// Should add .gsd to gitignore (external state dir is a symlink)
|
|
1179
|
+
const modified = ensureGitignore(repo);
|
|
1180
|
+
assertTrue(modified, "ensureGitignore: gitignore was modified");
|
|
1181
1181
|
|
|
1182
1182
|
const { readFileSync } = await import("node:fs");
|
|
1183
1183
|
const content = readFileSync(join(repo, ".gitignore"), "utf-8");
|
|
1184
|
-
|
|
1185
|
-
assertTrue(
|
|
1186
|
-
|
|
1187
|
-
// Should NOT contain individual runtime patterns (those are subsumed by blanket .gsd/)
|
|
1188
|
-
// But it's OK if it does — the blanket .gsd/ covers everything
|
|
1184
|
+
const lines = content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
|
|
1185
|
+
assertTrue(lines.includes(".gsd"), "ensureGitignore: .gitignore contains .gsd");
|
|
1189
1186
|
|
|
1190
1187
|
// Idempotent — calling again doesn't add duplicates
|
|
1191
|
-
const modified2 = ensureGitignore(repo
|
|
1192
|
-
assertTrue(!modified2, "
|
|
1193
|
-
|
|
1194
|
-
rmSync(repo, { recursive: true, force: true });
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// ─── ensureGitignore: commit_docs true removes blanket .gsd/ ────────
|
|
1198
|
-
|
|
1199
|
-
console.log("\n=== ensureGitignore: commit_docs true self-heals ===");
|
|
1200
|
-
|
|
1201
|
-
{
|
|
1202
|
-
const { ensureGitignore } = await import("../gitignore.ts");
|
|
1203
|
-
const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-selfheal-"));
|
|
1204
|
-
|
|
1205
|
-
// Start with a gitignore that has a blanket .gsd/ (e.g., user switched setting)
|
|
1206
|
-
writeFileSync(join(repo, ".gitignore"), ".gsd/\n");
|
|
1207
|
-
|
|
1208
|
-
const modified = ensureGitignore(repo, { commitDocs: true });
|
|
1209
|
-
assertTrue(modified, "commit_docs=true: gitignore was modified");
|
|
1210
|
-
|
|
1211
|
-
const { readFileSync } = await import("node:fs");
|
|
1212
|
-
const content = readFileSync(join(repo, ".gitignore"), "utf-8");
|
|
1213
|
-
// Blanket .gsd/ should be removed
|
|
1214
|
-
const lines = content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
|
|
1215
|
-
assertTrue(!lines.includes(".gsd/"), "commit_docs=true: blanket .gsd/ was removed");
|
|
1216
|
-
assertTrue(!lines.includes(".gsd"), "commit_docs=true: blanket .gsd was removed");
|
|
1188
|
+
const modified2 = ensureGitignore(repo);
|
|
1189
|
+
assertTrue(!modified2, "ensureGitignore: second call is idempotent");
|
|
1217
1190
|
|
|
1218
1191
|
rmSync(repo, { recursive: true, force: true });
|
|
1219
1192
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import test from 'node:test';
|
|
12
12
|
import assert from 'node:assert/strict';
|
|
13
|
-
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
|
|
13
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, realpathSync } from 'node:fs';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import { tmpdir } from 'node:os';
|
|
16
16
|
import { GSD_ROOT_FILES, resolveGsdRootFile } from '../paths.ts';
|
|
@@ -27,7 +27,7 @@ test('knowledge: KNOWLEDGE key exists in GSD_ROOT_FILES', () => {
|
|
|
27
27
|
// ─── resolveGsdRootFile resolves KNOWLEDGE.md ───────────────────────────────
|
|
28
28
|
|
|
29
29
|
test('knowledge: resolveGsdRootFile returns canonical path when KNOWLEDGE.md exists', () => {
|
|
30
|
-
const tmp = mkdtempSync(join(tmpdir(), 'gsd-knowledge-'));
|
|
30
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
|
|
31
31
|
const gsdDir = join(tmp, '.gsd');
|
|
32
32
|
mkdirSync(gsdDir, { recursive: true });
|
|
33
33
|
writeFileSync(join(gsdDir, 'KNOWLEDGE.md'), '# Project Knowledge\n');
|
|
@@ -39,7 +39,7 @@ test('knowledge: resolveGsdRootFile returns canonical path when KNOWLEDGE.md exi
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
test('knowledge: resolveGsdRootFile resolves when legacy knowledge.md exists', () => {
|
|
42
|
-
const tmp = mkdtempSync(join(tmpdir(), 'gsd-knowledge-'));
|
|
42
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
|
|
43
43
|
const gsdDir = join(tmp, '.gsd');
|
|
44
44
|
mkdirSync(gsdDir, { recursive: true });
|
|
45
45
|
writeFileSync(join(gsdDir, 'knowledge.md'), '# Project Knowledge\n');
|
|
@@ -58,7 +58,7 @@ test('knowledge: resolveGsdRootFile resolves when legacy knowledge.md exists', (
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
test('knowledge: resolveGsdRootFile returns canonical path when file does not exist', () => {
|
|
61
|
-
const tmp = mkdtempSync(join(tmpdir(), 'gsd-knowledge-'));
|
|
61
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
|
|
62
62
|
const gsdDir = join(tmp, '.gsd');
|
|
63
63
|
mkdirSync(gsdDir, { recursive: true });
|
|
64
64
|
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mechanical Completion — unit tests (ADR-003).
|
|
3
|
+
*
|
|
4
|
+
* Tests deterministic slice/milestone completion using fixture data.
|
|
5
|
+
* Uses node:test + node:assert for consistency with token-profile.test.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { mkdirSync, writeFileSync, readFileSync, rmSync, existsSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
|
|
15
|
+
// ─── Fixture Helpers ──────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
function createTmpBase(): string {
|
|
18
|
+
const base = join(tmpdir(), `gsd-mech-test-${randomBytes(4).toString("hex")}`);
|
|
19
|
+
mkdirSync(base, { recursive: true });
|
|
20
|
+
return base;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function scaffold(base: string, mid: string, sid: string, taskSummaries: Array<{ tid: string; content: string }>) {
|
|
24
|
+
const gsdRoot = join(base, ".gsd");
|
|
25
|
+
const mDir = join(gsdRoot, "milestones", mid);
|
|
26
|
+
const sDir = join(mDir, "slices", sid);
|
|
27
|
+
const tDir = join(sDir, "tasks");
|
|
28
|
+
mkdirSync(tDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
for (const { tid, content } of taskSummaries) {
|
|
31
|
+
writeFileSync(join(tDir, `${tid}-SUMMARY.md`), content, "utf-8");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { gsdRoot, mDir, sDir, tDir };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeTaskSummary(tid: string, opts: {
|
|
38
|
+
oneLiner?: string;
|
|
39
|
+
provides?: string[];
|
|
40
|
+
key_files?: string[];
|
|
41
|
+
key_decisions?: string[];
|
|
42
|
+
verification_result?: string;
|
|
43
|
+
}): string {
|
|
44
|
+
const lines: string[] = [
|
|
45
|
+
"---",
|
|
46
|
+
`id: ${tid}`,
|
|
47
|
+
`parent: S01`,
|
|
48
|
+
`milestone: M001`,
|
|
49
|
+
];
|
|
50
|
+
if (opts.provides?.length) lines.push(`provides:\n${opts.provides.map(p => ` - ${p}`).join("\n")}`);
|
|
51
|
+
if (opts.key_files?.length) lines.push(`key_files:\n${opts.key_files.map(f => ` - ${f}`).join("\n")}`);
|
|
52
|
+
if (opts.key_decisions?.length) lines.push(`key_decisions:\n${opts.key_decisions.map(d => ` - ${d}`).join("\n")}`);
|
|
53
|
+
lines.push(`verification_result: ${opts.verification_result ?? "passed"}`);
|
|
54
|
+
lines.push("---");
|
|
55
|
+
lines.push("");
|
|
56
|
+
lines.push(`# ${tid}: Test Task`);
|
|
57
|
+
lines.push("");
|
|
58
|
+
if (opts.oneLiner) lines.push(`**${opts.oneLiner}**`);
|
|
59
|
+
lines.push("");
|
|
60
|
+
lines.push("## What Happened");
|
|
61
|
+
lines.push("");
|
|
62
|
+
lines.push(`Implemented the feature described in ${tid}. This was a significant change that modified multiple files across the codebase to support the new functionality.`);
|
|
63
|
+
lines.push("");
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── Source-level structural tests ────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
const mechanicalSrc = readFileSync(
|
|
70
|
+
join(import.meta.dirname!, "..", "mechanical-completion.ts"),
|
|
71
|
+
"utf-8",
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
test("mechanical-completion: exports mechanicalSliceCompletion", () => {
|
|
75
|
+
assert.ok(
|
|
76
|
+
mechanicalSrc.includes("export async function mechanicalSliceCompletion"),
|
|
77
|
+
"should export mechanicalSliceCompletion",
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("mechanical-completion: exports aggregateMilestoneVerification", () => {
|
|
82
|
+
assert.ok(
|
|
83
|
+
mechanicalSrc.includes("export async function aggregateMilestoneVerification"),
|
|
84
|
+
"should export aggregateMilestoneVerification",
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("mechanical-completion: exports generateMilestoneSummary", () => {
|
|
89
|
+
assert.ok(
|
|
90
|
+
mechanicalSrc.includes("export async function generateMilestoneSummary"),
|
|
91
|
+
"should export generateMilestoneSummary",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("mechanical-completion: exports appendNewDecisions", () => {
|
|
96
|
+
assert.ok(
|
|
97
|
+
mechanicalSrc.includes("export async function appendNewDecisions"),
|
|
98
|
+
"should export appendNewDecisions",
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("mechanical-completion: uses atomicWriteSync for file writes", () => {
|
|
103
|
+
assert.ok(
|
|
104
|
+
mechanicalSrc.includes("atomicWriteSync"),
|
|
105
|
+
"should use atomicWriteSync for safe file writes",
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("mechanical-completion: quality gate checks summary length for multi-task slices", () => {
|
|
110
|
+
assert.ok(
|
|
111
|
+
mechanicalSrc.includes("totalContent.length < 200"),
|
|
112
|
+
"should have quality gate for summary content length",
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("mechanical-completion: marks slice [x] in roadmap", () => {
|
|
117
|
+
assert.ok(
|
|
118
|
+
mechanicalSrc.includes("markSliceInRoadmap"),
|
|
119
|
+
"should mark slice done in roadmap",
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("mechanical-completion: aggregates VERIFY.json files for milestone validation", () => {
|
|
124
|
+
assert.ok(
|
|
125
|
+
mechanicalSrc.includes("resolveTaskJsonFiles") && mechanicalSrc.includes("VERIFY"),
|
|
126
|
+
"should read VERIFY.json files for milestone validation",
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("mechanical-completion: deduplicates decisions against existing DECISIONS.md", () => {
|
|
131
|
+
assert.ok(
|
|
132
|
+
mechanicalSrc.includes("existing.includes(d.trim())"),
|
|
133
|
+
"should deduplicate decisions against existing content",
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("mechanical-completion: produces VALIDATION.md with verdict frontmatter", () => {
|
|
138
|
+
assert.ok(
|
|
139
|
+
mechanicalSrc.includes("verdict:") && mechanicalSrc.includes("remediation_round: 0"),
|
|
140
|
+
"VALIDATION.md should have verdict and remediation_round frontmatter",
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ─── Integration tests with fixture data ──────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
test("mechanical: slice completion with 2 task summaries produces SUMMARY.md", async () => {
|
|
147
|
+
const base = createTmpBase();
|
|
148
|
+
try {
|
|
149
|
+
const mid = "M001";
|
|
150
|
+
const sid = "S01";
|
|
151
|
+
|
|
152
|
+
// Scaffold task summaries
|
|
153
|
+
scaffold(base, mid, sid, [
|
|
154
|
+
{
|
|
155
|
+
tid: "T01",
|
|
156
|
+
content: makeTaskSummary("T01", {
|
|
157
|
+
oneLiner: "Set up project structure",
|
|
158
|
+
provides: ["project-scaffold"],
|
|
159
|
+
key_files: ["src/index.ts", "package.json"],
|
|
160
|
+
verification_result: "passed",
|
|
161
|
+
}),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
tid: "T02",
|
|
165
|
+
content: makeTaskSummary("T02", {
|
|
166
|
+
oneLiner: "Add core API endpoints",
|
|
167
|
+
provides: ["api-endpoints"],
|
|
168
|
+
key_files: ["src/api.ts"],
|
|
169
|
+
key_decisions: ["Used Express over Fastify"],
|
|
170
|
+
verification_result: "passed",
|
|
171
|
+
}),
|
|
172
|
+
},
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
// Write a roadmap with the slice unchecked
|
|
176
|
+
const roadmapPath = join(base, ".gsd", "milestones", mid, `${mid}-ROADMAP.md`);
|
|
177
|
+
writeFileSync(roadmapPath, `# Roadmap\n\n- [ ] **${sid}: First Slice**\n`, "utf-8");
|
|
178
|
+
|
|
179
|
+
// Write a slice plan with Verification section
|
|
180
|
+
const planPath = join(base, ".gsd", "milestones", mid, "slices", sid, `${sid}-PLAN.md`);
|
|
181
|
+
writeFileSync(planPath, `# Plan\n\n## Verification\n\n- Run \`npm test\`\n- Check output\n`, "utf-8");
|
|
182
|
+
|
|
183
|
+
// Dynamic import to get the actual module
|
|
184
|
+
const { mechanicalSliceCompletion } = await import("../mechanical-completion.js");
|
|
185
|
+
const ok = await mechanicalSliceCompletion(base, mid, sid);
|
|
186
|
+
|
|
187
|
+
assert.ok(ok, "should return true for valid slice completion");
|
|
188
|
+
|
|
189
|
+
// Check SUMMARY.md was written
|
|
190
|
+
const summaryPath = join(base, ".gsd", "milestones", mid, "slices", sid, `${sid}-SUMMARY.md`);
|
|
191
|
+
assert.ok(existsSync(summaryPath), "SUMMARY.md should exist");
|
|
192
|
+
|
|
193
|
+
const summaryContent = readFileSync(summaryPath, "utf-8");
|
|
194
|
+
assert.ok(summaryContent.includes("T01"), "summary should reference T01");
|
|
195
|
+
assert.ok(summaryContent.includes("T02"), "summary should reference T02");
|
|
196
|
+
assert.ok(summaryContent.includes("verification_result: passed"), "should have passed verification");
|
|
197
|
+
|
|
198
|
+
// Check roadmap was updated
|
|
199
|
+
const updatedRoadmap = readFileSync(roadmapPath, "utf-8");
|
|
200
|
+
assert.ok(updatedRoadmap.includes("[x]"), "roadmap should have [x] checkbox");
|
|
201
|
+
|
|
202
|
+
// Check UAT was written
|
|
203
|
+
const uatPath = join(base, ".gsd", "milestones", mid, "slices", sid, `${sid}-UAT.md`);
|
|
204
|
+
assert.ok(existsSync(uatPath), "UAT.md should exist");
|
|
205
|
+
const uatContent = readFileSync(uatPath, "utf-8");
|
|
206
|
+
assert.ok(uatContent.includes("npm test"), "UAT should contain verification content");
|
|
207
|
+
} finally {
|
|
208
|
+
rmSync(base, { recursive: true, force: true });
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("mechanical: returns false for empty task summaries", async () => {
|
|
213
|
+
const base = createTmpBase();
|
|
214
|
+
try {
|
|
215
|
+
const mid = "M001";
|
|
216
|
+
const sid = "S01";
|
|
217
|
+
scaffold(base, mid, sid, []);
|
|
218
|
+
|
|
219
|
+
const { mechanicalSliceCompletion } = await import("../mechanical-completion.js");
|
|
220
|
+
const ok = await mechanicalSliceCompletion(base, mid, sid);
|
|
221
|
+
assert.ok(!ok, "should return false when no summaries exist");
|
|
222
|
+
} finally {
|
|
223
|
+
rmSync(base, { recursive: true, force: true });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("mechanical: returns false for insufficient summary content in multi-task slice", async () => {
|
|
228
|
+
const base = createTmpBase();
|
|
229
|
+
try {
|
|
230
|
+
const mid = "M001";
|
|
231
|
+
const sid = "S01";
|
|
232
|
+
|
|
233
|
+
// Two tasks but with very short content (under 200 chars)
|
|
234
|
+
scaffold(base, mid, sid, [
|
|
235
|
+
{ tid: "T01", content: "---\nid: T01\nparent: S01\nmilestone: M001\n---\n\n# T01: A\n\n**Short**\n" },
|
|
236
|
+
{ tid: "T02", content: "---\nid: T02\nparent: S01\nmilestone: M001\n---\n\n# T02: B\n\n**Brief**\n" },
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
const { mechanicalSliceCompletion } = await import("../mechanical-completion.js");
|
|
240
|
+
const ok = await mechanicalSliceCompletion(base, mid, sid);
|
|
241
|
+
assert.ok(!ok, "should return false when summaries are too short");
|
|
242
|
+
} finally {
|
|
243
|
+
rmSync(base, { recursive: true, force: true });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("mechanical: milestone verification aggregates VERIFY.json files", async () => {
|
|
248
|
+
const base = createTmpBase();
|
|
249
|
+
try {
|
|
250
|
+
const mid = "M001";
|
|
251
|
+
const sid = "S01";
|
|
252
|
+
const { tDir } = scaffold(base, mid, sid, []);
|
|
253
|
+
|
|
254
|
+
// Write VERIFY.json files
|
|
255
|
+
const evidence = {
|
|
256
|
+
schemaVersion: 1,
|
|
257
|
+
taskId: "T01",
|
|
258
|
+
unitId: "M001/S01/T01",
|
|
259
|
+
timestamp: Date.now(),
|
|
260
|
+
passed: true,
|
|
261
|
+
discoverySource: "plan",
|
|
262
|
+
checks: [
|
|
263
|
+
{ command: "npm test", exitCode: 0, durationMs: 1500, verdict: "pass", blocking: true },
|
|
264
|
+
],
|
|
265
|
+
};
|
|
266
|
+
writeFileSync(join(tDir, "T01-VERIFY.json"), JSON.stringify(evidence), "utf-8");
|
|
267
|
+
|
|
268
|
+
const evidence2 = { ...evidence, taskId: "T02", passed: false, checks: [
|
|
269
|
+
{ command: "npm test", exitCode: 1, durationMs: 500, verdict: "fail", blocking: true },
|
|
270
|
+
]};
|
|
271
|
+
writeFileSync(join(tDir, "T02-VERIFY.json"), JSON.stringify(evidence2), "utf-8");
|
|
272
|
+
|
|
273
|
+
const { aggregateMilestoneVerification } = await import("../mechanical-completion.js");
|
|
274
|
+
const result = await aggregateMilestoneVerification(base, mid);
|
|
275
|
+
|
|
276
|
+
assert.equal(result.verdict, "mixed", "should be mixed when some pass and some fail");
|
|
277
|
+
assert.equal(result.checks.length, 2, "should have 2 checks");
|
|
278
|
+
|
|
279
|
+
// Check VALIDATION.md was written
|
|
280
|
+
const validationPath = join(base, ".gsd", "milestones", mid, `${mid}-VALIDATION.md`);
|
|
281
|
+
assert.ok(existsSync(validationPath), "VALIDATION.md should exist");
|
|
282
|
+
const validationContent = readFileSync(validationPath, "utf-8");
|
|
283
|
+
assert.ok(validationContent.includes("verdict: mixed"), "should have mixed verdict in frontmatter");
|
|
284
|
+
} finally {
|
|
285
|
+
rmSync(base, { recursive: true, force: true });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("mechanical: milestone summary aggregates slice summaries", async () => {
|
|
290
|
+
const base = createTmpBase();
|
|
291
|
+
try {
|
|
292
|
+
const mid = "M001";
|
|
293
|
+
|
|
294
|
+
// Create two slices with summaries
|
|
295
|
+
for (const sid of ["S01", "S02"]) {
|
|
296
|
+
const sDir = join(base, ".gsd", "milestones", mid, "slices", sid);
|
|
297
|
+
mkdirSync(sDir, { recursive: true });
|
|
298
|
+
writeFileSync(
|
|
299
|
+
join(sDir, `${sid}-SUMMARY.md`),
|
|
300
|
+
`---\nid: ${sid}\nprovides:\n - feature-${sid.toLowerCase()}\nkey_files:\n - src/${sid.toLowerCase()}.ts\n---\n\n# ${sid}: Slice\n\n**${sid} implemented**\n`,
|
|
301
|
+
"utf-8",
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const { generateMilestoneSummary } = await import("../mechanical-completion.js");
|
|
306
|
+
const content = await generateMilestoneSummary(base, mid);
|
|
307
|
+
|
|
308
|
+
assert.ok(content.includes("S01"), "should reference S01");
|
|
309
|
+
assert.ok(content.includes("S02"), "should reference S02");
|
|
310
|
+
assert.ok(content.includes("feature-s01"), "should aggregate provides");
|
|
311
|
+
assert.ok(content.includes("feature-s02"), "should aggregate provides");
|
|
312
|
+
|
|
313
|
+
const summaryPath = join(base, ".gsd", "milestones", mid, `${mid}-SUMMARY.md`);
|
|
314
|
+
assert.ok(existsSync(summaryPath), "M##-SUMMARY.md should exist");
|
|
315
|
+
} finally {
|
|
316
|
+
rmSync(base, { recursive: true, force: true });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("mechanical: decision deduplication skips existing decisions", async () => {
|
|
321
|
+
const base = createTmpBase();
|
|
322
|
+
try {
|
|
323
|
+
const gsdRoot = join(base, ".gsd");
|
|
324
|
+
mkdirSync(gsdRoot, { recursive: true });
|
|
325
|
+
|
|
326
|
+
// Write existing decisions
|
|
327
|
+
const decisionsPath = join(gsdRoot, "DECISIONS.md");
|
|
328
|
+
writeFileSync(decisionsPath, "# Decisions\n\n- Used TypeScript for type safety\n", "utf-8");
|
|
329
|
+
|
|
330
|
+
const { appendNewDecisions } = await import("../mechanical-completion.js");
|
|
331
|
+
|
|
332
|
+
// Call with one existing and one new decision
|
|
333
|
+
const mockSummaries = [
|
|
334
|
+
{
|
|
335
|
+
frontmatter: {
|
|
336
|
+
id: "T01", parent: "S01", milestone: "M001",
|
|
337
|
+
provides: [], requires: [], affects: [],
|
|
338
|
+
key_files: [], key_decisions: ["Used TypeScript for type safety", "Chose Express over Koa"],
|
|
339
|
+
patterns_established: [], drill_down_paths: [], observability_surfaces: [],
|
|
340
|
+
duration: "", verification_result: "passed", completed_at: "", blocker_discovered: false,
|
|
341
|
+
},
|
|
342
|
+
title: "T01", oneLiner: "", whatHappened: "", deviations: "", filesModified: [],
|
|
343
|
+
},
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
await appendNewDecisions(base, mockSummaries as any);
|
|
347
|
+
|
|
348
|
+
const updated = readFileSync(decisionsPath, "utf-8");
|
|
349
|
+
assert.ok(updated.includes("Chose Express over Koa"), "should append new decision");
|
|
350
|
+
// The existing decision should not be duplicated
|
|
351
|
+
const matches = updated.match(/Used TypeScript for type safety/g);
|
|
352
|
+
assert.equal(matches?.length, 1, "should not duplicate existing decision");
|
|
353
|
+
} finally {
|
|
354
|
+
rmSync(base, { recursive: true, force: true });
|
|
355
|
+
}
|
|
356
|
+
});
|
|
@@ -25,6 +25,7 @@ const BASE_VARS = {
|
|
|
25
25
|
outputPath: "/tmp/test-project/.gsd/milestones/M001/slices/S01/S01-PLAN.md",
|
|
26
26
|
inlinedContext: "--- test inlined context ---",
|
|
27
27
|
dependencySummaries: "", executorContextConstraints: "",
|
|
28
|
+
sourceFilePaths: "- **Requirements**: `.gsd/REQUIREMENTS.md`",
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
test("plan-slice prompt: commit step present when commit_docs=true", () => {
|