gsd-pi 2.17.0 → 2.19.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/README.md +39 -0
- package/dist/onboarding.js +2 -2
- package/dist/remote-questions-config.d.ts +10 -0
- package/dist/remote-questions-config.js +36 -0
- package/dist/resources/extensions/gsd/activity-log.ts +37 -7
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +65 -16
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +399 -29
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +382 -23
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/dist/resources/extensions/gsd/files.ts +123 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +237 -4
- package/dist/resources/extensions/gsd/index.ts +47 -3
- package/dist/resources/extensions/gsd/metrics.ts +48 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/paths.ts +9 -0
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +132 -1
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -0
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/queue-order.ts +231 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/dist/resources/extensions/gsd/state.ts +15 -3
- package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +227 -1
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +22 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/dist/resources/extensions/remote-questions/format.ts +12 -6
- package/dist/resources/extensions/remote-questions/manager.ts +8 -0
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts +5 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +21 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
- package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +5 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +4 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -2
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +21 -0
- package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/index.ts +5 -0
- package/packages/pi-coding-agent/src/main.ts +19 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
- package/src/resources/extensions/gsd/activity-log.ts +37 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +65 -16
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +399 -29
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +382 -23
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/src/resources/extensions/gsd/files.ts +123 -1
- package/src/resources/extensions/gsd/guided-flow.ts +237 -4
- package/src/resources/extensions/gsd/index.ts +47 -3
- package/src/resources/extensions/gsd/metrics.ts +48 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/paths.ts +9 -0
- package/src/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +132 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -0
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/queue-order.ts +231 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/src/resources/extensions/gsd/state.ts +15 -3
- package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/src/resources/extensions/gsd/templates/preferences.md +14 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +227 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +22 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/src/resources/extensions/remote-questions/format.ts +12 -6
- package/src/resources/extensions/remote-questions/manager.ts +8 -0
- package/src/resources/extensions/shared/next-action-ui.ts +16 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage dispatch ordering contract tests.
|
|
3
|
+
*
|
|
4
|
+
* These tests verify structural invariants of the triage integration
|
|
5
|
+
* by inspecting the actual source code of auto.ts and post-unit-hooks.ts.
|
|
6
|
+
* Full behavioral testing requires the @gsd/pi-coding-agent runtime.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const autoPath = join(__dirname, "..", "auto.ts");
|
|
17
|
+
const hooksPath = join(__dirname, "..", "post-unit-hooks.ts");
|
|
18
|
+
const autoPromptsPath = join(__dirname, "..", "auto-prompts.ts");
|
|
19
|
+
|
|
20
|
+
const autoSrc = readFileSync(autoPath, "utf-8");
|
|
21
|
+
const hooksSrc = readFileSync(hooksPath, "utf-8");
|
|
22
|
+
const autoPromptsSrc = (() => { try { return readFileSync(autoPromptsPath, "utf-8"); } catch { return autoSrc; } })();
|
|
23
|
+
|
|
24
|
+
// ─── Hook exclusion ──────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
test("dispatch: triage-captures excluded from post-unit hook triggering", () => {
|
|
27
|
+
// post-unit-hooks.ts must return null for triage-captures unit type
|
|
28
|
+
assert.ok(
|
|
29
|
+
hooksSrc.includes('"triage-captures"'),
|
|
30
|
+
"post-unit-hooks.ts should reference triage-captures",
|
|
31
|
+
);
|
|
32
|
+
assert.ok(
|
|
33
|
+
hooksSrc.includes('completedUnitType === "triage-captures"'),
|
|
34
|
+
"should check for triage-captures in the hook exclusion guard",
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ─── Triage check placement ──────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
test("dispatch: triage check appears after hook section and before stepMode check", () => {
|
|
41
|
+
const hookRetryIndex = autoSrc.indexOf("isRetryPending()");
|
|
42
|
+
// Find the triage check in handleAgentEnd (not in getAutoDashboardData)
|
|
43
|
+
const triageCheckIndex = autoSrc.indexOf("Triage check: dispatch triage unit");
|
|
44
|
+
const stepModeIndex = autoSrc.indexOf("In step mode, pause and show a wizard");
|
|
45
|
+
|
|
46
|
+
assert.ok(hookRetryIndex > 0, "hook retry check should exist");
|
|
47
|
+
assert.ok(triageCheckIndex > 0, "triage check block should exist");
|
|
48
|
+
assert.ok(stepModeIndex > 0, "step mode check should exist");
|
|
49
|
+
|
|
50
|
+
assert.ok(
|
|
51
|
+
triageCheckIndex > hookRetryIndex,
|
|
52
|
+
"triage check should come after hook retry check",
|
|
53
|
+
);
|
|
54
|
+
assert.ok(
|
|
55
|
+
triageCheckIndex < stepModeIndex,
|
|
56
|
+
"triage check should come before stepMode check",
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ─── Guard conditions ────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
test("dispatch: triage check guards against step mode", () => {
|
|
63
|
+
// The triage block should check !stepMode
|
|
64
|
+
const triageBlock = autoSrc.slice(
|
|
65
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
66
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
67
|
+
);
|
|
68
|
+
assert.ok(
|
|
69
|
+
triageBlock.includes("!stepMode"),
|
|
70
|
+
"triage block should guard against step mode",
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("dispatch: triage check guards against hook unit types", () => {
|
|
75
|
+
const triageBlock = autoSrc.slice(
|
|
76
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
77
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
78
|
+
);
|
|
79
|
+
assert.ok(
|
|
80
|
+
triageBlock.includes('!currentUnit.type.startsWith("hook/")'),
|
|
81
|
+
"triage block should not fire for hook units",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("dispatch: triage check guards against triage-on-triage", () => {
|
|
86
|
+
const triageBlock = autoSrc.slice(
|
|
87
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
88
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
89
|
+
);
|
|
90
|
+
assert.ok(
|
|
91
|
+
triageBlock.includes('currentUnit.type !== "triage-captures"'),
|
|
92
|
+
"triage block should not fire for triage units",
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("dispatch: triage check guards against quick-task triggering triage", () => {
|
|
97
|
+
const triageBlock = autoSrc.slice(
|
|
98
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
99
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
100
|
+
);
|
|
101
|
+
assert.ok(
|
|
102
|
+
triageBlock.includes('currentUnit.type !== "quick-task"'),
|
|
103
|
+
"triage block should not fire for quick-task units",
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("dispatch: triage dispatch uses early-return pattern", () => {
|
|
108
|
+
const triageBlock = autoSrc.slice(
|
|
109
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
110
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
111
|
+
);
|
|
112
|
+
assert.ok(
|
|
113
|
+
triageBlock.includes("return; // handleAgentEnd will fire again"),
|
|
114
|
+
"triage dispatch should return after sending message",
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("dispatch: triage imports hasPendingCaptures and loadPendingCaptures", () => {
|
|
119
|
+
assert.ok(
|
|
120
|
+
autoSrc.includes('hasPendingCaptures, loadPendingCaptures, countPendingCaptures') &&
|
|
121
|
+
autoSrc.includes('from "./captures.js"'),
|
|
122
|
+
"auto.ts should import capture functions including countPendingCaptures",
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ─── Prompt integration ──────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
test("dispatch: replan prompt builder loads capture context", () => {
|
|
129
|
+
const src = autoPromptsSrc;
|
|
130
|
+
assert.ok(
|
|
131
|
+
src.includes("loadReplanCaptures"),
|
|
132
|
+
"buildReplanSlicePrompt should load replan captures",
|
|
133
|
+
);
|
|
134
|
+
assert.ok(
|
|
135
|
+
src.includes("captureContext"),
|
|
136
|
+
"buildReplanSlicePrompt should pass captureContext to template",
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("dispatch: reassess prompt builder loads deferred captures", () => {
|
|
141
|
+
const src = autoPromptsSrc;
|
|
142
|
+
assert.ok(
|
|
143
|
+
src.includes("loadDeferredCaptures"),
|
|
144
|
+
"buildReassessRoadmapPrompt should load deferred captures",
|
|
145
|
+
);
|
|
146
|
+
assert.ok(
|
|
147
|
+
src.includes("deferredCaptures"),
|
|
148
|
+
"buildReassessRoadmapPrompt should pass deferredCaptures to template",
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ─── Prompt templates ────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
test("dispatch: replan prompt template includes captureContext variable", () => {
|
|
155
|
+
const promptPath = join(__dirname, "..", "prompts", "replan-slice.md");
|
|
156
|
+
const prompt = readFileSync(promptPath, "utf-8");
|
|
157
|
+
assert.ok(
|
|
158
|
+
prompt.includes("{{captureContext}}"),
|
|
159
|
+
"replan-slice.md should include {{captureContext}}",
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("dispatch: reassess prompt template includes deferredCaptures variable", () => {
|
|
164
|
+
const promptPath = join(__dirname, "..", "prompts", "reassess-roadmap.md");
|
|
165
|
+
const prompt = readFileSync(promptPath, "utf-8");
|
|
166
|
+
assert.ok(
|
|
167
|
+
prompt.includes("{{deferredCaptures}}"),
|
|
168
|
+
"reassess-roadmap.md should include {{deferredCaptures}}",
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("dispatch: triage prompt template exists and has classification criteria", () => {
|
|
173
|
+
const promptPath = join(__dirname, "..", "prompts", "triage-captures.md");
|
|
174
|
+
const prompt = readFileSync(promptPath, "utf-8");
|
|
175
|
+
assert.ok(prompt.includes("quick-task"), "should have quick-task classification");
|
|
176
|
+
assert.ok(prompt.includes("inject"), "should have inject classification");
|
|
177
|
+
assert.ok(prompt.includes("defer"), "should have defer classification");
|
|
178
|
+
assert.ok(prompt.includes("replan"), "should have replan classification");
|
|
179
|
+
assert.ok(prompt.includes("note"), "should have note classification");
|
|
180
|
+
assert.ok(prompt.includes("{{pendingCaptures}}"), "should have pending captures variable");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ─── Dashboard integration ───────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
test("dashboard: AutoDashboardData includes pendingCaptureCount field", () => {
|
|
186
|
+
assert.ok(
|
|
187
|
+
autoSrc.includes("pendingCaptureCount"),
|
|
188
|
+
"auto.ts should have pendingCaptureCount in AutoDashboardData",
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("dashboard: getAutoDashboardData computes pendingCaptureCount", () => {
|
|
193
|
+
assert.ok(
|
|
194
|
+
autoSrc.includes("pendingCaptureCount = countPendingCaptures") ||
|
|
195
|
+
autoSrc.includes("pendingCaptureCount = countPendingCaptures(basePath)"),
|
|
196
|
+
"getAutoDashboardData should compute pendingCaptureCount from countPendingCaptures (single-read)",
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("dashboard: overlay renders pending captures badge", () => {
|
|
201
|
+
const overlayPath = join(__dirname, "..", "dashboard-overlay.ts");
|
|
202
|
+
const overlaySrc = readFileSync(overlayPath, "utf-8");
|
|
203
|
+
assert.ok(
|
|
204
|
+
overlaySrc.includes("pendingCaptureCount"),
|
|
205
|
+
"dashboard-overlay.ts should reference pendingCaptureCount",
|
|
206
|
+
);
|
|
207
|
+
assert.ok(
|
|
208
|
+
overlaySrc.includes("pending capture"),
|
|
209
|
+
"dashboard-overlay.ts should show pending captures text",
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("dashboard: overlay labels triage-captures and quick-task unit types", () => {
|
|
214
|
+
const overlayPath = join(__dirname, "..", "dashboard-overlay.ts");
|
|
215
|
+
const overlaySrc = readFileSync(overlayPath, "utf-8");
|
|
216
|
+
assert.ok(
|
|
217
|
+
overlaySrc.includes('"triage-captures"'),
|
|
218
|
+
"unitLabel should handle triage-captures",
|
|
219
|
+
);
|
|
220
|
+
assert.ok(
|
|
221
|
+
overlaySrc.includes('"quick-task"'),
|
|
222
|
+
"unitLabel should handle quick-task",
|
|
223
|
+
);
|
|
224
|
+
});
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for GSD Triage Resolution — resolution execution and file overlap detection.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { mkdirSync, readFileSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { appendCapture, markCaptureResolved, loadAllCaptures } from "../captures.ts";
|
|
11
|
+
// Import only the functions that don't depend on @gsd/pi-coding-agent
|
|
12
|
+
// (triage-ui.ts imports next-action-ui.ts which imports the unavailable package)
|
|
13
|
+
import { executeInject, executeReplan, detectFileOverlap, loadDeferredCaptures, loadReplanCaptures, buildQuickTaskPrompt } from "../triage-resolution.ts";
|
|
14
|
+
|
|
15
|
+
function makeTempDir(prefix: string): string {
|
|
16
|
+
const dir = join(
|
|
17
|
+
tmpdir(),
|
|
18
|
+
`${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
19
|
+
);
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
return dir;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function setupPlanFile(tmp: string, mid: string, sid: string, content: string): string {
|
|
25
|
+
const planDir = join(tmp, ".gsd", "milestones", mid, "slices", sid);
|
|
26
|
+
mkdirSync(planDir, { recursive: true });
|
|
27
|
+
const planPath = join(planDir, `${sid}-PLAN.md`);
|
|
28
|
+
writeFileSync(planPath, content, "utf-8");
|
|
29
|
+
return planPath;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SAMPLE_PLAN = `# S01: Test Slice
|
|
33
|
+
|
|
34
|
+
**Goal:** Test
|
|
35
|
+
**Demo:** Test
|
|
36
|
+
|
|
37
|
+
## Must-Haves
|
|
38
|
+
|
|
39
|
+
- Something works
|
|
40
|
+
|
|
41
|
+
## Tasks
|
|
42
|
+
|
|
43
|
+
- [x] **T01: First task** \`est:1h\`
|
|
44
|
+
- Why: Setup
|
|
45
|
+
- Files: \`src/foo.ts\`, \`src/bar.ts\`
|
|
46
|
+
- Do: Build it
|
|
47
|
+
- Done when: Tests pass
|
|
48
|
+
|
|
49
|
+
- [ ] **T02: Second task** \`est:1h\`
|
|
50
|
+
- Why: Feature
|
|
51
|
+
- Files: \`src/baz.ts\`, \`src/qux.ts\`
|
|
52
|
+
- Do: Build it
|
|
53
|
+
- Done when: Tests pass
|
|
54
|
+
|
|
55
|
+
- [ ] **T03: Third task** \`est:30m\`
|
|
56
|
+
- Why: Polish
|
|
57
|
+
- Files: \`src/qux.ts\`, \`src/config.ts\`
|
|
58
|
+
- Do: Build it
|
|
59
|
+
- Done when: Tests pass
|
|
60
|
+
|
|
61
|
+
## Files Likely Touched
|
|
62
|
+
|
|
63
|
+
- \`src/foo.ts\`
|
|
64
|
+
- \`src/bar.ts\`
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
// ─── executeInject ────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
test("resolution: executeInject appends a new task to the plan", () => {
|
|
70
|
+
const tmp = makeTempDir("res-inject");
|
|
71
|
+
try {
|
|
72
|
+
const planPath = setupPlanFile(tmp, "M001", "S01", SAMPLE_PLAN);
|
|
73
|
+
const captureId = appendCapture(tmp, "add retry logic");
|
|
74
|
+
const captures = loadAllCaptures(tmp);
|
|
75
|
+
const capture = captures[0];
|
|
76
|
+
|
|
77
|
+
const newId = executeInject(tmp, "M001", "S01", capture);
|
|
78
|
+
|
|
79
|
+
assert.strictEqual(newId, "T04", "should be T04 (next after T03)");
|
|
80
|
+
|
|
81
|
+
const updated = readFileSync(planPath, "utf-8");
|
|
82
|
+
assert.ok(updated.includes("**T04:"), "should have T04 in plan");
|
|
83
|
+
assert.ok(updated.includes(capture.text), "should include capture text");
|
|
84
|
+
assert.ok(updated.includes("## Files Likely Touched"), "should preserve files section");
|
|
85
|
+
|
|
86
|
+
// T04 should appear before Files Likely Touched
|
|
87
|
+
const t04Pos = updated.indexOf("**T04:");
|
|
88
|
+
const filesPos = updated.indexOf("## Files Likely Touched");
|
|
89
|
+
assert.ok(t04Pos < filesPos, "T04 should be before Files section");
|
|
90
|
+
} finally {
|
|
91
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("resolution: executeInject returns null when plan doesn't exist", () => {
|
|
96
|
+
const tmp = makeTempDir("res-inject-noplan");
|
|
97
|
+
try {
|
|
98
|
+
const captureId = appendCapture(tmp, "some task");
|
|
99
|
+
const captures = loadAllCaptures(tmp);
|
|
100
|
+
const result = executeInject(tmp, "M001", "S01", captures[0]);
|
|
101
|
+
assert.strictEqual(result, null);
|
|
102
|
+
} finally {
|
|
103
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ─── executeReplan ────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
test("resolution: executeReplan writes REPLAN-TRIGGER.md", () => {
|
|
110
|
+
const tmp = makeTempDir("res-replan");
|
|
111
|
+
try {
|
|
112
|
+
setupPlanFile(tmp, "M001", "S01", SAMPLE_PLAN);
|
|
113
|
+
const captureId = appendCapture(tmp, "approach is wrong, need different strategy");
|
|
114
|
+
const captures = loadAllCaptures(tmp);
|
|
115
|
+
const capture = captures[0];
|
|
116
|
+
|
|
117
|
+
const result = executeReplan(tmp, "M001", "S01", capture);
|
|
118
|
+
assert.strictEqual(result, true);
|
|
119
|
+
|
|
120
|
+
const triggerPath = join(
|
|
121
|
+
tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-REPLAN-TRIGGER.md",
|
|
122
|
+
);
|
|
123
|
+
assert.ok(existsSync(triggerPath), "trigger file should exist");
|
|
124
|
+
|
|
125
|
+
const content = readFileSync(triggerPath, "utf-8");
|
|
126
|
+
assert.ok(content.includes(capture.id), "should include capture ID");
|
|
127
|
+
assert.ok(content.includes(capture.text), "should include capture text");
|
|
128
|
+
assert.ok(content.includes("# Replan Trigger"), "should have header");
|
|
129
|
+
} finally {
|
|
130
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ─── detectFileOverlap ───────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
test("resolution: detectFileOverlap finds overlapping incomplete tasks", () => {
|
|
137
|
+
const overlaps = detectFileOverlap(["src/qux.ts"], SAMPLE_PLAN);
|
|
138
|
+
assert.deepStrictEqual(overlaps, ["T02", "T03"]);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("resolution: detectFileOverlap ignores completed tasks", () => {
|
|
142
|
+
// T01 is [x] and uses src/foo.ts — should NOT be returned
|
|
143
|
+
const overlaps = detectFileOverlap(["src/foo.ts"], SAMPLE_PLAN);
|
|
144
|
+
assert.deepStrictEqual(overlaps, []);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("resolution: detectFileOverlap returns empty when no overlap", () => {
|
|
148
|
+
const overlaps = detectFileOverlap(["src/unrelated.ts"], SAMPLE_PLAN);
|
|
149
|
+
assert.deepStrictEqual(overlaps, []);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("resolution: detectFileOverlap returns empty for empty affected files", () => {
|
|
153
|
+
assert.deepStrictEqual(detectFileOverlap([], SAMPLE_PLAN), []);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("resolution: detectFileOverlap is case-insensitive", () => {
|
|
157
|
+
const overlaps = detectFileOverlap(["SRC/QUX.TS"], SAMPLE_PLAN);
|
|
158
|
+
assert.deepStrictEqual(overlaps, ["T02", "T03"]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// ─── loadDeferredCaptures / loadReplanCaptures ───────────────────────────────
|
|
162
|
+
|
|
163
|
+
test("resolution: loadDeferredCaptures returns only deferred captures", () => {
|
|
164
|
+
const tmp = makeTempDir("res-deferred");
|
|
165
|
+
try {
|
|
166
|
+
const id1 = appendCapture(tmp, "deferred one");
|
|
167
|
+
const id2 = appendCapture(tmp, "note one");
|
|
168
|
+
const id3 = appendCapture(tmp, "deferred two");
|
|
169
|
+
|
|
170
|
+
markCaptureResolved(tmp, id1, "defer", "deferred to S03", "future work");
|
|
171
|
+
markCaptureResolved(tmp, id2, "note", "acknowledged", "just a note");
|
|
172
|
+
markCaptureResolved(tmp, id3, "defer", "deferred to S04", "later");
|
|
173
|
+
|
|
174
|
+
const deferred = loadDeferredCaptures(tmp);
|
|
175
|
+
assert.strictEqual(deferred.length, 2);
|
|
176
|
+
assert.strictEqual(deferred[0].id, id1);
|
|
177
|
+
assert.strictEqual(deferred[1].id, id3);
|
|
178
|
+
} finally {
|
|
179
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("resolution: loadReplanCaptures returns only replan captures", () => {
|
|
184
|
+
const tmp = makeTempDir("res-replan-load");
|
|
185
|
+
try {
|
|
186
|
+
const id1 = appendCapture(tmp, "needs replan");
|
|
187
|
+
const id2 = appendCapture(tmp, "just a note");
|
|
188
|
+
|
|
189
|
+
markCaptureResolved(tmp, id1, "replan", "replan triggered", "approach changed");
|
|
190
|
+
markCaptureResolved(tmp, id2, "note", "acknowledged", "info only");
|
|
191
|
+
|
|
192
|
+
const replans = loadReplanCaptures(tmp);
|
|
193
|
+
assert.strictEqual(replans.length, 1);
|
|
194
|
+
assert.strictEqual(replans[0].id, id1);
|
|
195
|
+
} finally {
|
|
196
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ─── buildQuickTaskPrompt ────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
test("resolution: buildQuickTaskPrompt includes capture text and ID", () => {
|
|
203
|
+
const prompt = buildQuickTaskPrompt({
|
|
204
|
+
id: "CAP-abc123",
|
|
205
|
+
text: "add retry logic to OAuth",
|
|
206
|
+
timestamp: "2026-03-15T20:00:00Z",
|
|
207
|
+
status: "resolved",
|
|
208
|
+
classification: "quick-task",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
assert.ok(prompt.includes("CAP-abc123"), "should include capture ID");
|
|
212
|
+
assert.ok(prompt.includes("add retry logic to OAuth"), "should include capture text");
|
|
213
|
+
assert.ok(prompt.includes("Quick Task"), "should have Quick Task header");
|
|
214
|
+
assert.ok(prompt.includes("Do NOT modify"), "should warn about plan files");
|
|
215
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Tests for GSD visualizer data loader.
|
|
2
|
+
// Verifies the VisualizerData interface shape and source-file contracts.
|
|
3
|
+
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join, dirname } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const { assertTrue, report } = createTestContext();
|
|
11
|
+
|
|
12
|
+
const dataPath = join(__dirname, "..", "visualizer-data.ts");
|
|
13
|
+
const dataSrc = readFileSync(dataPath, "utf-8");
|
|
14
|
+
|
|
15
|
+
console.log("\n=== visualizer-data.ts source contracts ===");
|
|
16
|
+
|
|
17
|
+
// Interface exports
|
|
18
|
+
assertTrue(
|
|
19
|
+
dataSrc.includes("export interface VisualizerData"),
|
|
20
|
+
"exports VisualizerData interface",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
assertTrue(
|
|
24
|
+
dataSrc.includes("export interface VisualizerMilestone"),
|
|
25
|
+
"exports VisualizerMilestone interface",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
assertTrue(
|
|
29
|
+
dataSrc.includes("export interface VisualizerSlice"),
|
|
30
|
+
"exports VisualizerSlice interface",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
assertTrue(
|
|
34
|
+
dataSrc.includes("export interface VisualizerTask"),
|
|
35
|
+
"exports VisualizerTask interface",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Function export
|
|
39
|
+
assertTrue(
|
|
40
|
+
dataSrc.includes("export async function loadVisualizerData"),
|
|
41
|
+
"exports loadVisualizerData function",
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Data source usage
|
|
45
|
+
assertTrue(
|
|
46
|
+
dataSrc.includes("deriveState"),
|
|
47
|
+
"uses deriveState for state derivation",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
assertTrue(
|
|
51
|
+
dataSrc.includes("findMilestoneIds"),
|
|
52
|
+
"uses findMilestoneIds to enumerate milestones",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
assertTrue(
|
|
56
|
+
dataSrc.includes("parseRoadmap"),
|
|
57
|
+
"uses parseRoadmap for roadmap parsing",
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
assertTrue(
|
|
61
|
+
dataSrc.includes("parsePlan"),
|
|
62
|
+
"uses parsePlan for plan parsing",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
assertTrue(
|
|
66
|
+
dataSrc.includes("getLedger"),
|
|
67
|
+
"uses getLedger for in-memory metrics",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
assertTrue(
|
|
71
|
+
dataSrc.includes("loadLedgerFromDisk"),
|
|
72
|
+
"uses loadLedgerFromDisk as fallback",
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
assertTrue(
|
|
76
|
+
dataSrc.includes("getProjectTotals"),
|
|
77
|
+
"uses getProjectTotals for aggregation",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
assertTrue(
|
|
81
|
+
dataSrc.includes("aggregateByPhase"),
|
|
82
|
+
"uses aggregateByPhase",
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
assertTrue(
|
|
86
|
+
dataSrc.includes("aggregateBySlice"),
|
|
87
|
+
"uses aggregateBySlice",
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
assertTrue(
|
|
91
|
+
dataSrc.includes("aggregateByModel"),
|
|
92
|
+
"uses aggregateByModel",
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Interface fields
|
|
96
|
+
assertTrue(
|
|
97
|
+
dataSrc.includes("dependsOn: string[]"),
|
|
98
|
+
"VisualizerMilestone has dependsOn field",
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
assertTrue(
|
|
102
|
+
dataSrc.includes("depends: string[]"),
|
|
103
|
+
"VisualizerSlice has depends field",
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
assertTrue(
|
|
107
|
+
dataSrc.includes("totals: ProjectTotals | null"),
|
|
108
|
+
"VisualizerData has nullable totals",
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
assertTrue(
|
|
112
|
+
dataSrc.includes("units: UnitMetrics[]"),
|
|
113
|
+
"VisualizerData has units array",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Verify overlay source exists and imports data module
|
|
117
|
+
const overlayPath = join(__dirname, "..", "visualizer-overlay.ts");
|
|
118
|
+
const overlaySrc = readFileSync(overlayPath, "utf-8");
|
|
119
|
+
|
|
120
|
+
console.log("\n=== visualizer-overlay.ts source contracts ===");
|
|
121
|
+
|
|
122
|
+
assertTrue(
|
|
123
|
+
overlaySrc.includes("export class GSDVisualizerOverlay"),
|
|
124
|
+
"exports GSDVisualizerOverlay class",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
assertTrue(
|
|
128
|
+
overlaySrc.includes("loadVisualizerData"),
|
|
129
|
+
"overlay uses loadVisualizerData",
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
assertTrue(
|
|
133
|
+
overlaySrc.includes("renderProgressView"),
|
|
134
|
+
"overlay delegates to renderProgressView",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
assertTrue(
|
|
138
|
+
overlaySrc.includes("renderDepsView"),
|
|
139
|
+
"overlay delegates to renderDepsView",
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
assertTrue(
|
|
143
|
+
overlaySrc.includes("renderMetricsView"),
|
|
144
|
+
"overlay delegates to renderMetricsView",
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
assertTrue(
|
|
148
|
+
overlaySrc.includes("renderTimelineView"),
|
|
149
|
+
"overlay delegates to renderTimelineView",
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
assertTrue(
|
|
153
|
+
overlaySrc.includes("handleInput"),
|
|
154
|
+
"overlay has handleInput method",
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
assertTrue(
|
|
158
|
+
overlaySrc.includes("dispose"),
|
|
159
|
+
"overlay has dispose method",
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
assertTrue(
|
|
163
|
+
overlaySrc.includes("wrapInBox"),
|
|
164
|
+
"overlay has wrapInBox helper",
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
assertTrue(
|
|
168
|
+
overlaySrc.includes("activeTab"),
|
|
169
|
+
"overlay tracks active tab",
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
assertTrue(
|
|
173
|
+
overlaySrc.includes("scrollOffsets"),
|
|
174
|
+
"overlay tracks per-tab scroll offsets",
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Verify commands.ts integration
|
|
178
|
+
const commandsPath = join(__dirname, "..", "commands.ts");
|
|
179
|
+
const commandsSrc = readFileSync(commandsPath, "utf-8");
|
|
180
|
+
|
|
181
|
+
console.log("\n=== commands.ts integration ===");
|
|
182
|
+
|
|
183
|
+
assertTrue(
|
|
184
|
+
commandsSrc.includes('"visualize"'),
|
|
185
|
+
"commands.ts has visualize in subcommands array",
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
assertTrue(
|
|
189
|
+
commandsSrc.includes("GSDVisualizerOverlay"),
|
|
190
|
+
"commands.ts imports GSDVisualizerOverlay",
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
assertTrue(
|
|
194
|
+
commandsSrc.includes("handleVisualize"),
|
|
195
|
+
"commands.ts has handleVisualize handler",
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
report();
|