gsd-pi 2.18.0 → 2.20.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 +5 -1
- package/dist/cli.js +3 -3
- package/dist/onboarding.d.ts +3 -1
- package/dist/onboarding.js +77 -3
- package/dist/remote-questions-config.d.ts +1 -1
- package/dist/resources/extensions/google-search/index.ts +164 -47
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/dist/resources/extensions/gsd/auto.ts +690 -39
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +654 -36
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/context-budget.ts +243 -0
- package/dist/resources/extensions/gsd/context-store.ts +195 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/dist/resources/extensions/gsd/db-writer.ts +341 -0
- package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/dist/resources/extensions/gsd/doctor.ts +283 -2
- package/dist/resources/extensions/gsd/export.ts +81 -2
- package/dist/resources/extensions/gsd/files.ts +39 -9
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
- package/dist/resources/extensions/gsd/history.ts +0 -1
- package/dist/resources/extensions/gsd/index.ts +277 -1
- package/dist/resources/extensions/gsd/md-importer.ts +526 -0
- package/dist/resources/extensions/gsd/metrics.ts +84 -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/notifications.ts +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/dist/resources/extensions/gsd/preferences.ts +198 -150
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
- 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 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/quick.ts +156 -0
- package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/dist/resources/extensions/gsd/skill-health.ts +417 -0
- package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -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/context-budget.test.ts +283 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -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-isolation.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -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/unit-runtime.test.ts +25 -1
- package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -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/types.ts +29 -0
- package/dist/resources/extensions/gsd/undo.ts +0 -1
- package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/dist/resources/extensions/remote-questions/config.ts +4 -2
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/dist/resources/extensions/remote-questions/format.ts +166 -14
- package/dist/resources/extensions/remote-questions/manager.ts +14 -4
- package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/dist/resources/extensions/remote-questions/types.ts +2 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/dist/resources/extensions/voice/index.ts +4 -3
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -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 +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -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 +43 -11
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
- package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
- package/src/resources/extensions/google-search/index.ts +164 -47
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/src/resources/extensions/gsd/auto.ts +690 -39
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +654 -36
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/context-budget.ts +243 -0
- package/src/resources/extensions/gsd/context-store.ts +195 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/src/resources/extensions/gsd/db-writer.ts +341 -0
- package/src/resources/extensions/gsd/debug-logger.ts +178 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/src/resources/extensions/gsd/doctor.ts +283 -2
- package/src/resources/extensions/gsd/export.ts +81 -2
- package/src/resources/extensions/gsd/files.ts +39 -9
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gsd-db.ts +752 -0
- package/src/resources/extensions/gsd/guided-flow.ts +26 -1
- package/src/resources/extensions/gsd/history.ts +0 -1
- package/src/resources/extensions/gsd/index.ts +277 -1
- package/src/resources/extensions/gsd/md-importer.ts +526 -0
- package/src/resources/extensions/gsd/metrics.ts +84 -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/notifications.ts +0 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/src/resources/extensions/gsd/preferences.ts +198 -150
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
- 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 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/quick.ts +156 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/src/resources/extensions/gsd/skill-health.ts +417 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -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/context-budget.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -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-isolation.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -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/unit-runtime.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -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/types.ts +29 -0
- package/src/resources/extensions/gsd/undo.ts +0 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/src/resources/extensions/gsd/worktree-command.ts +18 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/src/resources/extensions/remote-questions/config.ts +4 -2
- package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/src/resources/extensions/remote-questions/format.ts +166 -14
- package/src/resources/extensions/remote-questions/manager.ts +14 -4
- package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/src/resources/extensions/remote-questions/types.ts +2 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/src/resources/extensions/voice/index.ts +4 -3
|
@@ -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
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { mkdtempSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import {
|
|
@@ -65,6 +65,30 @@ console.log("\n=== runtime record cleanup ===");
|
|
|
65
65
|
assertEq(loaded, null, "record removed");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
console.log("\n=== hook unit type sanitization (slash in unitType) ===");
|
|
69
|
+
{
|
|
70
|
+
// Hook units have unitType like "hook/code-review" with a slash
|
|
71
|
+
// This should NOT create a subdirectory - the slash must be sanitized
|
|
72
|
+
const hookRecord = writeUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10", 2000, { phase: "dispatched" });
|
|
73
|
+
assertEq(hookRecord.unitType, "hook/code-review", "unitType preserved in record");
|
|
74
|
+
assertEq(hookRecord.unitId, "M100/S02/T10", "unitId preserved in record");
|
|
75
|
+
|
|
76
|
+
const loaded = readUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10");
|
|
77
|
+
assertTrue(loaded !== null, "hook record readable");
|
|
78
|
+
assertEq(loaded!.phase, "dispatched", "hook phase correct");
|
|
79
|
+
|
|
80
|
+
// Verify the file is in the units dir, not in a subdirectory
|
|
81
|
+
const unitsDir = join(base, ".gsd", "runtime", "units");
|
|
82
|
+
const files = readdirSync(unitsDir);
|
|
83
|
+
const hookFile = files.find((f: string) => f.includes("hook-code-review"));
|
|
84
|
+
assertTrue(hookFile !== undefined, "hook file exists with sanitized name");
|
|
85
|
+
assertTrue(!files.some((f: string) => f === "hook"), "no 'hook' subdirectory created");
|
|
86
|
+
|
|
87
|
+
clearUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10");
|
|
88
|
+
const cleared = readUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10");
|
|
89
|
+
assertEq(cleared, null, "hook record removed");
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
// ─── Must-have durability integration tests ───────────────────────────────
|
|
69
93
|
|
|
70
94
|
// Create a separate temp base for must-have tests to avoid interference
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Tests for critical path algorithm.
|
|
2
|
+
// Tests computeCriticalPath with known DAG structures.
|
|
3
|
+
|
|
4
|
+
import { computeCriticalPath } from "../visualizer-data.js";
|
|
5
|
+
import type { VisualizerMilestone } from "../visualizer-data.js";
|
|
6
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
7
|
+
|
|
8
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
9
|
+
|
|
10
|
+
function makeMs(id: string, status: "complete" | "active" | "pending", dependsOn: string[], slices: any[] = []): VisualizerMilestone {
|
|
11
|
+
return { id, title: id, status, dependsOn, slices };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function makeSlice(id: string, done: boolean, depends: string[] = []) {
|
|
15
|
+
return { id, title: id, done, active: false, risk: "low", depends, tasks: [] };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ─── Linear chain ───────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
console.log("\n=== Critical Path: Linear Chain ===");
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
// M001 -> M002 -> M003
|
|
24
|
+
const milestones = [
|
|
25
|
+
makeMs("M001", "complete", []),
|
|
26
|
+
makeMs("M002", "active", ["M001"], [
|
|
27
|
+
makeSlice("S01", true),
|
|
28
|
+
makeSlice("S02", false, ["S01"]),
|
|
29
|
+
]),
|
|
30
|
+
makeMs("M003", "pending", ["M002"]),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const cp = computeCriticalPath(milestones);
|
|
34
|
+
assertTrue(cp.milestonePath.length > 0, "linear chain has critical path");
|
|
35
|
+
assertTrue(cp.milestonePath.includes("M002"), "M002 is on critical path");
|
|
36
|
+
assertTrue(cp.milestonePath.includes("M003"), "M003 is on critical path");
|
|
37
|
+
assertEq(cp.milestoneSlack.get("M002"), 0, "M002 has zero slack");
|
|
38
|
+
assertEq(cp.milestoneSlack.get("M003"), 0, "M003 has zero slack");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Diamond DAG ────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
console.log("\n=== Critical Path: Diamond DAG ===");
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
// M001 -> M002 -> M004
|
|
47
|
+
// M001 -> M003 -> M004
|
|
48
|
+
// M002 has 3 incomplete slices, M003 has 1 incomplete slice
|
|
49
|
+
const milestones = [
|
|
50
|
+
makeMs("M001", "complete", []),
|
|
51
|
+
makeMs("M002", "active", ["M001"], [
|
|
52
|
+
makeSlice("S01", false),
|
|
53
|
+
makeSlice("S02", false),
|
|
54
|
+
makeSlice("S03", false),
|
|
55
|
+
]),
|
|
56
|
+
makeMs("M003", "pending", ["M001"], [
|
|
57
|
+
makeSlice("S01", false),
|
|
58
|
+
]),
|
|
59
|
+
makeMs("M004", "pending", ["M002", "M003"]),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const cp = computeCriticalPath(milestones);
|
|
63
|
+
assertTrue(cp.milestonePath.length >= 2, "diamond DAG has critical path");
|
|
64
|
+
// M002 has weight 3 (3 incomplete), M003 has weight 1
|
|
65
|
+
// Critical path should go through M002 (longer)
|
|
66
|
+
assertTrue(cp.milestonePath.includes("M002"), "M002 (heavier) is on critical path");
|
|
67
|
+
|
|
68
|
+
// M003 should have non-zero slack since it's lighter
|
|
69
|
+
const m003Slack = cp.milestoneSlack.get("M003") ?? -1;
|
|
70
|
+
assertTrue(m003Slack > 0, "M003 has positive slack (lighter branch)");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── Independent branches ───────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
console.log("\n=== Critical Path: Independent Branches ===");
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
// M001 (no deps), M002 (no deps), M003 (no deps)
|
|
79
|
+
const milestones = [
|
|
80
|
+
makeMs("M001", "active", [], [makeSlice("S01", false)]),
|
|
81
|
+
makeMs("M002", "pending", [], [makeSlice("S01", false), makeSlice("S02", false)]),
|
|
82
|
+
makeMs("M003", "pending", [], [makeSlice("S01", false)]),
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const cp = computeCriticalPath(milestones);
|
|
86
|
+
assertTrue(cp.milestonePath.length >= 1, "independent branches have at least one critical node");
|
|
87
|
+
// M002 has the most incomplete slices, should be critical
|
|
88
|
+
assertTrue(cp.milestonePath.includes("M002"), "M002 (longest) is on critical path");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── Slice-level critical path ──────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
console.log("\n=== Critical Path: Slice-level ===");
|
|
94
|
+
|
|
95
|
+
{
|
|
96
|
+
// Active milestone with slice dependencies: S01 -> S02 -> S04, S01 -> S03
|
|
97
|
+
const milestones = [
|
|
98
|
+
makeMs("M001", "active", [], [
|
|
99
|
+
makeSlice("S01", true),
|
|
100
|
+
makeSlice("S02", false, ["S01"]),
|
|
101
|
+
makeSlice("S03", false, ["S01"]),
|
|
102
|
+
makeSlice("S04", false, ["S02"]),
|
|
103
|
+
]),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const cp = computeCriticalPath(milestones);
|
|
107
|
+
assertTrue(cp.slicePath.length > 0, "has slice-level critical path");
|
|
108
|
+
assertTrue(cp.slicePath.includes("S02"), "S02 is on slice critical path");
|
|
109
|
+
assertTrue(cp.slicePath.includes("S04"), "S04 is on slice critical path");
|
|
110
|
+
|
|
111
|
+
// S03 should have non-zero slack (it's a shorter branch)
|
|
112
|
+
const s03Slack = cp.sliceSlack.get("S03") ?? -1;
|
|
113
|
+
assertTrue(s03Slack > 0, "S03 has positive slack (shorter branch)");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─── Empty milestones ───────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
console.log("\n=== Critical Path: Empty ===");
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
const cp = computeCriticalPath([]);
|
|
122
|
+
assertEq(cp.milestonePath.length, 0, "empty milestones produce empty path");
|
|
123
|
+
assertEq(cp.slicePath.length, 0, "empty milestones produce empty slice path");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Single milestone ───────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
console.log("\n=== Critical Path: Single Milestone ===");
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
const milestones = [
|
|
132
|
+
makeMs("M001", "active", [], [
|
|
133
|
+
makeSlice("S01", false),
|
|
134
|
+
makeSlice("S02", false),
|
|
135
|
+
]),
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
const cp = computeCriticalPath(milestones);
|
|
139
|
+
assertTrue(cp.milestonePath.length === 1, "single milestone is its own critical path");
|
|
140
|
+
assertEq(cp.milestonePath[0], "M001", "M001 is the critical node");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Report ─────────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
report();
|
|
@@ -0,0 +1,290 @@
|
|
|
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
|
+
// New interfaces
|
|
39
|
+
assertTrue(
|
|
40
|
+
dataSrc.includes("export interface CriticalPathInfo"),
|
|
41
|
+
"exports CriticalPathInfo interface",
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
assertTrue(
|
|
45
|
+
dataSrc.includes("export interface AgentActivityInfo"),
|
|
46
|
+
"exports AgentActivityInfo interface",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
assertTrue(
|
|
50
|
+
dataSrc.includes("export interface ChangelogEntry"),
|
|
51
|
+
"exports ChangelogEntry interface",
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
assertTrue(
|
|
55
|
+
dataSrc.includes("export interface ChangelogInfo"),
|
|
56
|
+
"exports ChangelogInfo interface",
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Function export
|
|
60
|
+
assertTrue(
|
|
61
|
+
dataSrc.includes("export async function loadVisualizerData"),
|
|
62
|
+
"exports loadVisualizerData function",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
assertTrue(
|
|
66
|
+
dataSrc.includes("export function computeCriticalPath"),
|
|
67
|
+
"exports computeCriticalPath function",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Data source usage
|
|
71
|
+
assertTrue(
|
|
72
|
+
dataSrc.includes("deriveState"),
|
|
73
|
+
"uses deriveState for state derivation",
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
assertTrue(
|
|
77
|
+
dataSrc.includes("findMilestoneIds"),
|
|
78
|
+
"uses findMilestoneIds to enumerate milestones",
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
assertTrue(
|
|
82
|
+
dataSrc.includes("parseRoadmap"),
|
|
83
|
+
"uses parseRoadmap for roadmap parsing",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
assertTrue(
|
|
87
|
+
dataSrc.includes("parsePlan"),
|
|
88
|
+
"uses parsePlan for plan parsing",
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
assertTrue(
|
|
92
|
+
dataSrc.includes("parseSummary"),
|
|
93
|
+
"uses parseSummary for changelog parsing",
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
assertTrue(
|
|
97
|
+
dataSrc.includes("getLedger"),
|
|
98
|
+
"uses getLedger for in-memory metrics",
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
assertTrue(
|
|
102
|
+
dataSrc.includes("loadLedgerFromDisk"),
|
|
103
|
+
"uses loadLedgerFromDisk as fallback",
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
assertTrue(
|
|
107
|
+
dataSrc.includes("getProjectTotals"),
|
|
108
|
+
"uses getProjectTotals for aggregation",
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
assertTrue(
|
|
112
|
+
dataSrc.includes("aggregateByPhase"),
|
|
113
|
+
"uses aggregateByPhase",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
assertTrue(
|
|
117
|
+
dataSrc.includes("aggregateBySlice"),
|
|
118
|
+
"uses aggregateBySlice",
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
assertTrue(
|
|
122
|
+
dataSrc.includes("aggregateByModel"),
|
|
123
|
+
"uses aggregateByModel",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Interface fields
|
|
127
|
+
assertTrue(
|
|
128
|
+
dataSrc.includes("dependsOn: string[]"),
|
|
129
|
+
"VisualizerMilestone has dependsOn field",
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
assertTrue(
|
|
133
|
+
dataSrc.includes("depends: string[]"),
|
|
134
|
+
"VisualizerSlice has depends field",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
assertTrue(
|
|
138
|
+
dataSrc.includes("totals: ProjectTotals | null"),
|
|
139
|
+
"VisualizerData has nullable totals",
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
assertTrue(
|
|
143
|
+
dataSrc.includes("units: UnitMetrics[]"),
|
|
144
|
+
"VisualizerData has units array",
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// New data model fields
|
|
148
|
+
assertTrue(
|
|
149
|
+
dataSrc.includes("criticalPath: CriticalPathInfo"),
|
|
150
|
+
"VisualizerData has criticalPath field",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
assertTrue(
|
|
154
|
+
dataSrc.includes("remainingSliceCount: number"),
|
|
155
|
+
"VisualizerData has remainingSliceCount field",
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
assertTrue(
|
|
159
|
+
dataSrc.includes("agentActivity: AgentActivityInfo | null"),
|
|
160
|
+
"VisualizerData has agentActivity field",
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
assertTrue(
|
|
164
|
+
dataSrc.includes("changelog: ChangelogInfo"),
|
|
165
|
+
"VisualizerData has changelog field",
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Verify overlay source exists and imports data module
|
|
169
|
+
const overlayPath = join(__dirname, "..", "visualizer-overlay.ts");
|
|
170
|
+
const overlaySrc = readFileSync(overlayPath, "utf-8");
|
|
171
|
+
|
|
172
|
+
console.log("\n=== visualizer-overlay.ts source contracts ===");
|
|
173
|
+
|
|
174
|
+
assertTrue(
|
|
175
|
+
overlaySrc.includes("export class GSDVisualizerOverlay"),
|
|
176
|
+
"exports GSDVisualizerOverlay class",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
assertTrue(
|
|
180
|
+
overlaySrc.includes("loadVisualizerData"),
|
|
181
|
+
"overlay uses loadVisualizerData",
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
assertTrue(
|
|
185
|
+
overlaySrc.includes("renderProgressView"),
|
|
186
|
+
"overlay delegates to renderProgressView",
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
assertTrue(
|
|
190
|
+
overlaySrc.includes("renderDepsView"),
|
|
191
|
+
"overlay delegates to renderDepsView",
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
assertTrue(
|
|
195
|
+
overlaySrc.includes("renderMetricsView"),
|
|
196
|
+
"overlay delegates to renderMetricsView",
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
assertTrue(
|
|
200
|
+
overlaySrc.includes("renderTimelineView"),
|
|
201
|
+
"overlay delegates to renderTimelineView",
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
assertTrue(
|
|
205
|
+
overlaySrc.includes("renderAgentView"),
|
|
206
|
+
"overlay delegates to renderAgentView",
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
assertTrue(
|
|
210
|
+
overlaySrc.includes("renderChangelogView"),
|
|
211
|
+
"overlay delegates to renderChangelogView",
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
assertTrue(
|
|
215
|
+
overlaySrc.includes("renderExportView"),
|
|
216
|
+
"overlay delegates to renderExportView",
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
assertTrue(
|
|
220
|
+
overlaySrc.includes("handleInput"),
|
|
221
|
+
"overlay has handleInput method",
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
assertTrue(
|
|
225
|
+
overlaySrc.includes("dispose"),
|
|
226
|
+
"overlay has dispose method",
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
assertTrue(
|
|
230
|
+
overlaySrc.includes("wrapInBox"),
|
|
231
|
+
"overlay has wrapInBox helper",
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
assertTrue(
|
|
235
|
+
overlaySrc.includes("activeTab"),
|
|
236
|
+
"overlay tracks active tab",
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
assertTrue(
|
|
240
|
+
overlaySrc.includes("scrollOffsets"),
|
|
241
|
+
"overlay tracks per-tab scroll offsets",
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
assertTrue(
|
|
245
|
+
overlaySrc.includes("filterMode"),
|
|
246
|
+
"overlay has filterMode state",
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
assertTrue(
|
|
250
|
+
overlaySrc.includes("filterText"),
|
|
251
|
+
"overlay has filterText state",
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
assertTrue(
|
|
255
|
+
overlaySrc.includes("filterField"),
|
|
256
|
+
"overlay has filterField state",
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
assertTrue(
|
|
260
|
+
overlaySrc.includes("TAB_COUNT"),
|
|
261
|
+
"overlay defines TAB_COUNT",
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
assertTrue(
|
|
265
|
+
overlaySrc.includes("7 Export"),
|
|
266
|
+
"overlay has 7 tab labels",
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Verify commands.ts integration
|
|
270
|
+
const commandsPath = join(__dirname, "..", "commands.ts");
|
|
271
|
+
const commandsSrc = readFileSync(commandsPath, "utf-8");
|
|
272
|
+
|
|
273
|
+
console.log("\n=== commands.ts integration ===");
|
|
274
|
+
|
|
275
|
+
assertTrue(
|
|
276
|
+
commandsSrc.includes('"visualize"'),
|
|
277
|
+
"commands.ts has visualize in subcommands array",
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
assertTrue(
|
|
281
|
+
commandsSrc.includes("GSDVisualizerOverlay"),
|
|
282
|
+
"commands.ts imports GSDVisualizerOverlay",
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
assertTrue(
|
|
286
|
+
commandsSrc.includes("handleVisualize"),
|
|
287
|
+
"commands.ts has handleVisualize handler",
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
report();
|