gsd-pi 2.70.1 → 2.71.0-dev.246c32d

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 (541) hide show
  1. package/README.md +57 -17
  2. package/dist/cli.js +29 -3
  3. package/dist/headless-events.d.ts +2 -0
  4. package/dist/headless-events.js +7 -0
  5. package/dist/headless.js +16 -3
  6. package/dist/mcp-server.js +40 -17
  7. package/dist/provider-migrations.d.ts +10 -0
  8. package/dist/provider-migrations.js +12 -0
  9. package/dist/resource-loader.js +139 -13
  10. package/dist/resources/GSD-WORKFLOW.md +1 -1
  11. package/dist/resources/agents/debugger.md +58 -0
  12. package/dist/resources/agents/doc-writer.md +43 -0
  13. package/dist/resources/agents/git-ops.md +56 -0
  14. package/dist/resources/agents/javascript-pro.md +46 -271
  15. package/dist/resources/agents/planner.md +55 -0
  16. package/dist/resources/agents/refactorer.md +47 -0
  17. package/dist/resources/agents/reviewer.md +48 -0
  18. package/dist/resources/agents/security.md +59 -0
  19. package/dist/resources/agents/tester.md +50 -0
  20. package/dist/resources/agents/typescript-pro.md +41 -235
  21. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +242 -40
  22. package/dist/resources/extensions/get-secrets-from-user.js +17 -1
  23. package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
  24. package/dist/resources/extensions/gsd/auto/loop.js +32 -1
  25. package/dist/resources/extensions/gsd/auto/phases.js +5 -1
  26. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  27. package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
  28. package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
  29. package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
  30. package/dist/resources/extensions/gsd/auto-start.js +37 -18
  31. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  32. package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
  33. package/dist/resources/extensions/gsd/auto.js +56 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  35. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
  37. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  38. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  39. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  40. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
  41. package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
  42. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
  43. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
  44. package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
  45. package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
  46. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  47. package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
  48. package/dist/resources/extensions/gsd/error-classifier.js +5 -2
  49. package/dist/resources/extensions/gsd/file-lock.js +60 -0
  50. package/dist/resources/extensions/gsd/forensics.js +19 -6
  51. package/dist/resources/extensions/gsd/gate-registry.js +208 -0
  52. package/dist/resources/extensions/gsd/gsd-db.js +41 -0
  53. package/dist/resources/extensions/gsd/guided-flow.js +17 -20
  54. package/dist/resources/extensions/gsd/init-wizard.js +3 -11
  55. package/dist/resources/extensions/gsd/metrics.js +1 -0
  56. package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
  57. package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
  58. package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
  59. package/dist/resources/extensions/gsd/notification-store.js +56 -5
  60. package/dist/resources/extensions/gsd/notification-widget.js +5 -13
  61. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
  62. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  63. package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
  64. package/dist/resources/extensions/gsd/prompts/complete-slice.md +5 -3
  65. package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
  66. package/dist/resources/extensions/gsd/prompts/execute-task.md +22 -19
  67. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  68. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  69. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  71. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  72. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
  73. package/dist/resources/extensions/gsd/session-model-override.js +25 -0
  74. package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
  75. package/dist/resources/extensions/gsd/state.js +241 -332
  76. package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
  77. package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
  78. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +38 -1
  79. package/dist/resources/extensions/gsd/workflow-events.js +25 -13
  80. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
  81. package/dist/resources/extensions/gsd/workflow-mcp.js +1 -1
  82. package/dist/resources/extensions/ollama/index.js +13 -5
  83. package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
  84. package/dist/resources/extensions/subagent/agents.js +8 -0
  85. package/dist/resources/extensions/subagent/index.js +17 -0
  86. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  87. package/dist/startup-model-validation.d.ts +0 -1
  88. package/dist/startup-model-validation.js +6 -2
  89. package/dist/web/standalone/.next/BUILD_ID +1 -1
  90. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  91. package/dist/web/standalone/.next/build-manifest.json +4 -4
  92. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  93. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  94. package/dist/web/standalone/.next/required-server-files.json +4 -4
  95. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  96. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  106. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  112. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  154. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  164. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  170. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  186. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  190. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/app/index.html +1 -1
  200. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  201. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  202. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  203. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  204. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  205. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  206. package/dist/web/standalone/.next/server/app/page.js +2 -2
  207. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  209. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  210. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  211. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/middleware.js +2 -2
  214. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  215. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  216. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  217. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  218. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  219. package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
  220. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  221. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  222. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
  226. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  227. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  228. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  229. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  230. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  231. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  232. package/dist/web/standalone/server.js +1 -1
  233. package/package.json +1 -1
  234. package/packages/mcp-server/dist/env-writer.d.ts +39 -0
  235. package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
  236. package/packages/mcp-server/dist/env-writer.js +158 -0
  237. package/packages/mcp-server/dist/env-writer.js.map +1 -0
  238. package/packages/mcp-server/dist/server.d.ts +23 -3
  239. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  240. package/packages/mcp-server/dist/server.js +192 -44
  241. package/packages/mcp-server/dist/server.js.map +1 -1
  242. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  243. package/packages/mcp-server/dist/workflow-tools.js +22 -12
  244. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  245. package/packages/mcp-server/src/env-writer.test.ts +280 -0
  246. package/packages/mcp-server/src/env-writer.ts +183 -0
  247. package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
  248. package/packages/mcp-server/src/server.ts +247 -41
  249. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  250. package/packages/mcp-server/src/workflow-tools.ts +32 -12
  251. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  252. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  253. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
  254. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
  255. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
  256. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
  257. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  258. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  259. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  260. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  261. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  262. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  263. package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
  264. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  265. package/packages/pi-ai/dist/providers/anthropic.js +7 -4
  266. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  267. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  268. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  269. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  270. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  271. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
  272. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  273. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  274. package/packages/pi-ai/src/providers/anthropic.ts +8 -4
  275. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  276. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
  277. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
  278. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
  279. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
  280. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  281. package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
  282. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
  284. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
  286. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
  288. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
  290. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
  291. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +388 -0
  292. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
  293. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  294. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
  297. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
  298. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
  299. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
  300. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
  302. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  304. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  305. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  306. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  307. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +5 -0
  308. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  309. package/packages/pi-coding-agent/dist/core/retry-handler.js +55 -1
  310. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  311. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +57 -0
  312. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  313. package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
  314. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  315. package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
  316. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  317. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
  318. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
  319. package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
  320. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
  321. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  322. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  323. package/packages/pi-coding-agent/dist/index.js +1 -1
  324. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  325. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
  326. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
  327. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
  328. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
  329. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
  330. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  331. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
  332. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  333. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
  334. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  335. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
  336. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
  337. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  338. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  339. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
  340. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  341. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  342. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  343. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  344. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
  345. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  346. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
  347. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  348. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  349. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +175 -25
  350. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  351. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  352. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  353. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  354. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  355. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  356. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
  358. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  359. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +62 -5
  360. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  361. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
  362. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  363. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
  364. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  365. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
  366. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  367. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  368. package/packages/pi-coding-agent/package.json +1 -1
  369. package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
  370. package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
  371. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
  372. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
  373. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +468 -0
  374. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  375. package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
  376. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  377. package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
  378. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +83 -0
  379. package/packages/pi-coding-agent/src/core/retry-handler.ts +60 -1
  380. package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
  381. package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
  382. package/packages/pi-coding-agent/src/index.ts +1 -0
  383. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
  384. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
  385. package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
  386. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
  387. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  388. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
  389. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +205 -31
  390. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  391. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
  392. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +70 -5
  393. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
  394. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
  395. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
  396. package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
  397. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  398. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
  399. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
  400. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
  401. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
  402. package/packages/pi-tui/dist/components/input.d.ts +2 -0
  403. package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
  404. package/packages/pi-tui/dist/components/input.js +7 -4
  405. package/packages/pi-tui/dist/components/input.js.map +1 -1
  406. package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
  407. package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
  408. package/packages/pi-tui/dist/components/markdown.js +17 -1
  409. package/packages/pi-tui/dist/components/markdown.js.map +1 -1
  410. package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
  411. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
  412. package/packages/pi-tui/src/components/input.ts +7 -4
  413. package/packages/pi-tui/src/components/markdown.ts +22 -1
  414. package/pkg/package.json +1 -1
  415. package/src/resources/GSD-WORKFLOW.md +1 -1
  416. package/src/resources/agents/debugger.md +58 -0
  417. package/src/resources/agents/doc-writer.md +43 -0
  418. package/src/resources/agents/git-ops.md +56 -0
  419. package/src/resources/agents/javascript-pro.md +46 -271
  420. package/src/resources/agents/planner.md +55 -0
  421. package/src/resources/agents/refactorer.md +47 -0
  422. package/src/resources/agents/reviewer.md +48 -0
  423. package/src/resources/agents/security.md +59 -0
  424. package/src/resources/agents/tester.md +50 -0
  425. package/src/resources/agents/typescript-pro.md +41 -235
  426. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +288 -39
  427. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +330 -2
  428. package/src/resources/extensions/get-secrets-from-user.ts +24 -1
  429. package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
  430. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  431. package/src/resources/extensions/gsd/auto/loop.ts +45 -1
  432. package/src/resources/extensions/gsd/auto/phases.ts +6 -0
  433. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  434. package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
  435. package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
  436. package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
  437. package/src/resources/extensions/gsd/auto-start.ts +44 -20
  438. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  439. package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
  440. package/src/resources/extensions/gsd/auto.ts +72 -0
  441. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  442. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
  443. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
  444. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  445. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  446. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  447. package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
  448. package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
  449. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
  450. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
  451. package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
  452. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
  453. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  454. package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
  455. package/src/resources/extensions/gsd/error-classifier.ts +5 -2
  456. package/src/resources/extensions/gsd/file-lock.ts +59 -0
  457. package/src/resources/extensions/gsd/forensics.ts +23 -7
  458. package/src/resources/extensions/gsd/gate-registry.ts +251 -0
  459. package/src/resources/extensions/gsd/gsd-db.ts +51 -0
  460. package/src/resources/extensions/gsd/guided-flow.ts +17 -19
  461. package/src/resources/extensions/gsd/init-wizard.ts +3 -13
  462. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  463. package/src/resources/extensions/gsd/metrics.ts +12 -1
  464. package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
  465. package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
  466. package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
  467. package/src/resources/extensions/gsd/notification-store.ts +54 -5
  468. package/src/resources/extensions/gsd/notification-widget.ts +5 -14
  469. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
  470. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  471. package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
  472. package/src/resources/extensions/gsd/prompts/complete-slice.md +5 -3
  473. package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
  474. package/src/resources/extensions/gsd/prompts/execute-task.md +22 -19
  475. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  476. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  477. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  478. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  479. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  480. package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
  481. package/src/resources/extensions/gsd/session-model-override.ts +36 -0
  482. package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
  483. package/src/resources/extensions/gsd/state.ts +285 -344
  484. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
  485. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  486. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  487. package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
  488. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  489. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
  490. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
  491. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  492. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
  493. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  494. package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
  495. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
  496. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
  497. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
  498. package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
  499. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  500. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
  501. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
  502. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
  503. package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
  504. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
  505. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
  506. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
  507. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
  508. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  509. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  510. package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
  511. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -0
  512. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
  513. package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
  514. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
  515. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
  516. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  517. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  518. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
  519. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
  520. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
  521. package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
  522. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
  523. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +64 -26
  524. package/src/resources/extensions/gsd/types.ts +26 -0
  525. package/src/resources/extensions/gsd/workflow-events.ts +34 -25
  526. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
  527. package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
  528. package/src/resources/extensions/ollama/index.ts +13 -3
  529. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  530. package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
  531. package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
  532. package/src/resources/extensions/subagent/agents.ts +10 -0
  533. package/src/resources/extensions/subagent/index.ts +18 -0
  534. package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
  535. package/src/resources/skills/create-skill/SKILL.md +2 -0
  536. package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
  537. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  538. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  539. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  540. /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → hnGGkVMxIGGpxSJkf5jIV}/_buildManifest.js +0 -0
  541. /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → hnGGkVMxIGGpxSJkf5jIV}/_ssgManifest.js +0 -0
@@ -1,14 +1,36 @@
1
- import { Loader, Spacer, Text } from "@gsd/pi-tui";
1
+ import { Loader, Markdown, Spacer, Text } from "@gsd/pi-tui";
2
2
 
3
3
  import type { InteractiveModeEvent, InteractiveModeStateHost } from "../interactive-mode-state.js";
4
4
  import { theme } from "../theme/theme.js";
5
5
  import { AssistantMessageComponent } from "../components/assistant-message.js";
6
6
  import { ToolExecutionComponent } from "../components/tool-execution.js";
7
+ import { DynamicBorder } from "../components/dynamic-border.js";
7
8
  import { appKey } from "../components/keybinding-hints.js";
8
9
 
9
10
  // Tracks the last processed content index to avoid re-scanning all blocks on every message_update
10
11
  let lastProcessedContentIndex = 0;
11
12
 
13
+ function hasVisibleAssistantContent(message: { content: Array<any> }): boolean {
14
+ return message.content.some(
15
+ (c) =>
16
+ (c.type === "text" && typeof c.text === "string" && c.text.trim().length > 0)
17
+ || (c.type === "thinking" && typeof c.thinking === "string" && c.thinking.trim().length > 0),
18
+ );
19
+ }
20
+
21
+ function hasAssistantToolBlocks(message: { content: Array<any> }): boolean {
22
+ return message.content.some((c) => c.type === "toolCall" || c.type === "serverToolUse");
23
+ }
24
+
25
+ // Tracks the latest assistant text for the pinned message zone
26
+ let lastPinnedText = "";
27
+ // Whether any tool execution has been added in this assistant turn (triggers pinned display)
28
+ let hasToolsInTurn = false;
29
+ // Reference to the pinned border so we can toggle its label between working/idle
30
+ let pinnedBorder: DynamicBorder | undefined;
31
+ // Reference to the pinned markdown component below the border
32
+ let pinnedTextComponent: Markdown | undefined;
33
+
12
34
  export async function handleAgentEvent(host: InteractiveModeStateHost & {
13
35
  init: () => Promise<void>;
14
36
  getMarkdownThemeWithSettings: () => any;
@@ -31,9 +53,15 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
31
53
 
32
54
  host.footer.invalidate();
33
55
 
34
- // Reset content index tracker when a new assistant message starts
56
+ // Reset content index tracker and pinned state when a new assistant message starts
35
57
  if (event.type === "message_start" && event.message.role === "assistant") {
36
58
  lastProcessedContentIndex = 0;
59
+ lastPinnedText = "";
60
+ hasToolsInTurn = false;
61
+ if (pinnedBorder) pinnedBorder.stopSpinner();
62
+ pinnedBorder = undefined;
63
+ pinnedTextComponent = undefined;
64
+ host.pinnedMessageContainer.clear();
37
65
  }
38
66
 
39
67
  switch (event.type) {
@@ -46,6 +74,12 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
46
74
  host.streamingMessage = undefined;
47
75
  host.pendingTools.clear();
48
76
  host.pendingMessagesContainer.clear();
77
+ host.pinnedMessageContainer.clear();
78
+ lastPinnedText = "";
79
+ hasToolsInTurn = false;
80
+ if (pinnedBorder) pinnedBorder.stopSpinner();
81
+ pinnedBorder = undefined;
82
+ pinnedTextComponent = undefined;
49
83
  host.compactionQueuedMessages = [];
50
84
  host.rebuildChatFromMessages();
51
85
  host.updatePendingMessagesDisplay();
@@ -104,45 +138,54 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
104
138
  host.updatePendingMessagesDisplay();
105
139
  host.ui.requestRender();
106
140
  } else if (event.message.role === "assistant") {
107
- host.streamingComponent = new AssistantMessageComponent(
108
- undefined,
109
- host.hideThinkingBlock,
110
- host.getMarkdownThemeWithSettings(),
111
- host.settingsManager.getTimestampFormat(),
112
- );
113
141
  host.streamingMessage = event.message;
114
- host.chatContainer.addChild(host.streamingComponent);
115
- host.streamingComponent.updateContent(host.streamingMessage);
142
+ // External-tool providers can stream multiple assistant turns through
143
+ // one response. Delay component creation until visible assistant text
144
+ // arrives so tool outputs keep chronological ordering.
116
145
  host.ui.requestRender();
117
146
  }
118
147
  break;
119
148
 
120
149
  case "message_update":
121
- if (host.streamingComponent && event.message.role === "assistant") {
150
+ if (event.message.role === "assistant") {
122
151
  host.streamingMessage = event.message;
123
- host.streamingComponent.updateContent(host.streamingMessage);
124
-
125
- // When the stream adapter signals a completed tool call with an
126
- // external result (from Claude Code SDK), update the pending
127
- // ToolExecutionComponent immediately so output is visible in
128
- // real-time instead of waiting for the session to end.
129
152
  const innerEvent = event.assistantMessageEvent;
153
+
154
+ let externalToolResult:
155
+ | { toolCallId: string; content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; details: Record<string, unknown>; isError: boolean }
156
+ | undefined;
130
157
  if (innerEvent.type === "toolcall_end" && innerEvent.toolCall) {
131
158
  const tc = innerEvent.toolCall as any;
132
- const externalResult = tc.externalResult;
133
- if (externalResult) {
134
- const component = host.pendingTools.get(tc.id);
135
- if (component) {
136
- component.updateResult({
137
- content: externalResult.content ?? [{ type: "text", text: "" }],
138
- details: externalResult.details ?? {},
139
- isError: externalResult.isError ?? false,
140
- });
141
- }
159
+ const ext = tc.externalResult;
160
+ if (ext) {
161
+ externalToolResult = {
162
+ toolCallId: tc.id,
163
+ content: ext.content ?? [{ type: "text", text: "" }],
164
+ details: ext.details ?? {},
165
+ isError: ext.isError ?? false,
166
+ };
167
+ }
168
+ } else if (innerEvent.type === "server_tool_use") {
169
+ const idx = typeof innerEvent.contentIndex === "number" ? innerEvent.contentIndex : -1;
170
+ const block = idx >= 0 ? (host.streamingMessage.content[idx] as any) : undefined;
171
+ const ext = block?.externalResult;
172
+ if (block?.id && ext) {
173
+ externalToolResult = {
174
+ toolCallId: block.id,
175
+ content: ext.content ?? [{ type: "text", text: "" }],
176
+ details: ext.details ?? {},
177
+ isError: ext.isError ?? false,
178
+ };
142
179
  }
143
180
  }
144
181
 
145
182
  const contentBlocks = host.streamingMessage.content;
183
+ // Some adapters reuse a single assistant lifecycle while internally
184
+ // spanning multiple provider turns. When a new turn starts, content
185
+ // length can shrink back to 0/1; reset scan index to avoid skipping.
186
+ if (lastProcessedContentIndex >= contentBlocks.length) {
187
+ lastProcessedContentIndex = 0;
188
+ }
146
189
  for (let i = lastProcessedContentIndex; i < contentBlocks.length; i++) {
147
190
  const content = contentBlocks[i];
148
191
  if (content.type === "toolCall") {
@@ -192,19 +235,108 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
192
235
  }
193
236
  }
194
237
  }
238
+
239
+ // When the stream adapter signals a completed tool call with an
240
+ // external result (from Claude Code SDK), update the pending
241
+ // ToolExecutionComponent immediately so output is visible in
242
+ // real-time instead of waiting for the session to end.
243
+ if (externalToolResult) {
244
+ const component = host.pendingTools.get(externalToolResult.toolCallId);
245
+ if (component) {
246
+ component.updateResult({
247
+ content: externalToolResult.content,
248
+ details: externalToolResult.details,
249
+ isError: externalToolResult.isError,
250
+ });
251
+ }
252
+ }
253
+
254
+ // Render assistant text/thinking after tool components so mixed
255
+ // streams keep chronological ordering in the chat container.
256
+ const hasToolBlocks = hasAssistantToolBlocks(host.streamingMessage);
257
+ if (!host.streamingComponent && hasVisibleAssistantContent(host.streamingMessage)) {
258
+ host.streamingComponent = new AssistantMessageComponent(
259
+ undefined,
260
+ host.hideThinkingBlock,
261
+ host.getMarkdownThemeWithSettings(),
262
+ host.settingsManager.getTimestampFormat(),
263
+ );
264
+ host.chatContainer.addChild(host.streamingComponent);
265
+ }
266
+ if (host.streamingComponent) {
267
+ if (hasToolBlocks) {
268
+ host.chatContainer.removeChild(host.streamingComponent);
269
+ host.chatContainer.addChild(host.streamingComponent);
270
+ }
271
+ host.streamingComponent.updateContent(host.streamingMessage);
272
+ }
273
+
195
274
  // Update index: fully processed blocks won't need re-scanning.
196
275
  // Keep the last block's index (it may still be accumulating data),
197
276
  // so we re-check it next time but skip all earlier ones.
198
277
  if (contentBlocks.length > 0) {
199
278
  lastProcessedContentIndex = Math.max(0, contentBlocks.length - 1);
200
279
  }
280
+
281
+ // Pinned message: mirror the latest assistant text above the editor
282
+ // when tool executions push it out of the viewport.
283
+ const hasTools = contentBlocks.some(
284
+ (c: any) => c.type === "toolCall" || c.type === "serverToolUse",
285
+ );
286
+ if (hasTools) hasToolsInTurn = true;
287
+
288
+ if (hasToolsInTurn) {
289
+ // Collect the latest text block(s) from the assistant message
290
+ let latestText = "";
291
+ for (let i = contentBlocks.length - 1; i >= 0; i--) {
292
+ const c = contentBlocks[i] as any;
293
+ if (c.type === "text" && c.text?.trim()) {
294
+ latestText = c.text.trim();
295
+ break;
296
+ }
297
+ }
298
+
299
+ if (latestText && latestText !== lastPinnedText) {
300
+ lastPinnedText = latestText;
301
+
302
+ if (!pinnedBorder) {
303
+ // First time: create border + text component
304
+ host.pinnedMessageContainer.clear();
305
+ pinnedBorder = new DynamicBorder(
306
+ (str: string) => theme.fg("dim", str),
307
+ "Working · Latest Output",
308
+ );
309
+ pinnedBorder.startSpinner(host.ui, (str: string) => theme.fg("accent", str));
310
+ host.pinnedMessageContainer.addChild(pinnedBorder);
311
+ pinnedTextComponent = new Markdown(latestText, 1, 0, host.getMarkdownThemeWithSettings());
312
+ // Cap pinned content to ~40% of terminal height so tall output
313
+ // doesn't exceed the viewport and cause render flashing.
314
+ pinnedTextComponent.maxLines = Math.max(3, Math.floor(host.ui.terminal.rows * 0.4));
315
+ host.pinnedMessageContainer.addChild(pinnedTextComponent);
316
+ // Hide the separate status loader — the pinned zone replaces it
317
+ if (host.loadingAnimation) {
318
+ host.loadingAnimation.stop();
319
+ host.loadingAnimation = undefined;
320
+ }
321
+ host.statusContainer.clear();
322
+ } else {
323
+ // Update existing markdown component in-place
324
+ pinnedTextComponent?.setText(latestText);
325
+ // Refresh maxLines in case terminal was resized
326
+ if (pinnedTextComponent) {
327
+ pinnedTextComponent.maxLines = Math.max(3, Math.floor(host.ui.terminal.rows * 0.4));
328
+ }
329
+ }
330
+ }
331
+ }
332
+
201
333
  host.ui.requestRender();
202
334
  }
203
335
  break;
204
336
 
205
337
  case "message_end":
206
338
  if (event.message.role === "user") break;
207
- if (host.streamingComponent && event.message.role === "assistant") {
339
+ if (event.message.role === "assistant") {
208
340
  host.streamingMessage = event.message;
209
341
  let errorMessage: string | undefined;
210
342
  if (host.streamingMessage.stopReason === "aborted") {
@@ -214,13 +346,36 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
214
346
  : "Operation aborted";
215
347
  host.streamingMessage.errorMessage = errorMessage;
216
348
  }
217
- host.streamingComponent.updateContent(host.streamingMessage);
349
+
350
+ const shouldRenderAssistant = hasVisibleAssistantContent(host.streamingMessage)
351
+ || (
352
+ (host.streamingMessage.stopReason === "aborted" || host.streamingMessage.stopReason === "error")
353
+ && !hasAssistantToolBlocks(host.streamingMessage)
354
+ );
355
+ if (!host.streamingComponent && shouldRenderAssistant) {
356
+ host.streamingComponent = new AssistantMessageComponent(
357
+ undefined,
358
+ host.hideThinkingBlock,
359
+ host.getMarkdownThemeWithSettings(),
360
+ host.settingsManager.getTimestampFormat(),
361
+ );
362
+ host.chatContainer.addChild(host.streamingComponent);
363
+ }
364
+ if (host.streamingComponent) {
365
+ host.streamingComponent.updateContent(host.streamingMessage);
366
+ }
367
+
218
368
  if (host.streamingMessage.stopReason === "aborted" || host.streamingMessage.stopReason === "error") {
219
369
  if (!errorMessage) {
220
370
  errorMessage = host.streamingMessage.errorMessage || "Error";
221
371
  }
222
- for (const [, component] of host.pendingTools.entries()) {
223
- component.updateResult({ content: [{ type: "text", text: errorMessage }], isError: true });
372
+ const pendingComponents = Array.from(host.pendingTools.values());
373
+ if (pendingComponents.length > 0) {
374
+ const [first, ...rest] = pendingComponents;
375
+ first.completeWithError(errorMessage);
376
+ for (const component of rest) {
377
+ component.completeWithError();
378
+ }
224
379
  }
225
380
  host.pendingTools.clear();
226
381
  } else {
@@ -230,6 +385,15 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
230
385
  }
231
386
  host.streamingComponent = undefined;
232
387
  host.streamingMessage = undefined;
388
+ // Clear pinned output once the message is finalized in the chat
389
+ // container — prevents duplicate display when the agent continues
390
+ // (e.g. form elicitation) after the assistant message ends.
391
+ if (pinnedBorder) pinnedBorder.stopSpinner();
392
+ host.pinnedMessageContainer.clear();
393
+ lastPinnedText = "";
394
+ hasToolsInTurn = false;
395
+ pinnedBorder = undefined;
396
+ pinnedTextComponent = undefined;
233
397
  host.footer.invalidate();
234
398
  }
235
399
  host.ui.requestRender();
@@ -282,6 +446,16 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
282
446
  host.streamingMessage = undefined;
283
447
  }
284
448
  host.pendingTools.clear();
449
+ // Pinned output is only useful while work is actively streaming.
450
+ // Keep chat history as the single source after completion.
451
+ if (pinnedBorder) {
452
+ pinnedBorder.stopSpinner();
453
+ }
454
+ host.pinnedMessageContainer.clear();
455
+ lastPinnedText = "";
456
+ hasToolsInTurn = false;
457
+ pinnedBorder = undefined;
458
+ pinnedTextComponent = undefined;
285
459
  await host.checkShutdownRequested();
286
460
  host.ui.requestRender();
287
461
  break;
@@ -52,7 +52,12 @@ export async function findExactModelMatch(host: any, searchTerm: string): Promis
52
52
 
53
53
  export async function getModelCandidates(host: any): Promise<Model<any>[]> {
54
54
  if (host.session.scopedModels.length > 0) {
55
- return host.session.scopedModels.map((scoped: any) => scoped.model);
55
+ // Filter scoped models by provider auth readiness so callers like
56
+ // findExactModelMatch can't resolve a scoped-but-unconfigured model.
57
+ const registry = host.session.modelRegistry;
58
+ return host.session.scopedModels
59
+ .filter((scoped: any) => registry.isProviderRequestReady(scoped.model.provider))
60
+ .map((scoped: any) => scoped.model);
56
61
  }
57
62
 
58
63
  host.session.modelRegistry.refresh();
@@ -9,6 +9,7 @@ export interface InteractiveModeStateHost {
9
9
  keybindings: any;
10
10
  statusContainer: any;
11
11
  chatContainer: any;
12
+ pinnedMessageContainer: any;
12
13
  settingsManager: any;
13
14
  pendingTools: Map<string, any>;
14
15
  toolOutputExpanded: boolean;
@@ -168,6 +168,7 @@ export class InteractiveMode {
168
168
  private chatContainer: Container;
169
169
  private pendingMessagesContainer: Container;
170
170
  private statusContainer: Container;
171
+ private pinnedMessageContainer: Container;
171
172
  private defaultEditor: CustomEditor;
172
173
  private editor: EditorComponent;
173
174
  private autocompleteProvider: CombinedAutocompleteProvider | undefined;
@@ -285,6 +286,7 @@ export class InteractiveMode {
285
286
  this.chatContainer = new Container();
286
287
  this.pendingMessagesContainer = new Container();
287
288
  this.statusContainer = new Container();
289
+ this.pinnedMessageContainer = new Container();
288
290
  this.widgetContainerAbove = new Container();
289
291
  this.widgetContainerBelow = new Container();
290
292
  this.keybindings = KeybindingsManager.create();
@@ -490,6 +492,7 @@ export class InteractiveMode {
490
492
  this.ui.addChild(this.chatContainer);
491
493
  this.ui.addChild(this.pendingMessagesContainer);
492
494
  this.ui.addChild(this.statusContainer);
495
+ this.ui.addChild(this.pinnedMessageContainer);
493
496
  this.renderWidgets(); // Initialize with default spacer
494
497
  this.ui.addChild(this.widgetContainerAbove);
495
498
  this.ui.addChild(this.editorContainer);
@@ -1396,7 +1399,19 @@ export class InteractiveMode {
1396
1399
  */
1397
1400
  private renderWidgets(): void {
1398
1401
  if (!this.widgetContainerAbove || !this.widgetContainerBelow) return;
1399
- this.renderWidgetContainer(this.widgetContainerAbove, this.extensionWidgetsAbove, true, true);
1402
+
1403
+ // widgetContainerAbove: spacer collapses when pinned content is visible
1404
+ // so there's no extra blank line between pinned output and the editor border.
1405
+ this.widgetContainerAbove.clear();
1406
+ const pinned = this.pinnedMessageContainer;
1407
+ this.widgetContainerAbove.addChild({
1408
+ render: () => pinned.children.length > 0 ? [] : [""],
1409
+ invalidate: () => {},
1410
+ });
1411
+ for (const component of this.extensionWidgetsAbove.values()) {
1412
+ this.widgetContainerAbove.addChild(component);
1413
+ }
1414
+
1400
1415
  this.renderWidgetContainer(this.widgetContainerBelow, this.extensionWidgetsBelow, false, false);
1401
1416
  this.ui.requestRender();
1402
1417
  }
@@ -1631,7 +1646,7 @@ export class InteractiveMode {
1631
1646
  this.hideExtensionInput();
1632
1647
  resolve(undefined);
1633
1648
  },
1634
- { tui: this.ui, timeout: opts?.timeout },
1649
+ { tui: this.ui, timeout: opts?.timeout, secure: opts?.secure },
1635
1650
  );
1636
1651
 
1637
1652
  this.editorContainer.clear();
@@ -1770,7 +1785,7 @@ export class InteractiveMode {
1770
1785
  } else if (type === "warning") {
1771
1786
  this.showWarning(message);
1772
1787
  } else {
1773
- this.showStatus(message);
1788
+ this.showStatus(message, { append: true });
1774
1789
  }
1775
1790
  }
1776
1791
 
@@ -2037,12 +2052,13 @@ export class InteractiveMode {
2037
2052
  * If multiple status messages are emitted back-to-back (without anything else being added to the chat),
2038
2053
  * we update the previous status line instead of appending new ones to avoid log spam.
2039
2054
  */
2040
- private showStatus(message: string): void {
2055
+ private showStatus(message: string, options?: { append?: boolean }): void {
2056
+ const append = options?.append ?? false;
2041
2057
  const children = this.chatContainer.children;
2042
2058
  const last = children.length > 0 ? children[children.length - 1] : undefined;
2043
2059
  const secondLast = children.length > 1 ? children[children.length - 2] : undefined;
2044
2060
 
2045
- if (last && secondLast && last === this.lastStatusText && secondLast === this.lastStatusSpacer) {
2061
+ if (!append && last && secondLast && last === this.lastStatusText && secondLast === this.lastStatusSpacer) {
2046
2062
  this.lastStatusText.setText(theme.fg("dim", message));
2047
2063
  this.ui.requestRender();
2048
2064
  return;
@@ -2264,6 +2280,7 @@ export class InteractiveMode {
2264
2280
  updateFooter: true,
2265
2281
  populateHistory: true,
2266
2282
  });
2283
+ this.populatePinnedFromMessages(context.messages);
2267
2284
 
2268
2285
  // Show compaction info if session was compacted
2269
2286
  const allEntries = this.sessionManager.getEntries();
@@ -2287,6 +2304,54 @@ export class InteractiveMode {
2287
2304
  this.chatContainer.clear();
2288
2305
  const context = this.sessionManager.buildSessionContext();
2289
2306
  this.renderSessionContext(context);
2307
+ this.populatePinnedFromMessages(context.messages);
2308
+ }
2309
+
2310
+ /**
2311
+ * After rebuilding chat from messages, pin the last assistant text above the
2312
+ * editor if tool results would otherwise push it out of the viewport.
2313
+ */
2314
+ private populatePinnedFromMessages(messages: AgentMessage[]): void {
2315
+ this.pinnedMessageContainer.clear();
2316
+
2317
+ // Walk backwards to find the last assistant message
2318
+ let lastAssistant: AssistantMessage | undefined;
2319
+ for (let i = messages.length - 1; i >= 0; i--) {
2320
+ const msg = messages[i];
2321
+ if (msg && "role" in msg && msg.role === "assistant") {
2322
+ lastAssistant = msg as AssistantMessage;
2323
+ break;
2324
+ }
2325
+ }
2326
+ if (!lastAssistant) return;
2327
+
2328
+ // Check if any tool calls follow the last text block
2329
+ const content = lastAssistant.content;
2330
+ let lastTextIndex = -1;
2331
+ let hasToolAfterText = false;
2332
+ for (let i = 0; i < content.length; i++) {
2333
+ if (content[i].type === "text") lastTextIndex = i;
2334
+ }
2335
+ if (lastTextIndex >= 0) {
2336
+ for (let i = lastTextIndex + 1; i < content.length; i++) {
2337
+ if (content[i].type === "toolCall" || content[i].type === "serverToolUse") {
2338
+ hasToolAfterText = true;
2339
+ break;
2340
+ }
2341
+ }
2342
+ }
2343
+ if (!hasToolAfterText || lastTextIndex < 0) return;
2344
+
2345
+ const textBlock = content[lastTextIndex] as { type: "text"; text: string };
2346
+ const text = textBlock.text?.trim();
2347
+ if (!text) return;
2348
+
2349
+ this.pinnedMessageContainer.addChild(
2350
+ new DynamicBorder((str: string) => theme.fg("dim", str), "Latest Output"),
2351
+ );
2352
+ this.pinnedMessageContainer.addChild(
2353
+ new Markdown(text, 1, 0, this.getMarkdownThemeWithSettings()),
2354
+ );
2290
2355
  }
2291
2356
 
2292
2357
  // =========================================================================
@@ -499,12 +499,14 @@ function handleHotkeysCommand(ctx: SlashCommandContext): void {
499
499
  const suspend = getAppKeyDisplay(ctx.keybindings, "suspend");
500
500
  const cycleThinkingLevel = getAppKeyDisplay(ctx.keybindings, "cycleThinkingLevel");
501
501
  const cycleModelForward = getAppKeyDisplay(ctx.keybindings, "cycleModelForward");
502
+ const cycleModelBackward = getAppKeyDisplay(ctx.keybindings, "cycleModelBackward");
502
503
  const selectModel = getAppKeyDisplay(ctx.keybindings, "selectModel");
503
504
  const expandTools = getAppKeyDisplay(ctx.keybindings, "expandTools");
504
505
  const toggleThinking = getAppKeyDisplay(ctx.keybindings, "toggleThinking");
505
506
  const externalEditor = getAppKeyDisplay(ctx.keybindings, "externalEditor");
506
507
  const followUp = getAppKeyDisplay(ctx.keybindings, "followUp");
507
508
  const dequeue = getAppKeyDisplay(ctx.keybindings, "dequeue");
509
+ const pasteImage = getAppKeyDisplay(ctx.keybindings, "pasteImage");
508
510
 
509
511
  let hotkeys = `
510
512
  **Navigation**
@@ -540,14 +542,14 @@ function handleHotkeysCommand(ctx: SlashCommandContext): void {
540
542
  | \`${exit}\` | Exit (when editor is empty) |
541
543
  | \`${suspend}\` | Suspend to background |
542
544
  | \`${cycleThinkingLevel}\` | Cycle thinking level |
543
- | \`${cycleModelForward}\` | Cycle models |
545
+ | \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
544
546
  | \`${selectModel}\` | Open model selector |
545
547
  | \`${expandTools}\` | Toggle tool output expansion |
546
548
  | \`${toggleThinking}\` | Toggle thinking block visibility |
547
549
  | \`${externalEditor}\` | Edit message in external editor |
548
550
  | \`${followUp}\` | Queue follow-up message |
549
551
  | \`${dequeue}\` | Restore queued messages |
550
- | \`Ctrl+V\` | Paste image from clipboard |
552
+ | \`${pasteImage}\` | Paste image from clipboard |
551
553
  | \`/\` | Slash commands |
552
554
  | \`!\` | Run bash command |
553
555
  | \`!!\` | Run bash command (excluded from context) |
@@ -224,7 +224,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
224
224
  ),
225
225
 
226
226
  input: (title, placeholder, opts) =>
227
- createDialogPromise(opts, undefined, { method: "input", title, placeholder, timeout: opts?.timeout }, (r) =>
227
+ createDialogPromise(opts, undefined, { method: "input", title, placeholder, timeout: opts?.timeout, secure: opts?.secure }, (r) =>
228
228
  "cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined,
229
229
  ),
230
230
 
@@ -291,6 +291,7 @@ export type RpcExtensionUIRequest =
291
291
  title: string;
292
292
  placeholder?: string;
293
293
  timeout?: number;
294
+ secure?: boolean;
294
295
  }
295
296
  | { type: "extension_ui_request"; id: string; method: "editor"; title: string; prefill?: string }
296
297
  | {
@@ -25,5 +25,14 @@ describe("Input", () => {
25
25
  input.focused = false;
26
26
  assert.equal(input.focused, false);
27
27
  });
28
+ it("secure mode obscures typed characters in render output", () => {
29
+ const input = new Input();
30
+ input.secure = true;
31
+ input.focused = true;
32
+ input.handleInput("secret123");
33
+ 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");
36
+ });
28
37
  });
29
38
  //# sourceMappingURL=input.test.js.map
@@ -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;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"]}
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;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"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=markdown-maxlines.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-maxlines.test.d.ts","sourceRoot":"","sources":["../../../src/components/__tests__/markdown-maxlines.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { Markdown } from "../markdown.js";
4
+ function noopTheme() {
5
+ const identity = (text) => text;
6
+ return {
7
+ heading: identity,
8
+ link: identity,
9
+ linkUrl: identity,
10
+ code: identity,
11
+ codeBlock: identity,
12
+ codeBlockBorder: identity,
13
+ quote: identity,
14
+ quoteBorder: identity,
15
+ hr: identity,
16
+ listBullet: identity,
17
+ bold: identity,
18
+ italic: identity,
19
+ strikethrough: identity,
20
+ underline: identity,
21
+ };
22
+ }
23
+ test("Markdown renders all lines when maxLines is not set", () => {
24
+ const text = "Line 1\n\nLine 2\n\nLine 3\n\nLine 4\n\nLine 5";
25
+ const md = new Markdown(text, 0, 0, noopTheme());
26
+ const lines = md.render(80);
27
+ // Each paragraph produces a line + an inter-paragraph blank line
28
+ const contentLines = lines.filter((l) => l.trim().length > 0);
29
+ assert.ok(contentLines.length >= 5, `expected at least 5 content lines, got ${contentLines.length}`);
30
+ });
31
+ test("Markdown truncates from the top when maxLines is exceeded", () => {
32
+ const text = "Line 1\n\nLine 2\n\nLine 3\n\nLine 4\n\nLine 5";
33
+ const md = new Markdown(text, 0, 0, noopTheme());
34
+ md.maxLines = 3;
35
+ const lines = md.render(80);
36
+ assert.ok(lines.length <= 3, `expected at most 3 lines, got ${lines.length}`);
37
+ // First line should be the ellipsis indicator
38
+ assert.ok(lines[0].includes("…"), "first line should contain ellipsis indicator");
39
+ assert.ok(lines[0].includes("above"), "first line should mention lines above");
40
+ });
41
+ test("Markdown preserves most recent content when truncating", () => {
42
+ const text = "First paragraph\n\nSecond paragraph\n\nThird paragraph\n\nFourth paragraph\n\nFifth paragraph";
43
+ const md = new Markdown(text, 0, 0, noopTheme());
44
+ md.maxLines = 3;
45
+ const lines = md.render(80);
46
+ // The last rendered line should contain "Fifth paragraph" (the most recent content)
47
+ const lastContentLine = lines.filter((l) => !l.includes("…")).pop() ?? "";
48
+ assert.ok(lastContentLine.includes("Fifth paragraph"), `expected last content line to contain "Fifth paragraph", got "${lastContentLine}"`);
49
+ });
50
+ test("Markdown does not truncate when content fits within maxLines", () => {
51
+ const text = "Short text";
52
+ const md = new Markdown(text, 0, 0, noopTheme());
53
+ md.maxLines = 10;
54
+ const lines = md.render(80);
55
+ assert.ok(!lines.some((l) => l.includes("…")), "should not contain ellipsis when content fits");
56
+ assert.ok(lines.some((l) => l.includes("Short text")), "should contain the original text");
57
+ });
58
+ test("Markdown trims trailing empty lines", () => {
59
+ const text = "Some text\n\n";
60
+ const md = new Markdown(text, 0, 0, noopTheme());
61
+ const lines = md.render(80);
62
+ // Last line should not be empty (trailing empties are trimmed)
63
+ const lastLine = lines[lines.length - 1];
64
+ assert.ok(lastLine.trim().length > 0 || lines.length === 1, "trailing empty lines should be trimmed");
65
+ });
66
+ //# sourceMappingURL=markdown-maxlines.test.js.map