gsd-pi 2.19.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.
Files changed (249) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.js +3 -3
  3. package/dist/onboarding.d.ts +3 -1
  4. package/dist/onboarding.js +77 -3
  5. package/dist/remote-questions-config.d.ts +1 -1
  6. package/dist/resources/extensions/google-search/index.ts +164 -47
  7. package/dist/resources/extensions/gsd/auto-prompts.ts +103 -24
  8. package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
  9. package/dist/resources/extensions/gsd/auto.ts +424 -30
  10. package/dist/resources/extensions/gsd/commands.ts +518 -36
  11. package/dist/resources/extensions/gsd/context-budget.ts +243 -0
  12. package/dist/resources/extensions/gsd/context-store.ts +195 -0
  13. package/dist/resources/extensions/gsd/dashboard-overlay.ts +41 -3
  14. package/dist/resources/extensions/gsd/db-writer.ts +341 -0
  15. package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
  16. package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
  17. package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
  18. package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
  19. package/dist/resources/extensions/gsd/doctor.ts +283 -2
  20. package/dist/resources/extensions/gsd/export.ts +81 -2
  21. package/dist/resources/extensions/gsd/files.ts +39 -9
  22. package/dist/resources/extensions/gsd/git-service.ts +6 -0
  23. package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
  24. package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
  25. package/dist/resources/extensions/gsd/history.ts +0 -1
  26. package/dist/resources/extensions/gsd/index.ts +277 -1
  27. package/dist/resources/extensions/gsd/md-importer.ts +526 -0
  28. package/dist/resources/extensions/gsd/metrics.ts +39 -3
  29. package/dist/resources/extensions/gsd/notifications.ts +0 -1
  30. package/dist/resources/extensions/gsd/post-unit-hooks.ts +70 -1
  31. package/dist/resources/extensions/gsd/preferences.ts +125 -150
  32. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
  33. package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
  34. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
  35. package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
  36. package/dist/resources/extensions/gsd/prompts/system.md +2 -1
  37. package/dist/resources/extensions/gsd/quick.ts +156 -0
  38. package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
  39. package/dist/resources/extensions/gsd/skill-health.ts +417 -0
  40. package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
  41. package/dist/resources/extensions/gsd/state.ts +30 -0
  42. package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
  43. package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
  44. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  45. package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
  46. package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
  47. package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
  48. package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
  49. package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
  50. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
  51. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
  52. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
  53. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
  54. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
  55. package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
  56. package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
  57. package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
  58. package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
  59. package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
  60. package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
  61. package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
  62. package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
  63. package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
  64. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
  65. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
  66. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
  67. package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
  68. package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
  69. package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
  70. package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
  71. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +262 -1
  72. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
  73. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
  74. package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
  75. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
  76. package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
  77. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
  78. package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
  79. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +92 -0
  80. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
  81. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +228 -5
  82. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
  83. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
  84. package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
  85. package/dist/resources/extensions/gsd/types.ts +29 -0
  86. package/dist/resources/extensions/gsd/undo.ts +0 -1
  87. package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
  88. package/dist/resources/extensions/gsd/visualizer-data.ts +352 -1
  89. package/dist/resources/extensions/gsd/visualizer-overlay.ts +166 -22
  90. package/dist/resources/extensions/gsd/visualizer-views.ts +464 -2
  91. package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
  92. package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
  93. package/dist/resources/extensions/remote-questions/config.ts +4 -2
  94. package/dist/resources/extensions/remote-questions/discord-adapter.ts +2 -4
  95. package/dist/resources/extensions/remote-questions/format.ts +154 -8
  96. package/dist/resources/extensions/remote-questions/manager.ts +9 -7
  97. package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
  98. package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
  99. package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
  100. package/dist/resources/extensions/remote-questions/types.ts +2 -1
  101. package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
  102. package/dist/resources/extensions/voice/index.ts +4 -3
  103. package/package.json +1 -1
  104. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
  106. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  109. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
  111. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  112. package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
  113. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
  115. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
  117. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
  119. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
  120. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
  122. package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
  124. package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
  126. package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
  128. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
  130. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
  133. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
  136. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
  138. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
  141. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  142. package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
  143. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  144. package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
  145. package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
  146. package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
  147. package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
  148. package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
  149. package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
  150. package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
  151. package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
  152. package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
  153. package/src/resources/extensions/google-search/index.ts +164 -47
  154. package/src/resources/extensions/gsd/auto-prompts.ts +103 -24
  155. package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
  156. package/src/resources/extensions/gsd/auto.ts +424 -30
  157. package/src/resources/extensions/gsd/commands.ts +518 -36
  158. package/src/resources/extensions/gsd/context-budget.ts +243 -0
  159. package/src/resources/extensions/gsd/context-store.ts +195 -0
  160. package/src/resources/extensions/gsd/dashboard-overlay.ts +41 -3
  161. package/src/resources/extensions/gsd/db-writer.ts +341 -0
  162. package/src/resources/extensions/gsd/debug-logger.ts +178 -0
  163. package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
  164. package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
  165. package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
  166. package/src/resources/extensions/gsd/doctor.ts +283 -2
  167. package/src/resources/extensions/gsd/export.ts +81 -2
  168. package/src/resources/extensions/gsd/files.ts +39 -9
  169. package/src/resources/extensions/gsd/git-service.ts +6 -0
  170. package/src/resources/extensions/gsd/gsd-db.ts +752 -0
  171. package/src/resources/extensions/gsd/guided-flow.ts +26 -1
  172. package/src/resources/extensions/gsd/history.ts +0 -1
  173. package/src/resources/extensions/gsd/index.ts +277 -1
  174. package/src/resources/extensions/gsd/md-importer.ts +526 -0
  175. package/src/resources/extensions/gsd/metrics.ts +39 -3
  176. package/src/resources/extensions/gsd/notifications.ts +0 -1
  177. package/src/resources/extensions/gsd/post-unit-hooks.ts +70 -1
  178. package/src/resources/extensions/gsd/preferences.ts +125 -150
  179. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
  180. package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
  181. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
  182. package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
  183. package/src/resources/extensions/gsd/prompts/system.md +2 -1
  184. package/src/resources/extensions/gsd/quick.ts +156 -0
  185. package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
  186. package/src/resources/extensions/gsd/skill-health.ts +417 -0
  187. package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
  188. package/src/resources/extensions/gsd/state.ts +30 -0
  189. package/src/resources/extensions/gsd/templates/preferences.md +1 -0
  190. package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
  191. package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  192. package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
  193. package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
  194. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
  195. package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
  196. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
  197. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
  198. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
  199. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
  200. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
  201. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
  202. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
  203. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
  204. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
  205. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
  206. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
  207. package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
  208. package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
  209. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
  210. package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
  211. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
  212. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
  213. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
  214. package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
  215. package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
  216. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
  217. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
  218. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +262 -1
  219. package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
  220. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
  221. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
  222. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
  223. package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
  224. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
  225. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
  226. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +92 -0
  227. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
  228. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +228 -5
  229. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
  230. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
  231. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
  232. package/src/resources/extensions/gsd/types.ts +29 -0
  233. package/src/resources/extensions/gsd/undo.ts +0 -1
  234. package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
  235. package/src/resources/extensions/gsd/visualizer-data.ts +352 -1
  236. package/src/resources/extensions/gsd/visualizer-overlay.ts +166 -22
  237. package/src/resources/extensions/gsd/visualizer-views.ts +464 -2
  238. package/src/resources/extensions/gsd/worktree-command.ts +18 -0
  239. package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
  240. package/src/resources/extensions/remote-questions/config.ts +4 -2
  241. package/src/resources/extensions/remote-questions/discord-adapter.ts +2 -4
  242. package/src/resources/extensions/remote-questions/format.ts +154 -8
  243. package/src/resources/extensions/remote-questions/manager.ts +9 -7
  244. package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
  245. package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
  246. package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
  247. package/src/resources/extensions/remote-questions/types.ts +2 -1
  248. package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
  249. package/src/resources/extensions/voice/index.ts +4 -3
@@ -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();
@@ -35,12 +35,38 @@ assertTrue(
35
35
  "exports VisualizerTask interface",
36
36
  );
37
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
+
38
59
  // Function export
39
60
  assertTrue(
40
61
  dataSrc.includes("export async function loadVisualizerData"),
41
62
  "exports loadVisualizerData function",
42
63
  );
43
64
 
65
+ assertTrue(
66
+ dataSrc.includes("export function computeCriticalPath"),
67
+ "exports computeCriticalPath function",
68
+ );
69
+
44
70
  // Data source usage
45
71
  assertTrue(
46
72
  dataSrc.includes("deriveState"),
@@ -62,6 +88,11 @@ assertTrue(
62
88
  "uses parsePlan for plan parsing",
63
89
  );
64
90
 
91
+ assertTrue(
92
+ dataSrc.includes("parseSummary"),
93
+ "uses parseSummary for changelog parsing",
94
+ );
95
+
65
96
  assertTrue(
66
97
  dataSrc.includes("getLedger"),
67
98
  "uses getLedger for in-memory metrics",
@@ -113,6 +144,27 @@ assertTrue(
113
144
  "VisualizerData has units array",
114
145
  );
115
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
+
116
168
  // Verify overlay source exists and imports data module
117
169
  const overlayPath = join(__dirname, "..", "visualizer-overlay.ts");
118
170
  const overlaySrc = readFileSync(overlayPath, "utf-8");
@@ -149,6 +201,21 @@ assertTrue(
149
201
  "overlay delegates to renderTimelineView",
150
202
  );
151
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
+
152
219
  assertTrue(
153
220
  overlaySrc.includes("handleInput"),
154
221
  "overlay has handleInput method",
@@ -174,6 +241,31 @@ assertTrue(
174
241
  "overlay tracks per-tab scroll offsets",
175
242
  );
176
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
+
177
269
  // Verify commands.ts integration
178
270
  const commandsPath = join(__dirname, "..", "commands.ts");
179
271
  const commandsSrc = readFileSync(commandsPath, "utf-8");
@@ -0,0 +1,120 @@
1
+ // Tests for GSD visualizer overlay.
2
+ // Verifies filter mode, tab switching, and export key handling.
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, assertEq, report } = createTestContext();
11
+
12
+ const overlaySrc = readFileSync(join(__dirname, "..", "visualizer-overlay.ts"), "utf-8");
13
+
14
+ console.log("\n=== Overlay: Tab Configuration ===");
15
+
16
+ assertTrue(
17
+ overlaySrc.includes("TAB_COUNT = 7"),
18
+ "TAB_COUNT is 7",
19
+ );
20
+
21
+ assertTrue(
22
+ overlaySrc.includes('"1 Progress"'),
23
+ "has Progress tab label",
24
+ );
25
+
26
+ assertTrue(
27
+ overlaySrc.includes('"5 Agent"'),
28
+ "has Agent tab label",
29
+ );
30
+
31
+ assertTrue(
32
+ overlaySrc.includes('"6 Changes"'),
33
+ "has Changes tab label",
34
+ );
35
+
36
+ assertTrue(
37
+ overlaySrc.includes('"7 Export"'),
38
+ "has Export tab label",
39
+ );
40
+
41
+ console.log("\n=== Overlay: Filter Mode ===");
42
+
43
+ assertTrue(
44
+ overlaySrc.includes('filterMode = false'),
45
+ "filterMode initialized to false",
46
+ );
47
+
48
+ assertTrue(
49
+ overlaySrc.includes('filterText = ""'),
50
+ "filterText initialized to empty string",
51
+ );
52
+
53
+ assertTrue(
54
+ overlaySrc.includes('filterField:'),
55
+ "has filterField state",
56
+ );
57
+
58
+ // Filter mode entry via "/"
59
+ assertTrue(
60
+ overlaySrc.includes('data === "/"') || overlaySrc.includes("data === '/'"),
61
+ "/ key enters filter mode",
62
+ );
63
+
64
+ // Filter field cycling via "f"
65
+ assertTrue(
66
+ overlaySrc.includes('data === "f"') || overlaySrc.includes("data === 'f'"),
67
+ "f key cycles filter field",
68
+ );
69
+
70
+ console.log("\n=== Overlay: Tab Switching ===");
71
+
72
+ // Supports 1-7 keys
73
+ assertTrue(
74
+ overlaySrc.includes('"1234567"'),
75
+ "supports keys 1-7 for tab switching",
76
+ );
77
+
78
+ // Tab wraps with TAB_COUNT
79
+ assertTrue(
80
+ overlaySrc.includes("% TAB_COUNT"),
81
+ "tab key wraps around TAB_COUNT",
82
+ );
83
+
84
+ console.log("\n=== Overlay: Export Key Interception ===");
85
+
86
+ assertTrue(
87
+ overlaySrc.includes("activeTab === 6"),
88
+ "export key handling checks for tab 7 (index 6)",
89
+ );
90
+
91
+ assertTrue(
92
+ overlaySrc.includes('handleExportKey'),
93
+ "has handleExportKey method",
94
+ );
95
+
96
+ assertTrue(
97
+ overlaySrc.includes('"m"') && overlaySrc.includes('"j"') && overlaySrc.includes('"s"'),
98
+ "handles m, j, s keys for export",
99
+ );
100
+
101
+ console.log("\n=== Overlay: Footer ===");
102
+
103
+ assertTrue(
104
+ overlaySrc.includes("Tab/1-7"),
105
+ "footer hint shows 1-7 tab range",
106
+ );
107
+
108
+ assertTrue(
109
+ overlaySrc.includes("/ filter"),
110
+ "footer hint mentions filter",
111
+ );
112
+
113
+ console.log("\n=== Overlay: Scroll Offsets ===");
114
+
115
+ assertTrue(
116
+ overlaySrc.includes(`new Array(TAB_COUNT).fill(0)`),
117
+ "scroll offsets sized to TAB_COUNT",
118
+ );
119
+
120
+ report();