gsd-pi 2.77.0-dev.eaa4973bc → 2.78.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 (655) hide show
  1. package/README.md +1 -1
  2. package/dist/claude-cli-check.js +5 -1
  3. package/dist/headless.js +49 -4
  4. package/dist/loader.js +0 -0
  5. package/dist/resource-loader.d.ts +40 -0
  6. package/dist/resource-loader.js +32 -13
  7. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  8. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  9. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  10. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  11. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  12. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  13. package/dist/resources/extensions/claude-code-cli/readiness.js +5 -1
  14. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  15. package/dist/resources/extensions/github-sync/templates.js +103 -0
  16. package/dist/resources/extensions/google-search/index.js +3 -2
  17. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  18. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  19. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  20. package/dist/resources/extensions/gsd/auto-dispatch.js +142 -29
  21. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  22. package/dist/resources/extensions/gsd/auto-post-unit.js +150 -64
  23. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  24. package/dist/resources/extensions/gsd/auto-recovery.js +197 -48
  25. package/dist/resources/extensions/gsd/auto-start.js +107 -29
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  27. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  28. package/dist/resources/extensions/gsd/auto.js +76 -21
  29. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  30. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  31. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  34. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  35. package/dist/resources/extensions/gsd/component-types.js +69 -0
  36. package/dist/resources/extensions/gsd/context-store.js +23 -7
  37. package/dist/resources/extensions/gsd/detection.js +49 -1
  38. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -17
  39. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  40. package/dist/resources/extensions/gsd/forensics.js +106 -0
  41. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  42. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  43. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  44. package/dist/resources/extensions/gsd/git-service.js +126 -2
  45. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  46. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  47. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  48. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  49. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  50. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  51. package/dist/resources/extensions/gsd/model-router.js +6 -0
  52. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  53. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  54. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  55. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  56. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  57. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  58. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  59. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  60. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  61. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  62. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  63. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  64. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  65. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  66. package/dist/resources/extensions/gsd/state.js +69 -58
  67. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  68. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  69. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  70. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  71. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  72. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  73. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  74. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  75. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  76. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  77. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  78. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  79. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +85 -8
  81. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  82. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  83. package/dist/resources/extensions/mcp-client/index.js +3 -1
  84. package/dist/resources/extensions/ollama/index.js +5 -1
  85. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  86. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  89. package/dist/web/standalone/.next/build-manifest.json +3 -3
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/required-server-files.json +3 -3
  92. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  93. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  103. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  131. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  151. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  161. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  167. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  181. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  183. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  187. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/index.html +1 -1
  197. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  198. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  199. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  200. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  201. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  202. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  203. package/dist/web/standalone/.next/server/app/page.js +2 -2
  204. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  205. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  206. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  207. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  208. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  209. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  210. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  211. package/dist/web/standalone/.next/server/middleware.js +2 -2
  212. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  214. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  215. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  216. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  217. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  218. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  219. package/dist/web/standalone/.next/static/chunks/app/page-151349214571e2b6.js +1 -0
  220. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  222. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  223. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  224. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  225. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  226. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  227. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  228. package/dist/web/standalone/server.js +1 -1
  229. package/package.json +2 -3
  230. package/packages/daemon/package.json +2 -2
  231. package/packages/daemon/src/logger.ts +4 -3
  232. package/packages/mcp-server/dist/server.d.ts +24 -0
  233. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  234. package/packages/mcp-server/dist/server.js +88 -87
  235. package/packages/mcp-server/dist/server.js.map +1 -1
  236. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  237. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  238. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  239. package/packages/mcp-server/package.json +2 -2
  240. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  241. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  242. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  243. package/packages/mcp-server/src/server.ts +131 -105
  244. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  245. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  246. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  247. package/packages/native/package.json +2 -2
  248. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  249. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  250. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  251. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  252. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  253. package/packages/pi-agent-core/package.json +1 -1
  254. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  255. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  256. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  257. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  258. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  259. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  260. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  261. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  262. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  263. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  264. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  265. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  266. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  267. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  268. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  269. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  270. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  271. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  272. package/packages/pi-ai/dist/models.test.js +36 -11
  273. package/packages/pi-ai/dist/models.test.js.map +1 -1
  274. package/packages/pi-ai/package.json +1 -1
  275. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  276. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  277. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  278. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  279. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  280. package/packages/pi-ai/src/models.test.ts +48 -11
  281. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  282. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  283. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  284. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  285. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  286. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  287. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  288. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  289. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  291. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  293. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  294. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  295. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  297. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  298. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  299. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  300. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  302. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  304. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  305. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  306. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  307. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  308. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  309. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  310. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  311. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  312. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  313. package/packages/pi-coding-agent/dist/index.js +1 -0
  314. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  315. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  316. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  317. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  318. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  319. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  320. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  321. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  322. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  323. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  324. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  325. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  326. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  327. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  328. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  329. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  331. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  333. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  335. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  336. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  337. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  338. package/packages/pi-coding-agent/package.json +1 -1
  339. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  340. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  341. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  342. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  343. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  344. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  345. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  346. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  347. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  348. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  349. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  350. package/packages/pi-coding-agent/src/index.ts +1 -0
  351. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  352. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  353. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  354. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  355. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  356. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  357. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  358. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  359. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  360. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  361. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  362. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  363. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  364. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +37 -11
  365. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  366. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  367. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  368. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  369. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  370. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  371. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  372. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  373. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  374. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  375. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  376. package/packages/pi-tui/dist/components/editor.js +19 -0
  377. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  378. package/packages/pi-tui/dist/components/image.test.js +6 -5
  379. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  380. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  381. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  382. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  383. package/packages/pi-tui/package.json +1 -1
  384. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  385. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  386. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +42 -11
  387. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  388. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  389. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  390. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  391. package/packages/pi-tui/src/components/editor.ts +22 -0
  392. package/packages/pi-tui/src/components/image.test.ts +10 -5
  393. package/packages/pi-tui/src/editor-component.ts +3 -0
  394. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  395. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  396. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  397. package/packages/rpc-client/package.json +1 -1
  398. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  399. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  400. package/pkg/package.json +1 -1
  401. package/scripts/install.js +15 -1
  402. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  403. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  404. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  405. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  406. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  407. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  408. package/src/resources/extensions/claude-code-cli/readiness.ts +5 -1
  409. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  410. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  411. package/src/resources/extensions/github-sync/templates.ts +151 -0
  412. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  413. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  414. package/src/resources/extensions/google-search/index.ts +3 -2
  415. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  416. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  417. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  418. package/src/resources/extensions/gsd/auto-dispatch.ts +156 -29
  419. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  420. package/src/resources/extensions/gsd/auto-post-unit.ts +163 -73
  421. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  422. package/src/resources/extensions/gsd/auto-recovery.ts +230 -51
  423. package/src/resources/extensions/gsd/auto-start.ts +127 -9
  424. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  425. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  426. package/src/resources/extensions/gsd/auto.ts +90 -23
  427. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  428. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  429. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  430. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  431. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  432. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  433. package/src/resources/extensions/gsd/component-types.ts +362 -0
  434. package/src/resources/extensions/gsd/context-store.ts +25 -8
  435. package/src/resources/extensions/gsd/detection.ts +58 -1
  436. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -20
  437. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  438. package/src/resources/extensions/gsd/forensics.ts +118 -1
  439. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  440. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  441. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  442. package/src/resources/extensions/gsd/git-service.ts +149 -2
  443. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  444. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  445. package/src/resources/extensions/gsd/journal.ts +11 -1
  446. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  447. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  448. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  449. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  450. package/src/resources/extensions/gsd/model-router.ts +6 -0
  451. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  452. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  453. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  454. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  455. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  456. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  457. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  458. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  459. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  460. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  461. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  462. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  463. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  464. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  465. package/src/resources/extensions/gsd/state.ts +76 -66
  466. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  467. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  468. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  469. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  470. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  471. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +133 -292
  472. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  473. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  474. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  475. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  476. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  477. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  478. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  479. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  480. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  481. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  482. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  483. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  484. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  485. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  486. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  487. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  488. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  489. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  490. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  491. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  492. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  493. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  494. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  495. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  496. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  497. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  498. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  499. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  500. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  501. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  502. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  503. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  504. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  505. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +5 -0
  506. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  507. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  508. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  509. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  510. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  511. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  512. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  513. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  514. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  515. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  516. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +14 -4
  517. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  518. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  519. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  520. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  521. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  522. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  523. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  524. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  525. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  526. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  527. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  528. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  529. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  530. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  531. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  532. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  533. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  534. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  535. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  536. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  537. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  538. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  539. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  540. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  541. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  542. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  543. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  544. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  545. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  546. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  547. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  548. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  549. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  550. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  551. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  552. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  553. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  554. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  555. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  556. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  557. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  558. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  559. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  560. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  561. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  562. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  563. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  564. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  565. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  566. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  567. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  568. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  569. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  570. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  571. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  572. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  573. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  574. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  575. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  576. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  577. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  578. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  579. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  580. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  581. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  582. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  583. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  584. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  585. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  586. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  587. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  588. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  589. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  590. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  591. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  592. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -0
  593. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  594. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  595. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  596. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  597. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  598. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  599. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  600. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  601. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  602. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  603. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  604. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  605. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  606. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  607. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  608. package/src/resources/extensions/gsd/types.ts +3 -3
  609. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  610. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  611. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  612. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  613. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  614. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  615. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  616. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  617. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  618. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  619. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  620. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  621. package/src/resources/extensions/gsd/worktree-manager.ts +108 -7
  622. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  623. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  624. package/src/resources/extensions/mcp-client/index.ts +3 -1
  625. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  626. package/src/resources/extensions/ollama/index.ts +5 -1
  627. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  628. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  629. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  630. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  631. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  632. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  633. package/dist/web/standalone/.next/static/chunks/app/page-5b113fd32bc2a1c3.js +0 -1
  634. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  635. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  636. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  637. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  638. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  639. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  640. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  641. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  642. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  643. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  644. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  645. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  646. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  647. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  648. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  649. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  650. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  651. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  652. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  653. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  654. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → C1zT2kEfoLhDdbWPWKrXd}/_buildManifest.js +0 -0
  655. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → C1zT2kEfoLhDdbWPWKrXd}/_ssgManifest.js +0 -0
@@ -29,10 +29,17 @@ describe("Input", () => {
29
29
  const input = new Input();
30
30
  input.secure = true;
31
31
  input.focused = true;
32
- input.handleInput("secret123");
32
+ const SECRET = "secret123";
33
+ input.handleInput(SECRET);
33
34
  const line = input.render(40)[0] ?? "";
34
- assert.ok(!line.includes("secret123"), "rendered line must not expose raw secret text");
35
- assert.ok(line.includes("*********"), "rendered line should include masked characters");
35
+ // Previous assertion was `line.includes("*********")` a literal
36
+ // 9-star string that silently goes stale if SECRET is renamed to
37
+ // a different length (#4796). Match any run of asterisks and
38
+ // assert its length covers the secret.
39
+ assert.ok(!line.includes(SECRET), "rendered line must not expose raw secret text");
40
+ const maskMatch = line.match(/\*+/);
41
+ assert.ok(maskMatch, `rendered line must include masked characters, got: ${JSON.stringify(line)}`);
42
+ assert.ok(maskMatch[0].length >= SECRET.length, `mask must cover at least the secret length (${SECRET.length}), got ${maskMatch[0].length} asterisks`);
36
43
  });
37
44
  it("maps kitty keypad digits to text instead of inserting private-use glyphs", () => {
38
45
  const input = new Input();
@@ -1 +1 @@
1
- {"version":3,"file":"input.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/input.test.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,4DAA4D;AAE5D,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACtB,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QAErB,yDAAyD;QACzD,KAAK,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;QAEtC,2BAA2B;QAC3B,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QAEtB,mDAAmD;QACnD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QAErB,iEAAiE;QACjE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAChD,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACnC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QACjE,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAE/B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,+CAA+C,CAAC,CAAC;QACxF,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,gDAAgD,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QACnF,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QAErB,KAAK,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAErC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC7D,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QAErB,KAAK,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QAEjC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["// pi-tui Input component regression tests\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\n\nimport { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { Input } from \"../input.js\";\n\ndescribe(\"Input\", () => {\n\tit(\"paste buffer is cleared when focus is lost\", () => {\n\t\tconst input = new Input();\n\t\tinput.focused = true;\n\n\t\t// Simulate starting a paste (bracket paste start marker)\n\t\tinput.handleInput(\"\\x1b[200~partial\");\n\n\t\t// Now lose focus mid-paste\n\t\tinput.focused = false;\n\n\t\t// Regain focus — should not have stale paste state\n\t\tinput.focused = true;\n\n\t\t// Typing normal text should work without paste buffer corruption\n\t\tinput.handleInput(\"hello\");\n\t\tassert.equal(input.getValue(), \"hello\");\n\t});\n\n\tit(\"focused getter/setter works correctly\", () => {\n\t\tconst input = new Input();\n\t\tassert.equal(input.focused, false);\n\t\tinput.focused = true;\n\t\tassert.equal(input.focused, true);\n\t\tinput.focused = false;\n\t\tassert.equal(input.focused, false);\n\t});\n\n\tit(\"secure mode obscures typed characters in render output\", () => {\n\t\tconst input = new Input();\n\t\tinput.secure = true;\n\t\tinput.focused = true;\n\t\tinput.handleInput(\"secret123\");\n\n\t\tconst line = input.render(40)[0] ?? \"\";\n\t\tassert.ok(!line.includes(\"secret123\"), \"rendered line must not expose raw secret text\");\n\t\tassert.ok(line.includes(\"*********\"), \"rendered line should include masked characters\");\n\t});\n\n\tit(\"maps kitty keypad digits to text instead of inserting private-use glyphs\", () => {\n\t\tconst input = new Input();\n\t\tinput.focused = true;\n\n\t\tinput.handleInput(\"\\x1b[57400;129u\");\n\n\t\tassert.equal(input.getValue(), \"1\");\n\t});\n\n\tit(\"ignores kitty keypad navigation keys in text input\", () => {\n\t\tconst input = new Input();\n\t\tinput.focused = true;\n\n\t\tinput.handleInput(\"\\x1b[57417u\");\n\n\t\tassert.equal(input.getValue(), \"\");\n\t});\n});\n"]}
1
+ {"version":3,"file":"input.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/input.test.ts"],"names":[],"mappings":"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","sourcesContent":["// pi-tui Input component regression tests\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\n\nimport { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { Input } from \"../input.js\";\n\ndescribe(\"Input\", () => {\n\tit(\"paste buffer is cleared when focus is lost\", () => {\n\t\tconst input = new Input();\n\t\tinput.focused = true;\n\n\t\t// Simulate starting a paste (bracket paste start marker)\n\t\tinput.handleInput(\"\\x1b[200~partial\");\n\n\t\t// Now lose focus mid-paste\n\t\tinput.focused = false;\n\n\t\t// Regain focus — should not have stale paste state\n\t\tinput.focused = true;\n\n\t\t// Typing normal text should work without paste buffer corruption\n\t\tinput.handleInput(\"hello\");\n\t\tassert.equal(input.getValue(), \"hello\");\n\t});\n\n\tit(\"focused getter/setter works correctly\", () => {\n\t\tconst input = new Input();\n\t\tassert.equal(input.focused, false);\n\t\tinput.focused = true;\n\t\tassert.equal(input.focused, true);\n\t\tinput.focused = false;\n\t\tassert.equal(input.focused, false);\n\t});\n\n\tit(\"secure mode obscures typed characters in render output\", () => {\n\t\tconst input = new Input();\n\t\tinput.secure = true;\n\t\tinput.focused = true;\n\t\tconst SECRET = \"secret123\";\n\t\tinput.handleInput(SECRET);\n\n\t\tconst line = input.render(40)[0] ?? \"\";\n\t\t// Previous assertion was `line.includes(\"*********\")` — a literal\n\t\t// 9-star string that silently goes stale if SECRET is renamed to\n\t\t// a different length (#4796). Match any run of asterisks and\n\t\t// assert its length covers the secret.\n\t\tassert.ok(\n\t\t\t!line.includes(SECRET),\n\t\t\t\"rendered line must not expose raw secret text\",\n\t\t);\n\t\tconst maskMatch = line.match(/\\*+/);\n\t\tassert.ok(\n\t\t\tmaskMatch,\n\t\t\t`rendered line must include masked characters, got: ${JSON.stringify(line)}`,\n\t\t);\n\t\tassert.ok(\n\t\t\tmaskMatch[0].length >= SECRET.length,\n\t\t\t`mask must cover at least the secret length (${SECRET.length}), got ${maskMatch[0].length} asterisks`,\n\t\t);\n\t});\n\n\tit(\"maps kitty keypad digits to text instead of inserting private-use glyphs\", () => {\n\t\tconst input = new Input();\n\t\tinput.focused = true;\n\n\t\tinput.handleInput(\"\\x1b[57400;129u\");\n\n\t\tassert.equal(input.getValue(), \"1\");\n\t});\n\n\tit(\"ignores kitty keypad navigation keys in text input\", () => {\n\t\tconst input = new Input();\n\t\tinput.focused = true;\n\n\t\tinput.handleInput(\"\\x1b[57417u\");\n\n\t\tassert.equal(input.getValue(), \"\");\n\t});\n});\n"]}
@@ -1,6 +1,17 @@
1
1
  // pi-tui Loader component regression tests
2
- // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
2
+ //
3
+ // The previous version of this file contained 3 tests that called
4
+ // `start()`/`stop()`/`dispose()` with no assertions at all — the claimed
5
+ // regression ("interval leak") would not have failed any of them. See
6
+ // #4794 / #4784.
7
+ //
8
+ // This rewrite uses Node's `mock.timers` to observe the interval that
9
+ // `Loader` registers internally, and `mock.fn()` on the mock TUI to
10
+ // count render requests. Each test asserts on observable behaviour:
11
+ // how many `requestRender` calls happen per tick, whether the interval
12
+ // is cleared on stop/dispose, and whether post-dispose stop() is safe.
3
13
  import { describe, it, mock, beforeEach, afterEach } from "node:test";
14
+ import assert from "node:assert/strict";
4
15
  import { Loader } from "../loader.js";
5
16
  function makeMockTUI() {
6
17
  return { requestRender: mock.fn() };
@@ -10,28 +21,61 @@ describe("Loader", () => {
10
21
  let tui;
11
22
  beforeEach(() => {
12
23
  tui = makeMockTUI();
24
+ mock.timers.enable({ apis: ["setInterval"] });
13
25
  });
14
26
  afterEach(() => {
15
- loader?.stop();
27
+ try {
28
+ loader?.stop();
29
+ }
30
+ catch {
31
+ /* best-effort */
32
+ }
33
+ mock.timers.reset();
34
+ });
35
+ it("constructor starts a spinner that ticks every 80ms and requests renders", () => {
36
+ loader = new Loader(tui, (s) => s, (s) => s, "test");
37
+ // Initial render request from start().
38
+ const initial = tui.requestRender.mock.callCount();
39
+ assert.ok(initial >= 1, `constructor should trigger at least one render (got ${initial})`);
40
+ // Advance 80ms — one interval tick should call requestRender once more.
41
+ mock.timers.tick(80);
42
+ assert.equal(tui.requestRender.mock.callCount(), initial + 1, "80ms tick should advance the spinner and call requestRender");
43
+ // Advance another 240ms — three more ticks.
44
+ mock.timers.tick(240);
45
+ assert.equal(tui.requestRender.mock.callCount(), initial + 4, "four total ticks after the initial render");
16
46
  });
17
47
  it("start() is idempotent — calling twice does not leak intervals", () => {
18
48
  loader = new Loader(tui, (s) => s, (s) => s, "test");
19
- // Constructor calls start() once, call it again
49
+ // Constructor already started one interval. Call start() again.
20
50
  loader.start();
21
- // stop() should clear the interval cleanly without orphaned timers
51
+ const before = tui.requestRender.mock.callCount();
52
+ mock.timers.tick(80);
53
+ const after = tui.requestRender.mock.callCount();
54
+ // Exactly ONE tick's worth of work should happen — if the second
55
+ // start() leaked an interval, we'd see two increments per tick.
56
+ assert.equal(after - before, 1, "double-start must not double-tick (interval leak would show 2 increments)");
57
+ });
58
+ it("stop() clears the interval — no more ticks advance requestRender", () => {
59
+ loader = new Loader(tui, (s) => s, (s) => s, "test");
22
60
  loader.stop();
61
+ const beforeTick = tui.requestRender.mock.callCount();
62
+ mock.timers.tick(400); // 5 tick intervals
63
+ assert.equal(tui.requestRender.mock.callCount(), beforeTick, "stopped loader must not advance renders");
23
64
  });
24
65
  it("dispose() stops the interval and nulls the TUI reference", () => {
25
66
  loader = new Loader(tui, (s) => s, (s) => s, "test");
26
67
  loader.dispose();
27
- // After dispose, calling stop() again should be safe (no-op)
28
- loader.stop();
68
+ const beforeTick = tui.requestRender.mock.callCount();
69
+ mock.timers.tick(400);
70
+ assert.equal(tui.requestRender.mock.callCount(), beforeTick, "disposed loader must not advance renders");
71
+ // Calling stop() after dispose() must not throw.
72
+ assert.doesNotThrow(() => loader.stop());
29
73
  });
30
- it("stop() is safe to call multiple times", () => {
74
+ it("stop() is safe to call multiple times without throwing", () => {
31
75
  loader = new Loader(tui, (s) => s, (s) => s, "test");
32
76
  loader.stop();
33
- loader.stop();
34
- loader.stop();
77
+ assert.doesNotThrow(() => loader.stop());
78
+ assert.doesNotThrow(() => loader.stop());
35
79
  });
36
80
  });
37
81
  //# sourceMappingURL=loader.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/loader.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,4DAA4D;AAE5D,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,SAAS,WAAW;IACnB,OAAO,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,EAAS,CAAC;AAC5C,CAAC;AAED,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACvB,IAAI,MAAc,CAAC;IACnB,IAAI,GAAmC,CAAC;IAExC,UAAU,CAAC,GAAG,EAAE;QACf,GAAG,GAAG,WAAW,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,MAAM,EAAE,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACxE,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACrD,gDAAgD;QAChD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,mEAAmE;QACnE,MAAM,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QACnE,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,6DAA6D;QAC7D,MAAM,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAChD,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["// pi-tui Loader component regression tests\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\n\nimport { describe, it, mock, beforeEach, afterEach } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { Loader } from \"../loader.js\";\n\nfunction makeMockTUI() {\n\treturn { requestRender: mock.fn() } as any;\n}\n\ndescribe(\"Loader\", () => {\n\tlet loader: Loader;\n\tlet tui: ReturnType<typeof makeMockTUI>;\n\n\tbeforeEach(() => {\n\t\ttui = makeMockTUI();\n\t});\n\n\tafterEach(() => {\n\t\tloader?.stop();\n\t});\n\n\tit(\"start() is idempotent — calling twice does not leak intervals\", () => {\n\t\tloader = new Loader(tui, (s) => s, (s) => s, \"test\");\n\t\t// Constructor calls start() once, call it again\n\t\tloader.start();\n\t\t// stop() should clear the interval cleanly without orphaned timers\n\t\tloader.stop();\n\t});\n\n\tit(\"dispose() stops the interval and nulls the TUI reference\", () => {\n\t\tloader = new Loader(tui, (s) => s, (s) => s, \"test\");\n\t\tloader.dispose();\n\t\t// After dispose, calling stop() again should be safe (no-op)\n\t\tloader.stop();\n\t});\n\n\tit(\"stop() is safe to call multiple times\", () => {\n\t\tloader = new Loader(tui, (s) => s, (s) => s, \"test\");\n\t\tloader.stop();\n\t\tloader.stop();\n\t\tloader.stop();\n\t});\n});\n"]}
1
+ {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/loader.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,kEAAkE;AAClE,yEAAyE;AACzE,sEAAsE;AACtE,iBAAiB;AACjB,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,oEAAoE;AACpE,uEAAuE;AACvE,uEAAuE;AAEvE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAMtC,SAAS,WAAW;IAClB,OAAO,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;AACtC,CAAC;AAED,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,IAAI,MAAc,CAAC;IACnB,IAAI,GAAY,CAAC;IAEjB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,WAAW,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,GAAG,IAAI,MAAM,CAAC,GAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,uCAAuC;QACvC,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACnD,MAAM,CAAC,EAAE,CACP,OAAO,IAAI,CAAC,EACZ,uDAAuD,OAAO,GAAG,CAClE,CAAC;QAEF,wEAAwE;QACxE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,MAAM,CAAC,KAAK,CACV,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAClC,OAAO,GAAG,CAAC,EACX,6DAA6D,CAC9D,CAAC;QAEF,4CAA4C;QAC5C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,CAAC,KAAK,CACV,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAClC,OAAO,GAAG,CAAC,EACX,2CAA2C,CAC5C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,GAAG,IAAI,MAAM,CAAC,GAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,gEAAgE;QAChE,MAAM,CAAC,KAAK,EAAE,CAAC;QAEf,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjD,iEAAiE;QACjE,gEAAgE;QAChE,MAAM,CAAC,KAAK,CACV,KAAK,GAAG,MAAM,EACd,CAAC,EACD,2EAA2E,CAC5E,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,GAAG,IAAI,MAAM,CAAC,GAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,MAAM,CAAC,IAAI,EAAE,CAAC;QAEd,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB;QAC1C,MAAM,CAAC,KAAK,CACV,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAClC,UAAU,EACV,yCAAyC,CAC1C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,GAAG,IAAI,MAAM,CAAC,GAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,MAAM,CAAC,OAAO,EAAE,CAAC;QAEjB,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,CAAC,KAAK,CACV,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAClC,UAAU,EACV,0CAA0C,CAC3C,CAAC;QAEF,iDAAiD;QACjD,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,IAAI,MAAM,CAAC,GAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// pi-tui Loader component regression tests\n//\n// The previous version of this file contained 3 tests that called\n// `start()`/`stop()`/`dispose()` with no assertions at all — the claimed\n// regression (\"interval leak\") would not have failed any of them. See\n// #4794 / #4784.\n//\n// This rewrite uses Node's `mock.timers` to observe the interval that\n// `Loader` registers internally, and `mock.fn()` on the mock TUI to\n// count render requests. Each test asserts on observable behaviour:\n// how many `requestRender` calls happen per tick, whether the interval\n// is cleared on stop/dispose, and whether post-dispose stop() is safe.\n\nimport { describe, it, mock, beforeEach, afterEach } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { Loader } from \"../loader.js\";\n\ninterface MockTui {\n requestRender: ReturnType<typeof mock.fn>;\n}\n\nfunction makeMockTUI(): MockTui {\n return { requestRender: mock.fn() };\n}\n\ndescribe(\"Loader\", () => {\n let loader: Loader;\n let tui: MockTui;\n\n beforeEach(() => {\n tui = makeMockTUI();\n mock.timers.enable({ apis: [\"setInterval\"] });\n });\n\n afterEach(() => {\n try {\n loader?.stop();\n } catch {\n /* best-effort */\n }\n mock.timers.reset();\n });\n\n it(\"constructor starts a spinner that ticks every 80ms and requests renders\", () => {\n loader = new Loader(tui as never, (s) => s, (s) => s, \"test\");\n // Initial render request from start().\n const initial = tui.requestRender.mock.callCount();\n assert.ok(\n initial >= 1,\n `constructor should trigger at least one render (got ${initial})`,\n );\n\n // Advance 80ms — one interval tick should call requestRender once more.\n mock.timers.tick(80);\n assert.equal(\n tui.requestRender.mock.callCount(),\n initial + 1,\n \"80ms tick should advance the spinner and call requestRender\",\n );\n\n // Advance another 240ms — three more ticks.\n mock.timers.tick(240);\n assert.equal(\n tui.requestRender.mock.callCount(),\n initial + 4,\n \"four total ticks after the initial render\",\n );\n });\n\n it(\"start() is idempotent — calling twice does not leak intervals\", () => {\n loader = new Loader(tui as never, (s) => s, (s) => s, \"test\");\n // Constructor already started one interval. Call start() again.\n loader.start();\n\n const before = tui.requestRender.mock.callCount();\n mock.timers.tick(80);\n const after = tui.requestRender.mock.callCount();\n // Exactly ONE tick's worth of work should happen — if the second\n // start() leaked an interval, we'd see two increments per tick.\n assert.equal(\n after - before,\n 1,\n \"double-start must not double-tick (interval leak would show 2 increments)\",\n );\n });\n\n it(\"stop() clears the interval no more ticks advance requestRender\", () => {\n loader = new Loader(tui as never, (s) => s, (s) => s, \"test\");\n loader.stop();\n\n const beforeTick = tui.requestRender.mock.callCount();\n mock.timers.tick(400); // 5 tick intervals\n assert.equal(\n tui.requestRender.mock.callCount(),\n beforeTick,\n \"stopped loader must not advance renders\",\n );\n });\n\n it(\"dispose() stops the interval and nulls the TUI reference\", () => {\n loader = new Loader(tui as never, (s) => s, (s) => s, \"test\");\n loader.dispose();\n\n const beforeTick = tui.requestRender.mock.callCount();\n mock.timers.tick(400);\n assert.equal(\n tui.requestRender.mock.callCount(),\n beforeTick,\n \"disposed loader must not advance renders\",\n );\n\n // Calling stop() after dispose() must not throw.\n assert.doesNotThrow(() => loader.stop());\n });\n\n it(\"stop() is safe to call multiple times without throwing\", () => {\n loader = new Loader(tui as never, (s) => s, (s) => s, \"test\");\n loader.stop();\n assert.doesNotThrow(() => loader.stop());\n assert.doesNotThrow(() => loader.stop());\n });\n});\n"]}
@@ -59,8 +59,12 @@ test("Markdown trims trailing empty lines", () => {
59
59
  const text = "Some text\n\n";
60
60
  const md = new Markdown(text, 0, 0, noopTheme());
61
61
  const lines = md.render(80);
62
- // Last line should not be empty (trailing empties are trimmed)
62
+ // Previous assertion was `lastLine.trim().length > 0 || lines.length === 1`
63
+ // — the `|| lines.length === 1` disjunction trivially passed for any
64
+ // single-line render, so a regression that returned `['']` still
65
+ // passed (#4796). Assert the trim invariant directly.
66
+ assert.ok(lines.length > 0, "render must produce at least one line");
63
67
  const lastLine = lines[lines.length - 1];
64
- assert.ok(lastLine.trim().length > 0 || lines.length === 1, "trailing empty lines should be trimmed");
68
+ assert.ok(lastLine.trim().length > 0, `last line must have visible content after trim, got: ${JSON.stringify(lastLine)}`);
65
69
  });
66
70
  //# sourceMappingURL=markdown-maxlines.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-maxlines.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/markdown-maxlines.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { test } from \"node:test\";\n\nimport { Markdown, type MarkdownTheme } from \"../markdown.js\";\n\nfunction noopTheme(): MarkdownTheme {\n\tconst identity = (text: string) => text;\n\treturn {\n\t\theading: identity,\n\t\tlink: identity,\n\t\tlinkUrl: identity,\n\t\tcode: identity,\n\t\tcodeBlock: identity,\n\t\tcodeBlockBorder: identity,\n\t\tquote: identity,\n\t\tquoteBorder: identity,\n\t\thr: identity,\n\t\tlistBullet: identity,\n\t\tbold: identity,\n\t\titalic: identity,\n\t\tstrikethrough: identity,\n\t\tunderline: identity,\n\t};\n}\n\ntest(\"Markdown renders all lines when maxLines is not set\", () => {\n\tconst text = \"Line 1\\n\\nLine 2\\n\\nLine 3\\n\\nLine 4\\n\\nLine 5\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tconst lines = md.render(80);\n\t// Each paragraph produces a line + an inter-paragraph blank line\n\tconst contentLines = lines.filter((l) => l.trim().length > 0);\n\tassert.ok(contentLines.length >= 5, `expected at least 5 content lines, got ${contentLines.length}`);\n});\n\ntest(\"Markdown truncates from the top when maxLines is exceeded\", () => {\n\tconst text = \"Line 1\\n\\nLine 2\\n\\nLine 3\\n\\nLine 4\\n\\nLine 5\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tmd.maxLines = 3;\n\tconst lines = md.render(80);\n\tassert.ok(lines.length <= 3, `expected at most 3 lines, got ${lines.length}`);\n\t// First line should be the ellipsis indicator\n\tassert.ok(lines[0].includes(\"…\"), \"first line should contain ellipsis indicator\");\n\tassert.ok(lines[0].includes(\"above\"), \"first line should mention lines above\");\n});\n\ntest(\"Markdown preserves most recent content when truncating\", () => {\n\tconst text = \"First paragraph\\n\\nSecond paragraph\\n\\nThird paragraph\\n\\nFourth paragraph\\n\\nFifth paragraph\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tmd.maxLines = 3;\n\tconst lines = md.render(80);\n\t// The last rendered line should contain \"Fifth paragraph\" (the most recent content)\n\tconst lastContentLine = lines.filter((l) => !l.includes(\"…\")).pop() ?? \"\";\n\tassert.ok(\n\t\tlastContentLine.includes(\"Fifth paragraph\"),\n\t\t`expected last content line to contain \"Fifth paragraph\", got \"${lastContentLine}\"`,\n\t);\n});\n\ntest(\"Markdown does not truncate when content fits within maxLines\", () => {\n\tconst text = \"Short text\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tmd.maxLines = 10;\n\tconst lines = md.render(80);\n\tassert.ok(!lines.some((l) => l.includes(\"…\")), \"should not contain ellipsis when content fits\");\n\tassert.ok(lines.some((l) => l.includes(\"Short text\")), \"should contain the original text\");\n});\n\ntest(\"Markdown trims trailing empty lines\", () => {\n\tconst text = \"Some text\\n\\n\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tconst lines = md.render(80);\n\t// Last line should not be empty (trailing empties are trimmed)\n\tconst lastLine = lines[lines.length - 1];\n\tassert.ok(lastLine.trim().length > 0 || lines.length === 1, \"trailing empty lines should be trimmed\");\n});\n"]}
1
+ {"version":3,"file":"markdown-maxlines.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/markdown-maxlines.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { test } from \"node:test\";\n\nimport { Markdown, type MarkdownTheme } from \"../markdown.js\";\n\nfunction noopTheme(): MarkdownTheme {\n\tconst identity = (text: string) => text;\n\treturn {\n\t\theading: identity,\n\t\tlink: identity,\n\t\tlinkUrl: identity,\n\t\tcode: identity,\n\t\tcodeBlock: identity,\n\t\tcodeBlockBorder: identity,\n\t\tquote: identity,\n\t\tquoteBorder: identity,\n\t\thr: identity,\n\t\tlistBullet: identity,\n\t\tbold: identity,\n\t\titalic: identity,\n\t\tstrikethrough: identity,\n\t\tunderline: identity,\n\t};\n}\n\ntest(\"Markdown renders all lines when maxLines is not set\", () => {\n\tconst text = \"Line 1\\n\\nLine 2\\n\\nLine 3\\n\\nLine 4\\n\\nLine 5\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tconst lines = md.render(80);\n\t// Each paragraph produces a line + an inter-paragraph blank line\n\tconst contentLines = lines.filter((l) => l.trim().length > 0);\n\tassert.ok(contentLines.length >= 5, `expected at least 5 content lines, got ${contentLines.length}`);\n});\n\ntest(\"Markdown truncates from the top when maxLines is exceeded\", () => {\n\tconst text = \"Line 1\\n\\nLine 2\\n\\nLine 3\\n\\nLine 4\\n\\nLine 5\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tmd.maxLines = 3;\n\tconst lines = md.render(80);\n\tassert.ok(lines.length <= 3, `expected at most 3 lines, got ${lines.length}`);\n\t// First line should be the ellipsis indicator\n\tassert.ok(lines[0].includes(\"…\"), \"first line should contain ellipsis indicator\");\n\tassert.ok(lines[0].includes(\"above\"), \"first line should mention lines above\");\n});\n\ntest(\"Markdown preserves most recent content when truncating\", () => {\n\tconst text = \"First paragraph\\n\\nSecond paragraph\\n\\nThird paragraph\\n\\nFourth paragraph\\n\\nFifth paragraph\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tmd.maxLines = 3;\n\tconst lines = md.render(80);\n\t// The last rendered line should contain \"Fifth paragraph\" (the most recent content)\n\tconst lastContentLine = lines.filter((l) => !l.includes(\"…\")).pop() ?? \"\";\n\tassert.ok(\n\t\tlastContentLine.includes(\"Fifth paragraph\"),\n\t\t`expected last content line to contain \"Fifth paragraph\", got \"${lastContentLine}\"`,\n\t);\n});\n\ntest(\"Markdown does not truncate when content fits within maxLines\", () => {\n\tconst text = \"Short text\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tmd.maxLines = 10;\n\tconst lines = md.render(80);\n\tassert.ok(!lines.some((l) => l.includes(\"…\")), \"should not contain ellipsis when content fits\");\n\tassert.ok(lines.some((l) => l.includes(\"Short text\")), \"should contain the original text\");\n});\n\ntest(\"Markdown trims trailing empty lines\", () => {\n\tconst text = \"Some text\\n\\n\";\n\tconst md = new Markdown(text, 0, 0, noopTheme());\n\tconst lines = md.render(80);\n\t// Previous assertion was `lastLine.trim().length > 0 || lines.length === 1`\n\t// — the `|| lines.length === 1` disjunction trivially passed for any\n\t// single-line render, so a regression that returned `['']` still\n\t// passed (#4796). Assert the trim invariant directly.\n\tassert.ok(lines.length > 0, \"render must produce at least one line\");\n\tconst lastLine = lines[lines.length - 1];\n\tassert.ok(\n\t\tlastLine.trim().length > 0,\n\t\t`last line must have visible content after trim, got: ${JSON.stringify(lastLine)}`,\n\t);\n});\n"]}
@@ -55,6 +55,7 @@ export declare class Editor implements Component, Focusable {
55
55
  private visualLineMapCache;
56
56
  onSubmit?: (text: string) => void;
57
57
  onChange?: (text: string) => void;
58
+ onPasteImagePath?: (filePath: string) => void;
58
59
  disableSubmit: boolean;
59
60
  constructor(tui: TUI, theme: EditorTheme, options?: EditorOptions);
60
61
  getPaddingX(): number;
@@ -110,6 +111,19 @@ export declare class Editor implements Component, Focusable {
110
111
  * Prevents synchronous fuzzyFind calls from blocking the event loop on every keystroke.
111
112
  */
112
113
  private debouncedTriggerAutocomplete;
114
+ /**
115
+ * Image file extensions recognized when pasted as a file path.
116
+ *
117
+ * Restricted to formats commonly accepted by AI vision APIs and that have
118
+ * reliable magic-byte signatures for content verification. SVG is excluded
119
+ * (XML/JS-bearing); BMP/TIFF/HEIC/HEIF/AVIF excluded for compatibility and
120
+ * verification simplicity — users can convert before pasting.
121
+ *
122
+ * Detection assumes terminal emulators (iTerm2, Warp, etc.) paste a single
123
+ * absolute file path on drag-drop. Multi-line pastes and bare extensions
124
+ * are intentionally not matched.
125
+ */
126
+ private static readonly IMAGE_EXTENSIONS;
113
127
  private handlePaste;
114
128
  private addNewLine;
115
129
  private shouldSubmitOnBackslashEnter;
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/components/editor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/components/editor.ts"],"names":[],"mappings":"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"}
@@ -855,9 +855,28 @@ export class Editor {
855
855
  this.tui.requestRender();
856
856
  }, Editor.AUTOCOMPLETE_DEBOUNCE_MS);
857
857
  }
858
+ /**
859
+ * Image file extensions recognized when pasted as a file path.
860
+ *
861
+ * Restricted to formats commonly accepted by AI vision APIs and that have
862
+ * reliable magic-byte signatures for content verification. SVG is excluded
863
+ * (XML/JS-bearing); BMP/TIFF/HEIC/HEIF/AVIF excluded for compatibility and
864
+ * verification simplicity — users can convert before pasting.
865
+ *
866
+ * Detection assumes terminal emulators (iTerm2, Warp, etc.) paste a single
867
+ * absolute file path on drag-drop. Multi-line pastes and bare extensions
868
+ * are intentionally not matched.
869
+ */
870
+ static { this.IMAGE_EXTENSIONS = /\.(png|jpe?g|gif|webp)$/i; }
858
871
  handlePaste(pastedText) {
859
872
  this.historyIndex = -1; // Exit history browsing mode
860
873
  this.lastAction = null;
874
+ // Detect pasted image file paths (from terminal emulators like iTerm2)
875
+ const trimmed = pastedText.trim();
876
+ if (this.onPasteImagePath && !trimmed.includes("\n") && Editor.IMAGE_EXTENSIONS.test(trimmed)) {
877
+ this.onPasteImagePath(trimmed);
878
+ return;
879
+ }
861
880
  this.pushUndoSnapshot();
862
881
  // Clean the pasted text
863
882
  const cleanText = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");