gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6

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 (368) hide show
  1. package/dist/headless.js +25 -4
  2. package/dist/resource-loader.d.ts +40 -0
  3. package/dist/resource-loader.js +32 -13
  4. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  5. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  6. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  7. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  8. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  9. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +5 -18
  11. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
  14. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  15. package/dist/resources/extensions/gsd/auto-start.js +75 -24
  16. package/dist/resources/extensions/gsd/auto.js +34 -0
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  18. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
  19. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  20. package/dist/resources/extensions/gsd/component-types.js +69 -0
  21. package/dist/resources/extensions/gsd/context-store.js +23 -7
  22. package/dist/resources/extensions/gsd/detection.js +49 -1
  23. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  24. package/dist/resources/extensions/gsd/forensics.js +106 -0
  25. package/dist/resources/extensions/gsd/gsd-db.js +1 -1
  26. package/dist/resources/extensions/gsd/guided-flow.js +2 -4
  27. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  28. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  29. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  30. package/dist/resources/extensions/gsd/model-router.js +6 -0
  31. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  32. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  33. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  34. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  35. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  36. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  37. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  38. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  39. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  40. package/dist/resources/extensions/gsd/unit-context-manifest.js +334 -0
  41. package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
  42. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  43. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  44. package/dist/resources/extensions/mcp-client/index.js +3 -1
  45. package/dist/resources/extensions/ollama/index.js +5 -1
  46. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  47. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  48. package/dist/web/standalone/.next/BUILD_ID +1 -1
  49. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  50. package/dist/web/standalone/.next/build-manifest.json +2 -2
  51. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  76. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  77. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  79. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  80. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  81. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  82. package/package.json +1 -3
  83. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  84. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  85. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  86. package/packages/native/package.json +1 -1
  87. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  88. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  89. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  90. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  91. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  92. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  93. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  94. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  95. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  96. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  98. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  99. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  100. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  101. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  102. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  103. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  104. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  105. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  106. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  107. package/packages/pi-ai/dist/models.test.js +29 -11
  108. package/packages/pi-ai/dist/models.test.js.map +1 -1
  109. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  110. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  111. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  112. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  113. package/packages/pi-ai/src/models.test.ts +39 -11
  114. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  115. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  116. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  118. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  120. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  122. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  124. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  126. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  128. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  130. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  133. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  135. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  137. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  139. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  140. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  141. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  143. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/index.js +1 -0
  147. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  156. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  157. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  158. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  159. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  160. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  161. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  163. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  164. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  165. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  166. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  167. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  168. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  169. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  170. package/packages/pi-coding-agent/src/index.ts +1 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  172. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  173. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  174. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  175. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  176. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  177. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  178. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  179. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  180. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  181. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  182. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  183. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  184. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  185. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  186. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  187. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  188. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  189. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  190. package/packages/pi-tui/dist/components/image.test.js +6 -5
  191. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  192. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  193. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  194. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  195. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  196. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  197. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  198. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  199. package/packages/pi-tui/src/components/image.test.ts +10 -5
  200. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  201. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  202. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  203. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  204. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  205. package/scripts/install.js +15 -1
  206. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  207. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  208. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  209. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  210. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  211. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  212. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -72
  213. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  214. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  215. package/src/resources/extensions/gsd/auto/phases.ts +6 -17
  216. package/src/resources/extensions/gsd/auto/session.ts +7 -0
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
  218. package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
  219. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  220. package/src/resources/extensions/gsd/auto-start.ts +97 -4
  221. package/src/resources/extensions/gsd/auto.ts +37 -0
  222. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  223. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
  224. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  225. package/src/resources/extensions/gsd/component-types.ts +362 -0
  226. package/src/resources/extensions/gsd/context-store.ts +25 -8
  227. package/src/resources/extensions/gsd/detection.ts +58 -1
  228. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  229. package/src/resources/extensions/gsd/forensics.ts +118 -1
  230. package/src/resources/extensions/gsd/git-service.ts +16 -0
  231. package/src/resources/extensions/gsd/gsd-db.ts +1 -1
  232. package/src/resources/extensions/gsd/guided-flow.ts +2 -4
  233. package/src/resources/extensions/gsd/journal.ts +11 -1
  234. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  235. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  236. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  237. package/src/resources/extensions/gsd/model-router.ts +6 -0
  238. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  239. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  240. package/src/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  241. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  242. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  243. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  244. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  245. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
  247. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  248. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  249. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  250. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  251. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  252. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  253. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  254. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  255. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  256. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  257. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  258. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  259. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  260. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  261. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -4
  262. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  263. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  264. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  265. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  266. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  267. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  268. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  269. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  270. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  271. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  272. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  273. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  274. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  275. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  276. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1 -1
  277. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  278. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  279. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  280. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  281. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  282. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  283. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  284. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  285. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  287. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  288. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  289. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  290. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  291. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  292. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  294. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +22 -16
  295. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  296. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  297. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  298. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  299. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  300. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  301. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  303. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  304. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  305. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  306. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  307. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  308. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  309. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  310. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  311. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  312. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  313. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  314. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  315. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  316. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  317. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  318. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  319. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  320. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -0
  321. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  322. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  323. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  324. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  325. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  326. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  327. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  328. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  329. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  330. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  331. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  332. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  333. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  334. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  335. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  336. package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
  337. package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
  338. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  339. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  340. package/src/resources/extensions/mcp-client/index.ts +3 -1
  341. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  342. package/src/resources/extensions/ollama/index.ts +5 -1
  343. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  344. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  345. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  346. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  347. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  348. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  349. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  350. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  351. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  352. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  353. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  354. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  355. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  356. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  357. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  358. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  359. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  360. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  361. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  362. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  363. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  364. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  365. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  366. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  367. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
  368. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
@@ -1,294 +1,359 @@
1
- // Tests for GSD visualizer overlay.
2
- // Verifies filter mode, tab switching, mouse support, page scroll, help overlay, and 10-tab config.
3
-
4
- import { readFileSync } from "node:fs";
5
- import { join, dirname } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import { test } from 'node:test';
8
- import assert from 'node:assert/strict';
9
-
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
-
12
- const overlaySrc = readFileSync(join(__dirname, "..", "visualizer-overlay.ts"), "utf-8");
13
-
14
- console.log("\n=== Overlay: Tab Configuration ===");
15
-
16
- assert.ok(
17
- overlaySrc.includes("TAB_COUNT = 10"),
18
- "TAB_COUNT is 10",
19
- );
20
-
21
- assert.ok(
22
- overlaySrc.includes('"1 Progress"'),
23
- "has Progress tab label",
24
- );
25
-
26
- assert.ok(
27
- overlaySrc.includes('"2 Timeline"'),
28
- "has Timeline tab label",
29
- );
30
-
31
- assert.ok(
32
- overlaySrc.includes('"3 Deps"'),
33
- "has Deps tab label",
34
- );
35
-
36
- assert.ok(
37
- overlaySrc.includes('"5 Health"'),
38
- "has Health tab label",
39
- );
40
-
41
- assert.ok(
42
- overlaySrc.includes('"6 Agent"'),
43
- "has Agent tab label",
44
- );
45
-
46
- assert.ok(
47
- overlaySrc.includes('"7 Changes"'),
48
- "has Changes tab label",
49
- );
50
-
51
- assert.ok(
52
- overlaySrc.includes('"8 Knowledge"'),
53
- "has Knowledge tab label",
54
- );
55
-
56
- assert.ok(
57
- overlaySrc.includes('"9 Captures"'),
58
- "has Captures tab label",
59
- );
60
-
61
- assert.ok(
62
- overlaySrc.includes('"0 Export"'),
63
- "has Export tab label",
64
- );
65
-
66
- console.log("\n=== Overlay: Filter Mode ===");
67
-
68
- assert.ok(
69
- overlaySrc.includes('filterMode = false'),
70
- "filterMode initialized to false",
71
- );
72
-
73
- assert.ok(
74
- overlaySrc.includes('filterText = ""'),
75
- "filterText initialized to empty string",
76
- );
77
-
78
- assert.ok(
79
- overlaySrc.includes('filterField:'),
80
- "has filterField state",
81
- );
82
-
83
- // Filter mode entry via "/"
84
- assert.ok(
85
- overlaySrc.includes('data === "/"') || overlaySrc.includes("data === '/'"),
86
- "/ key enters filter mode",
87
- );
88
-
89
- // Filter field cycling via "f"
90
- assert.ok(
91
- overlaySrc.includes('data === "f"') || overlaySrc.includes("data === 'f'"),
92
- "f key cycles filter field",
93
- );
94
-
95
- console.log("\n=== Overlay: Tab Switching ===");
96
-
97
- // Supports 1-9,0 keys
98
- assert.ok(
99
- overlaySrc.includes('"1234567890"'),
100
- "supports keys 1-9,0 for tab switching",
101
- );
102
-
103
- // Tab wraps with TAB_COUNT
104
- assert.ok(
105
- overlaySrc.includes("% TAB_COUNT"),
106
- "tab key wraps around TAB_COUNT",
107
- );
108
-
109
- assert.ok(
110
- overlaySrc.includes('Key.shift("tab")') || overlaySrc.includes("Key.shift('tab')"),
111
- "supports Shift+Tab for reverse tab switching",
112
- );
113
-
114
- console.log("\n=== Overlay: Page/Half-Page Scroll ===");
115
-
116
- assert.ok(
117
- overlaySrc.includes("Key.pageUp"),
118
- "has Key.pageUp handler",
119
- );
120
-
121
- assert.ok(
122
- overlaySrc.includes("Key.pageDown"),
123
- "has Key.pageDown handler",
124
- );
125
-
126
- assert.ok(
127
- overlaySrc.includes('Key.ctrl("u")'),
128
- "has Ctrl+U half-page scroll",
129
- );
130
-
131
- assert.ok(
132
- overlaySrc.includes('Key.ctrl("d")'),
133
- "has Ctrl+D half-page scroll",
134
- );
135
-
136
- console.log("\n=== Overlay: Mouse Support ===");
137
-
138
- assert.ok(
139
- overlaySrc.includes("parseSGRMouse"),
140
- "has parseSGRMouse method",
141
- );
142
-
143
- assert.ok(
144
- overlaySrc.includes("?1003h"),
145
- "enables mouse tracking in constructor",
146
- );
147
-
148
- assert.ok(
149
- overlaySrc.includes("?1003l"),
150
- "disables mouse tracking in dispose",
151
- );
152
-
153
- console.log("\n=== Overlay: Collapsible Milestones ===");
154
-
155
- assert.ok(
156
- overlaySrc.includes("collapsedMilestones"),
157
- "has collapsedMilestones state",
158
- );
159
-
160
- console.log("\n=== Overlay: Help Overlay ===");
161
-
162
- assert.ok(
163
- overlaySrc.includes("showHelp"),
164
- "has showHelp state",
165
- );
166
-
167
- assert.ok(
168
- overlaySrc.includes('data === "?"'),
169
- "? key toggles help",
170
- );
171
-
172
- console.log("\n=== Overlay: Export Key Interception ===");
173
-
174
- assert.ok(
175
- overlaySrc.includes("activeTab === 9"),
176
- "export key handling checks for tab 0 (index 9)",
177
- );
178
-
179
- assert.ok(
180
- overlaySrc.includes('handleExportKey'),
181
- "has handleExportKey method",
182
- );
183
-
184
- assert.ok(
185
- overlaySrc.includes('"m"') && overlaySrc.includes('"j"') && overlaySrc.includes('"s"'),
186
- "handles m, j, s keys for export",
187
- );
188
-
189
- console.log("\n=== Overlay: Footer ===");
190
-
191
- assert.ok(
192
- overlaySrc.includes("1-9,0"),
193
- "footer hint shows 1-9,0 tab range",
194
- );
195
-
196
- assert.ok(
197
- overlaySrc.includes("PgUp/PgDn"),
198
- "footer hint mentions PgUp/PgDn",
199
- );
200
-
201
- assert.ok(
202
- overlaySrc.includes("? help"),
203
- "footer hint mentions ? for help",
204
- );
205
-
206
- console.log("\n=== Overlay: Scroll Offsets ===");
207
-
208
- assert.ok(
209
- overlaySrc.includes(`new Array(TAB_COUNT).fill(0)`),
210
- "scroll offsets sized to TAB_COUNT",
211
- );
212
-
213
- console.log("\n=== Overlay: Terminal Resize Handling ===");
214
-
215
- assert.ok(
216
- overlaySrc.includes('resizeHandler'),
217
- "has resizeHandler property",
218
- );
219
-
220
- assert.ok(
221
- overlaySrc.includes('"resize"'),
222
- "listens for resize events",
223
- );
224
-
225
- assert.ok(
226
- overlaySrc.includes('removeListener("resize"'),
227
- "removes resize listener on dispose",
228
- );
229
-
230
- console.log("\n=== Overlay: Shared Imports ===");
231
-
232
- assert.ok(
233
- overlaySrc.includes('from "../shared/mod.js"'),
234
- "imports from shared barrel",
235
- );
236
-
237
- test("visualizer overlay closes on escape in filter and help submodes", async () => {
238
- const mod = await import("../visualizer-overlay.js");
239
-
240
- const mockTui = { requestRender: () => {} };
241
- const mockTheme = {
242
- fg: (_color: string, text: string) => text,
243
- bold: (text: string) => text,
1
+ /**
2
+ * GSD visualizer overlay behaviour tests.
3
+ *
4
+ * These tests drive the overlay through its public surface:
5
+ * - construct an instance
6
+ * - feed input events (keystrokes, SGR mouse sequences)
7
+ * - observe the rendered line array and mutable state
8
+ *
9
+ * The previous version of this file was 39 `overlaySrc.includes(...)`
10
+ * assertions — a pure source-grep. Replaced with behaviour-driven tests.
11
+ */
12
+
13
+ import { test } from "node:test";
14
+ import assert from "node:assert/strict";
15
+
16
+ import { GSDVisualizerOverlay } from "../visualizer-overlay.ts";
17
+
18
+ function makeTui() {
19
+ const renders: number[] = [];
20
+ return {
21
+ renders,
22
+ tui: {
23
+ requestRender: () => {
24
+ renders.push(Date.now());
25
+ },
26
+ },
244
27
  };
28
+ }
29
+
30
+ const mockTheme = {
31
+ fg: (_color: string, text: string) => text,
32
+ bold: (text: string) => text,
33
+ } as any;
34
+
35
+ function makeOverlay(t: { after: (fn: () => void) => void }, onClose: () => void = () => {}) {
36
+ const { tui, renders } = makeTui();
37
+ const overlay = new GSDVisualizerOverlay(tui as any, mockTheme, onClose);
38
+ t.after(() => overlay.dispose());
39
+ return { overlay, renders };
40
+ }
41
+
42
+ function stripAnsi(s: string): string {
43
+ return s.replace(/\x1b\[[0-9;]*m/g, "");
44
+ }
45
+
46
+ // ─── Tab bar rendering ───────────────────────────────────────────────────
47
+
48
+ test("overlay renders 10 tabs (Progress, Timeline, Deps, Metrics, Health, Agent, Changes, Knowledge, Captures, Export)", (t) => {
49
+ const { overlay } = makeOverlay(t);
50
+ overlay.loading = true; // body shows loading text, but tab bar renders regardless
51
+
52
+ // Use a very wide terminal so the tab bar is not truncated.
53
+ const lines = overlay.render(200).map(stripAnsi);
54
+ const tabBar = lines.find((l) => l.includes("Progress") && l.includes("Export"));
55
+ assert.ok(tabBar, `expected a tab-bar line containing all labels, got:\n${lines.slice(0, 5).join("\n")}`);
56
+ for (const label of ["Progress", "Timeline", "Deps", "Metrics", "Health", "Agent", "Changes", "Knowledge", "Captures", "Export"]) {
57
+ assert.ok(tabBar!.includes(label), `tab bar missing label ${label}`);
58
+ }
59
+ });
60
+
61
+ test("overlay shows a Captures badge count when pending captures are present", (t) => {
62
+ const { overlay } = makeOverlay(t);
63
+ overlay.loading = true;
64
+ overlay.data = { captures: { pendingCount: 7 } } as any;
65
+
66
+ const lines = overlay.render(120).map(stripAnsi);
67
+ const tabBar = lines.find((l) => l.includes("Captures"))!;
68
+ assert.ok(tabBar.includes("(7)"), `captures tab should carry (7) badge, got: ${tabBar}`);
69
+ });
70
+
71
+ // ─── Tab switching via digit keys ────────────────────────────────────────
72
+
73
+ test("overlay switches tabs via 1–9,0 digit keys", (t) => {
74
+ const { overlay } = makeOverlay(t);
75
+
76
+ const cases: Array<[string, number]> = [
77
+ ["1", 0],
78
+ ["2", 1],
79
+ ["3", 2],
80
+ ["4", 3],
81
+ ["5", 4],
82
+ ["6", 5],
83
+ ["7", 6],
84
+ ["8", 7],
85
+ ["9", 8],
86
+ ["0", 9],
87
+ ];
88
+ for (const [key, expected] of cases) {
89
+ overlay.handleInput(key);
90
+ assert.equal(overlay.activeTab, expected, `key ${key} should select tab ${expected}`);
91
+ }
92
+ });
93
+
94
+ test("overlay Tab key cycles forward and wraps around at TAB_COUNT", (t) => {
95
+ const { overlay } = makeOverlay(t);
96
+ overlay.activeTab = 9;
97
+ overlay.handleInput("\t");
98
+ assert.equal(overlay.activeTab, 0, "Tab wraps from 9 back to 0");
99
+ });
100
+
101
+ test("overlay Shift+Tab cycles backward and wraps", (t) => {
102
+ const { overlay } = makeOverlay(t);
103
+ overlay.activeTab = 0;
104
+ overlay.handleInput("\u001b[Z");
105
+ assert.equal(overlay.activeTab, 9, "Shift+Tab wraps from 0 to 9");
106
+ });
107
+
108
+ // ─── Filter mode ─────────────────────────────────────────────────────────
109
+
110
+ test("overlay / enters filter mode and typed characters accumulate in filterText", (t) => {
111
+ const { overlay } = makeOverlay(t);
112
+ assert.equal(overlay.filterMode, false, "starts out of filter mode");
113
+ assert.equal(overlay.filterText, "", "starts with empty filter text");
114
+
115
+ overlay.handleInput("/");
116
+ assert.equal(overlay.filterMode, true, "/ enters filter mode");
117
+
118
+ overlay.handleInput("f");
119
+ overlay.handleInput("o");
120
+ overlay.handleInput("o");
121
+ assert.equal(overlay.filterText, "foo");
122
+
123
+ overlay.handleInput("\r"); // enter — exits filter mode
124
+ assert.equal(overlay.filterMode, false, "Enter exits filter mode");
125
+ assert.equal(overlay.filterText, "foo", "filter text persists after exit");
126
+ });
127
+
128
+ test("overlay filter backspace deletes last character", (t) => {
129
+ const { overlay } = makeOverlay(t);
130
+ overlay.handleInput("/");
131
+ overlay.handleInput("a");
132
+ overlay.handleInput("b");
133
+ overlay.handleInput("c");
134
+ overlay.handleInput("\u007f"); // backspace
135
+ assert.equal(overlay.filterText, "ab");
136
+ });
137
+
138
+ test("overlay f key cycles filter field on Progress tab: all → status → risk → keyword → all", (t) => {
139
+ const { overlay } = makeOverlay(t);
140
+ overlay.activeTab = 0;
141
+ assert.equal(overlay.filterField, "all");
142
+ overlay.handleInput("f");
143
+ assert.equal(overlay.filterField, "status");
144
+ overlay.handleInput("f");
145
+ assert.equal(overlay.filterField, "risk");
146
+ overlay.handleInput("f");
147
+ assert.equal(overlay.filterField, "keyword");
148
+ overlay.handleInput("f");
149
+ assert.equal(overlay.filterField, "all", "cycles back to all");
150
+ });
151
+
152
+ test("overlay f key on non-Progress tabs only toggles all ↔ keyword", (t) => {
153
+ const { overlay } = makeOverlay(t);
154
+ overlay.activeTab = 2; // Deps tab
155
+ overlay.filterField = "all";
156
+ overlay.handleInput("f");
157
+ assert.equal(overlay.filterField, "keyword");
158
+ overlay.handleInput("f");
159
+ assert.equal(overlay.filterField, "all");
160
+ });
161
+
162
+ // ─── Help overlay ────────────────────────────────────────────────────────
163
+
164
+ test("overlay ? key shows help, and pressing ? again dismisses it in-place", (t) => {
165
+ let closed = false;
166
+ const { overlay } = makeOverlay(t, () => {
167
+ closed = true;
168
+ });
169
+
170
+ overlay.handleInput("?");
171
+ assert.equal(overlay.showHelp, true, "? toggles help on");
172
+
173
+ overlay.handleInput("?");
174
+ assert.equal(overlay.showHelp, false, "? pressed again dismisses help");
175
+ assert.equal(closed, false, "toggling help does not close the overlay");
176
+ });
177
+
178
+ // ─── Escape / Ctrl+C close behaviour ─────────────────────────────────────
179
+
180
+ test("overlay Escape closes when no sub-mode is active", (t) => {
181
+ let closed = false;
182
+ const { overlay } = makeOverlay(t, () => {
183
+ closed = true;
184
+ });
185
+ overlay.handleInput("\u001b");
186
+ assert.equal(closed, true);
187
+ });
188
+
189
+ test("overlay Ctrl+C closes even while filter mode is active", (t) => {
190
+ let closed = false;
191
+ const { overlay } = makeOverlay(t, () => {
192
+ closed = true;
193
+ });
194
+ overlay.filterMode = true;
195
+ overlay.handleInput("\u0003"); // Ctrl+C
196
+ assert.equal(closed, true, "Ctrl+C must close even in filter mode");
197
+ });
198
+
199
+ test("overlay Escape closes even when help is visible (top-level Escape short-circuits)", (t) => {
200
+ let closed = false;
201
+ const { overlay } = makeOverlay(t, () => {
202
+ closed = true;
203
+ });
204
+ overlay.showHelp = true;
205
+ overlay.handleInput("\u001b");
206
+ assert.equal(closed, true, "Escape calls onClose regardless of sub-mode");
207
+ });
208
+
209
+ // ─── Scroll behaviour ────────────────────────────────────────────────────
245
210
 
246
- let closedFilter = false;
247
- const filterOverlay = new mod.GSDVisualizerOverlay(
248
- mockTui,
249
- mockTheme as any,
250
- () => { closedFilter = true; },
211
+ test("overlay j / Down scrolls one line forward, k / Up scrolls back", (t) => {
212
+ const { overlay } = makeOverlay(t);
213
+ overlay.activeTab = 1;
214
+ overlay.scrollOffsets[1] = 5;
215
+
216
+ overlay.handleInput("j");
217
+ assert.equal(overlay.scrollOffsets[1], 6);
218
+
219
+ overlay.handleInput("k");
220
+ assert.equal(overlay.scrollOffsets[1], 5);
221
+
222
+ overlay.handleInput("\u001b[B"); // Down arrow
223
+ assert.equal(overlay.scrollOffsets[1], 6);
224
+
225
+ overlay.handleInput("\u001b[A"); // Up arrow
226
+ assert.equal(overlay.scrollOffsets[1], 5);
227
+ });
228
+
229
+ test("overlay k / Up does not scroll below zero", (t) => {
230
+ const { overlay } = makeOverlay(t);
231
+ overlay.scrollOffsets[0] = 0;
232
+ overlay.handleInput("k");
233
+ assert.equal(overlay.scrollOffsets[0], 0);
234
+ });
235
+
236
+ test("overlay g jumps to top, G jumps to bottom-sentinel", (t) => {
237
+ const { overlay } = makeOverlay(t);
238
+ overlay.scrollOffsets[0] = 50;
239
+ overlay.handleInput("g");
240
+ assert.equal(overlay.scrollOffsets[0], 0);
241
+ overlay.handleInput("G");
242
+ assert.ok(overlay.scrollOffsets[0] >= 100, "G sets a large offset sentinel");
243
+ });
244
+
245
+ test("overlay Ctrl+U / Ctrl+D scroll half-page", (t) => {
246
+ const { overlay } = makeOverlay(t);
247
+ overlay.scrollOffsets[0] = 100;
248
+ overlay.handleInput("\u0004"); // Ctrl+D
249
+ assert.ok(
250
+ overlay.scrollOffsets[0] > 100,
251
+ `Ctrl+D should move forward, got ${overlay.scrollOffsets[0]}`,
251
252
  );
252
- filterOverlay.filterMode = true;
253
- filterOverlay.handleInput("\u0003");
254
- assert.equal(closedFilter, true, "Ctrl+C closes while filter mode is active");
255
- filterOverlay.dispose();
256
-
257
- let closedHelp = false;
258
- const helpOverlay = new mod.GSDVisualizerOverlay(
259
- mockTui,
260
- mockTheme as any,
261
- () => { closedHelp = true; },
253
+ const afterDown = overlay.scrollOffsets[0];
254
+ overlay.handleInput("\u0015"); // Ctrl+U
255
+ assert.ok(
256
+ overlay.scrollOffsets[0] < afterDown,
257
+ "Ctrl+U should move backward",
262
258
  );
263
- helpOverlay.showHelp = true;
264
- helpOverlay.handleInput("\u001b");
265
- assert.equal(closedHelp, true, "Escape closes while help overlay is visible");
266
- helpOverlay.dispose();
267
259
  });
268
260
 
269
- test("visualizer overlay tab hitboxes include rendered badges", async () => {
270
- const mod = await import("../visualizer-overlay.js");
261
+ test("overlay PageUp / PageDown scroll one viewport", (t) => {
262
+ const { overlay } = makeOverlay(t);
263
+ overlay.scrollOffsets[0] = 50;
264
+ overlay.handleInput("\u001b[6~"); // Page Down
265
+ assert.ok(overlay.scrollOffsets[0] > 50);
266
+ const after = overlay.scrollOffsets[0];
267
+ overlay.handleInput("\u001b[5~"); // Page Up
268
+ assert.ok(overlay.scrollOffsets[0] < after);
269
+ });
271
270
 
272
- const mockTui = { requestRender: () => {} };
273
- const mockTheme = {
274
- fg: (_color: string, text: string) => text,
275
- bold: (text: string) => text,
276
- };
271
+ // ─── Mouse wheel + click ─────────────────────────────────────────────────
272
+
273
+ test("overlay SGR mouse wheel up/down scrolls active tab", (t) => {
274
+ const { overlay } = makeOverlay(t);
275
+ overlay.scrollOffsets[0] = 20;
276
+ // SGR mouse wheel down: button 65
277
+ overlay.handleInput("\u001b[<65;10;10M");
278
+ assert.equal(overlay.scrollOffsets[0], 23, "wheel-down scrolls +3");
279
+ // Wheel up: button 64
280
+ overlay.handleInput("\u001b[<64;10;10M");
281
+ assert.equal(overlay.scrollOffsets[0], 20, "wheel-up scrolls −3");
282
+ });
277
283
 
278
- const overlay = new mod.GSDVisualizerOverlay(
279
- mockTui,
280
- mockTheme as any,
281
- () => {},
282
- );
284
+ test("overlay click on Captures tab badge selects that tab", (t) => {
285
+ const { overlay } = makeOverlay(t);
283
286
  overlay.loading = true;
284
287
  overlay.data = { captures: { pendingCount: 3 } } as any;
285
288
 
286
289
  const lines = overlay.render(120);
287
- const tabLine = lines.find((line: string) => line.includes("Captures") && line.includes("(3)"));
290
+ const tabLine = lines.find((line) => line.includes("Captures") && line.includes("(3)"))!;
288
291
  assert.ok(tabLine, "rendered tab bar includes captures badge");
289
- const plain = tabLine!.replace(/\x1b\[[0-9;]*m/g, "");
292
+ const plain = stripAnsi(tabLine);
290
293
  const badgeColumn = plain.indexOf("(3)") + 2;
291
- overlay.handleInput(`\x1b[<0;${badgeColumn};2M`);
294
+ overlay.handleInput(`\u001b[<0;${badgeColumn};2M`);
292
295
  assert.equal(overlay.activeTab, 8, "clicking the badge area selects the captures tab");
296
+ });
297
+
298
+ // ─── Collapse state on Progress tab ──────────────────────────────────────
299
+
300
+ test("overlay exposes collapsedMilestones set for Progress-tab collapse tracking", (t) => {
301
+ const { overlay } = makeOverlay(t);
302
+ assert.ok(overlay.collapsedMilestones instanceof Set);
303
+ assert.equal(overlay.collapsedMilestones.size, 0);
304
+ });
305
+
306
+ // ─── Loading state rendering ─────────────────────────────────────────────
307
+
308
+ test("overlay renders a Loading… marker when no data is loaded yet", (t) => {
309
+ const { overlay } = makeOverlay(t);
310
+ overlay.loading = true;
311
+
312
+ const lines = overlay.render(120).map(stripAnsi);
313
+ assert.ok(
314
+ lines.some((l) => l.includes("Loading")),
315
+ "expected Loading… indicator in render output",
316
+ );
317
+ });
318
+
319
+ // ─── Footer hint ─────────────────────────────────────────────────────────
320
+
321
+ test("overlay footer hint mentions tab navigation, filter, scroll, and help", (t) => {
322
+ const { overlay } = makeOverlay(t);
323
+ overlay.loading = true;
324
+
325
+ const lines = overlay.render(120).map(stripAnsi).join("\n");
326
+ assert.ok(lines.includes("1-9,0"), "footer shows 1-9,0 tab range hint");
327
+ assert.ok(lines.includes("PgUp/PgDn") || lines.includes("PgUp"), "footer mentions PgUp/PgDn");
328
+ assert.ok(lines.includes("? help"), "footer mentions ? help");
329
+ assert.ok(lines.includes("/"), "footer mentions / for filter");
330
+ });
331
+
332
+ // ─── Scroll offsets array is sized to TAB_COUNT ──────────────────────────
333
+
334
+ test("overlay scrollOffsets array has one slot per tab (10 tabs total)", (t) => {
335
+ const { overlay } = makeOverlay(t);
336
+ assert.equal(overlay.scrollOffsets.length, 10, "scrollOffsets sized to TAB_COUNT=10");
337
+ assert.ok(overlay.scrollOffsets.every((n: number) => n === 0), "initialized to zero");
338
+ });
339
+
340
+ // ─── Dispose cleanup ─────────────────────────────────────────────────────
341
+
342
+ test("overlay dispose is idempotent and flips the disposed flag", (t) => {
343
+ const { tui } = makeTui();
344
+ const overlay = new GSDVisualizerOverlay(tui as any, mockTheme, () => {});
345
+ // Ensure constructor-owned resources are released even if an assertion
346
+ // below fails — dispose() is documented as idempotent, so a teardown
347
+ // call after the body's explicit dispose is a no-op.
348
+ t.after(() => {
349
+ try { overlay.dispose(); } catch { /* already disposed */ }
350
+ });
351
+
352
+ // Sanity: disposed flag flips
353
+ assert.equal(overlay.disposed, false);
354
+ overlay.dispose();
355
+ assert.equal(overlay.disposed, true);
356
+
357
+ // Calling dispose again is safe (no throw)
293
358
  overlay.dispose();
294
359
  });