gsd-pi 2.38.0-dev.eeb3520 → 2.39.0-dev.20aba06

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 (346) hide show
  1. package/README.md +15 -11
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/remote-questions-config.js +2 -2
  8. package/dist/resource-loader.js +100 -3
  9. package/dist/resources/extensions/async-jobs/index.js +10 -0
  10. package/dist/resources/extensions/browser-tools/index.js +3 -1
  11. package/dist/resources/extensions/browser-tools/package.json +3 -1
  12. package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
  13. package/dist/resources/extensions/cmux/index.js +55 -1
  14. package/dist/resources/extensions/context7/package.json +1 -1
  15. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  16. package/dist/resources/extensions/github-sync/cli.js +284 -0
  17. package/dist/resources/extensions/github-sync/index.js +73 -0
  18. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  19. package/dist/resources/extensions/github-sync/sync.js +424 -0
  20. package/dist/resources/extensions/github-sync/templates.js +118 -0
  21. package/dist/resources/extensions/github-sync/types.js +7 -0
  22. package/dist/resources/extensions/google-search/package.json +3 -1
  23. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  24. package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
  25. package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
  26. package/dist/resources/extensions/gsd/auto-loop.js +923 -787
  27. package/dist/resources/extensions/gsd/auto-post-unit.js +107 -70
  28. package/dist/resources/extensions/gsd/auto-prompts.js +205 -51
  29. package/dist/resources/extensions/gsd/auto-start.js +19 -3
  30. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  31. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  32. package/dist/resources/extensions/gsd/auto.js +149 -100
  33. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
  37. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
  38. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
  39. package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
  40. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
  41. package/dist/resources/extensions/gsd/captures.js +9 -1
  42. package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
  43. package/dist/resources/extensions/gsd/commands/context.js +84 -0
  44. package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
  45. package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
  48. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
  49. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
  50. package/dist/resources/extensions/gsd/commands/index.js +11 -0
  51. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  52. package/dist/resources/extensions/gsd/commands-handlers.js +17 -4
  53. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  54. package/dist/resources/extensions/gsd/commands.js +8 -1169
  55. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  56. package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
  57. package/dist/resources/extensions/gsd/detection.js +1 -2
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  59. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  60. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  61. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  62. package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
  63. package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
  64. package/dist/resources/extensions/gsd/doctor.js +234 -12
  65. package/dist/resources/extensions/gsd/env-utils.js +29 -0
  66. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  67. package/dist/resources/extensions/gsd/export-html.js +46 -0
  68. package/dist/resources/extensions/gsd/export.js +1 -1
  69. package/dist/resources/extensions/gsd/files.js +48 -9
  70. package/dist/resources/extensions/gsd/forensics.js +1 -1
  71. package/dist/resources/extensions/gsd/git-service.js +30 -12
  72. package/dist/resources/extensions/gsd/gitignore.js +16 -3
  73. package/dist/resources/extensions/gsd/guided-flow.js +149 -38
  74. package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
  75. package/dist/resources/extensions/gsd/health-widget.js +4 -87
  76. package/dist/resources/extensions/gsd/index.js +4 -1111
  77. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  78. package/dist/resources/extensions/gsd/migrate-external.js +18 -1
  79. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  80. package/dist/resources/extensions/gsd/package.json +1 -1
  81. package/dist/resources/extensions/gsd/paths.js +3 -0
  82. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  83. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  84. package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
  85. package/dist/resources/extensions/gsd/preferences.js +22 -11
  86. package/dist/resources/extensions/gsd/progress-score.js +20 -1
  87. package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
  88. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  89. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  90. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  91. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
  92. package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
  93. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  94. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  95. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  96. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  97. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  98. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  99. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  100. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  101. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  102. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  103. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  104. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  105. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  106. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  107. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  108. package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
  109. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  110. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  111. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  112. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  113. package/dist/resources/extensions/gsd/state.js +42 -23
  114. package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
  115. package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
  116. package/dist/resources/extensions/gsd/visualizer-data.js +27 -2
  117. package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
  118. package/dist/resources/extensions/gsd/worktree.js +35 -16
  119. package/dist/resources/extensions/mcp-client/index.js +14 -1
  120. package/dist/resources/extensions/remote-questions/status.js +4 -1
  121. package/dist/resources/extensions/remote-questions/store.js +4 -1
  122. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  123. package/dist/resources/extensions/shared/frontmatter.js +1 -1
  124. package/dist/resources/extensions/subagent/index.js +12 -3
  125. package/dist/resources/extensions/subagent/isolation.js +2 -1
  126. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  127. package/dist/resources/extensions/universal-config/package.json +1 -1
  128. package/dist/welcome-screen.d.ts +13 -0
  129. package/dist/welcome-screen.js +97 -0
  130. package/package.json +1 -1
  131. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  132. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  133. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  134. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  135. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/agent-session.js +107 -24
  137. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  140. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  143. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
  145. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
  147. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
  148. package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
  149. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/skills.js +8 -2
  151. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  153. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  154. package/packages/pi-coding-agent/dist/index.js +1 -1
  155. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
  167. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
  169. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -457
  179. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  180. package/packages/pi-coding-agent/package.json +1 -1
  181. package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
  182. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  183. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  184. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
  185. package/packages/pi-coding-agent/src/core/skills.ts +11 -2
  186. package/packages/pi-coding-agent/src/index.ts +1 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
  189. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
  191. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
  192. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
  193. package/pkg/package.json +1 -1
  194. package/src/resources/extensions/async-jobs/index.ts +11 -0
  195. package/src/resources/extensions/browser-tools/index.ts +3 -0
  196. package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
  197. package/src/resources/extensions/cmux/index.ts +57 -1
  198. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  199. package/src/resources/extensions/github-sync/cli.ts +364 -0
  200. package/src/resources/extensions/github-sync/index.ts +93 -0
  201. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  202. package/src/resources/extensions/github-sync/sync.ts +556 -0
  203. package/src/resources/extensions/github-sync/templates.ts +183 -0
  204. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  205. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  206. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  208. package/src/resources/extensions/github-sync/types.ts +47 -0
  209. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  210. package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
  211. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
  212. package/src/resources/extensions/gsd/auto-loop.ts +1285 -1138
  213. package/src/resources/extensions/gsd/auto-post-unit.ts +90 -46
  214. package/src/resources/extensions/gsd/auto-prompts.ts +250 -53
  215. package/src/resources/extensions/gsd/auto-start.ts +24 -3
  216. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  217. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  218. package/src/resources/extensions/gsd/auto.ts +152 -111
  219. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
  220. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
  221. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
  222. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
  225. package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
  226. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
  227. package/src/resources/extensions/gsd/captures.ts +10 -1
  228. package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
  229. package/src/resources/extensions/gsd/commands/context.ts +101 -0
  230. package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
  231. package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
  232. package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
  233. package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
  234. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
  235. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
  236. package/src/resources/extensions/gsd/commands/index.ts +14 -0
  237. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -3
  239. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  240. package/src/resources/extensions/gsd/commands.ts +10 -1307
  241. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
  243. package/src/resources/extensions/gsd/detection.ts +2 -2
  244. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  245. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  246. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  247. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  248. package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
  249. package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
  250. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  251. package/src/resources/extensions/gsd/doctor.ts +243 -14
  252. package/src/resources/extensions/gsd/env-utils.ts +31 -0
  253. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  254. package/src/resources/extensions/gsd/export-html.ts +51 -0
  255. package/src/resources/extensions/gsd/export.ts +1 -1
  256. package/src/resources/extensions/gsd/files.ts +51 -11
  257. package/src/resources/extensions/gsd/forensics.ts +1 -1
  258. package/src/resources/extensions/gsd/git-service.ts +44 -10
  259. package/src/resources/extensions/gsd/gitignore.ts +17 -3
  260. package/src/resources/extensions/gsd/guided-flow.ts +177 -44
  261. package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
  262. package/src/resources/extensions/gsd/health-widget.ts +4 -89
  263. package/src/resources/extensions/gsd/index.ts +12 -1307
  264. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  265. package/src/resources/extensions/gsd/migrate-external.ts +18 -1
  266. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  267. package/src/resources/extensions/gsd/paths.ts +4 -0
  268. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  269. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  270. package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
  271. package/src/resources/extensions/gsd/preferences.ts +25 -11
  272. package/src/resources/extensions/gsd/progress-score.ts +23 -0
  273. package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
  274. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  275. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  276. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  277. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
  278. package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
  279. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  280. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  281. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  282. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  283. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  284. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  285. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  286. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  287. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  288. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  289. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  290. package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  291. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  292. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  294. package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
  295. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  296. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  297. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  298. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  299. package/src/resources/extensions/gsd/state.ts +39 -21
  300. package/src/resources/extensions/gsd/templates/runtime.md +21 -0
  301. package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
  302. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  303. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -77
  304. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
  305. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  306. package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
  307. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  308. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
  309. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  310. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
  311. package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
  312. package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
  313. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
  314. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  315. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  316. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
  317. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  318. package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
  319. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
  320. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
  321. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  322. package/src/resources/extensions/gsd/types.ts +18 -1
  323. package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
  324. package/src/resources/extensions/gsd/visualizer-data.ts +52 -2
  325. package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
  326. package/src/resources/extensions/gsd/worktree.ts +35 -15
  327. package/src/resources/extensions/mcp-client/index.ts +17 -1
  328. package/src/resources/extensions/remote-questions/status.ts +5 -1
  329. package/src/resources/extensions/remote-questions/store.ts +5 -1
  330. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  331. package/src/resources/extensions/shared/frontmatter.ts +1 -1
  332. package/src/resources/extensions/subagent/index.ts +12 -3
  333. package/src/resources/extensions/subagent/isolation.ts +3 -1
  334. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  335. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  336. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  337. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  338. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  339. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  340. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  341. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  342. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  343. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  344. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  345. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  346. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -89,6 +89,15 @@ import { TreeSelectorComponent } from "./components/tree-selector.js";
89
89
  import { UserMessageComponent } from "./components/user-message.js";
90
90
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
91
91
  import { type SlashCommandContext, dispatchSlashCommand, getAppKeyDisplay } from "./slash-command-handlers.js";
92
+ import { handleAgentEvent } from "./controllers/chat-controller.js";
93
+ import { createExtensionUIContext as buildExtensionUIContext } from "./controllers/extension-ui-controller.js";
94
+ import { setupEditorSubmitHandler as setupEditorSubmitHandlerController } from "./controllers/input-controller.js";
95
+ import {
96
+ findExactModelMatch as findExactModelMatchController,
97
+ getModelCandidates as getModelCandidatesController,
98
+ handleModelCommand as handleModelCommandController,
99
+ updateAvailableProviderCount as updateAvailableProviderCountController,
100
+ } from "./controllers/model-controller.js";
92
101
  import {
93
102
  getAvailableThemes,
94
103
  getAvailableThemesWithPaths,
@@ -1175,12 +1184,10 @@ export class InteractiveMode {
1175
1184
  }
1176
1185
 
1177
1186
  /**
1178
- * Get a registered tool definition by name (for custom rendering).
1187
+ * Get a tool definition by name (for custom rendering).
1179
1188
  */
1180
1189
  private getRegisteredToolDefinition(toolName: string) {
1181
- const tools = this.session.extensionRunner?.getAllRegisteredTools() ?? [];
1182
- const registeredTool = tools.find((t) => t.definition.name === toolName);
1183
- return registeredTool?.definition;
1190
+ return this.session.getRenderableToolDefinition(toolName);
1184
1191
  }
1185
1192
 
1186
1193
  /**
@@ -1486,60 +1493,7 @@ export class InteractiveMode {
1486
1493
  * Create the ExtensionUIContext for extensions.
1487
1494
  */
1488
1495
  private createExtensionUIContext(): ExtensionUIContext {
1489
- return {
1490
- select: (title, options, opts) => this.showExtensionSelector(title, options, opts),
1491
- confirm: (title, message, opts) => this.showExtensionConfirm(title, message, opts),
1492
- input: (title, placeholder, opts) => this.showExtensionInput(title, placeholder, opts),
1493
- notify: (message, type) => this.showExtensionNotify(message, type),
1494
- onTerminalInput: (handler) => this.addExtensionTerminalInputListener(handler),
1495
- setStatus: (key, text) => this.setExtensionStatus(key, text),
1496
- setWorkingMessage: (message) => {
1497
- if (this.loadingAnimation) {
1498
- if (message) {
1499
- this.loadingAnimation.setMessage(message);
1500
- } else {
1501
- this.loadingAnimation.setMessage(
1502
- `${this.defaultWorkingMessage} (${appKey(this.keybindings, "interrupt")} to interrupt)`,
1503
- );
1504
- }
1505
- } else {
1506
- // Queue message for when loadingAnimation is created (handles agent_start race)
1507
- this.pendingWorkingMessage = message;
1508
- }
1509
- },
1510
- setWidget: (key, content, options) => this.setExtensionWidget(key, content, options),
1511
- setFooter: (factory) => this.setExtensionFooter(factory),
1512
- setHeader: (factory) => this.setExtensionHeader(factory),
1513
- setTitle: (title) => this.ui.terminal.setTitle(title),
1514
- custom: (factory, options) => this.showExtensionCustom(factory, options),
1515
- pasteToEditor: (text) => this.editor.handleInput(`\x1b[200~${text}\x1b[201~`),
1516
- setEditorText: (text) => this.editor.setText(text),
1517
- getEditorText: () => this.editor.getText(),
1518
- editor: (title, prefill) => this.showExtensionEditor(title, prefill),
1519
- setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
1520
- get theme() {
1521
- return theme;
1522
- },
1523
- getAllThemes: () => getAvailableThemesWithPaths(),
1524
- getTheme: (name) => getThemeByName(name),
1525
- setTheme: (themeOrName) => {
1526
- if (themeOrName instanceof Theme) {
1527
- setThemeInstance(themeOrName);
1528
- this.ui.requestRender();
1529
- return { success: true };
1530
- }
1531
- const result = setTheme(themeOrName, true);
1532
- if (result.success) {
1533
- if (this.settingsManager.getTheme() !== themeOrName) {
1534
- this.settingsManager.setTheme(themeOrName);
1535
- }
1536
- this.ui.requestRender();
1537
- }
1538
- return result;
1539
- },
1540
- getToolsExpanded: () => this.toolOutputExpanded,
1541
- setToolsExpanded: (expanded) => this.setToolsExpanded(expanded),
1542
- };
1496
+ return buildExtensionUIContext(this);
1543
1497
  }
1544
1498
 
1545
1499
  /**
@@ -2017,69 +1971,7 @@ export class InteractiveMode {
2017
1971
  }
2018
1972
 
2019
1973
  private setupEditorSubmitHandler(): void {
2020
- this.defaultEditor.onSubmit = async (text: string) => {
2021
- text = text.trim();
2022
- if (!text) return;
2023
-
2024
- // Handle slash commands
2025
- if (text.startsWith("/")) {
2026
- const handled = await dispatchSlashCommand(text, this.getSlashCommandContext());
2027
- if (handled) {
2028
- this.editor.setText("");
2029
- return;
2030
- }
2031
- }
2032
-
2033
- // Handle bash command (! for normal, !! for excluded from context)
2034
- if (text.startsWith("!")) {
2035
- const isExcluded = text.startsWith("!!");
2036
- const command = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
2037
- if (command) {
2038
- if (this.session.isBashRunning) {
2039
- this.showWarning("A bash command is already running. Press Esc to cancel it first.");
2040
- this.editor.setText(text);
2041
- return;
2042
- }
2043
- this.editor.addToHistory?.(text);
2044
- await this.handleBashCommand(command, isExcluded);
2045
- this.isBashMode = false;
2046
- this.updateEditorBorderColor();
2047
- return;
2048
- }
2049
- }
2050
-
2051
- // Queue input during compaction (extension commands execute immediately)
2052
- if (this.session.isCompacting) {
2053
- if (this.isExtensionCommand(text)) {
2054
- this.editor.addToHistory?.(text);
2055
- this.editor.setText("");
2056
- await this.session.prompt(text);
2057
- } else {
2058
- this.queueCompactionMessage(text, "steer");
2059
- }
2060
- return;
2061
- }
2062
-
2063
- // If streaming, use prompt() with steer behavior
2064
- // This handles extension commands (execute immediately), prompt template expansion, and queueing
2065
- if (this.session.isStreaming) {
2066
- this.editor.addToHistory?.(text);
2067
- this.editor.setText("");
2068
- await this.session.prompt(text, { streamingBehavior: "steer" });
2069
- this.updatePendingMessagesDisplay();
2070
- this.ui.requestRender();
2071
- return;
2072
- }
2073
-
2074
- // Normal message submission
2075
- // First, move any pending bash components to chat
2076
- this.flushPendingBashComponents();
2077
-
2078
- if (this.onInputCallback) {
2079
- this.onInputCallback(text);
2080
- }
2081
- this.editor.addToHistory?.(text);
2082
- };
1974
+ setupEditorSubmitHandlerController(this as any);
2083
1975
  }
2084
1976
 
2085
1977
  private subscribeToAgent(): void {
@@ -2089,338 +1981,7 @@ export class InteractiveMode {
2089
1981
  }
2090
1982
 
2091
1983
  private async handleEvent(event: AgentSessionEvent): Promise<void> {
2092
- if (!this.isInitialized) {
2093
- await this.init();
2094
- }
2095
-
2096
- this.footer.invalidate();
2097
-
2098
- switch (event.type) {
2099
- case "agent_start":
2100
- // Restore main escape handler if retry handler is still active
2101
- // (retry success event fires later, but we need main handler now)
2102
- if (this.retryEscapeHandler) {
2103
- this.defaultEditor.onEscape = this.retryEscapeHandler;
2104
- this.retryEscapeHandler = undefined;
2105
- }
2106
- if (this.retryLoader) {
2107
- this.retryLoader.stop();
2108
- this.retryLoader = undefined;
2109
- }
2110
- if (this.loadingAnimation) {
2111
- this.loadingAnimation.stop();
2112
- }
2113
- this.statusContainer.clear();
2114
- this.loadingAnimation = new Loader(
2115
- this.ui,
2116
- (spinner) => theme.fg("accent", spinner),
2117
- (text) => theme.fg("muted", text),
2118
- this.defaultWorkingMessage,
2119
- );
2120
- this.statusContainer.addChild(this.loadingAnimation);
2121
- // Apply any pending working message queued before loader existed
2122
- if (this.pendingWorkingMessage !== undefined) {
2123
- if (this.pendingWorkingMessage) {
2124
- this.loadingAnimation.setMessage(this.pendingWorkingMessage);
2125
- }
2126
- this.pendingWorkingMessage = undefined;
2127
- }
2128
- this.ui.requestRender();
2129
- break;
2130
-
2131
- case "message_start":
2132
- if (event.message.role === "custom") {
2133
- this.addMessageToChat(event.message);
2134
- this.ui.requestRender();
2135
- } else if (event.message.role === "user") {
2136
- this.addMessageToChat(event.message);
2137
- this.updatePendingMessagesDisplay();
2138
- this.ui.requestRender();
2139
- } else if (event.message.role === "assistant") {
2140
- this.streamingComponent = new AssistantMessageComponent(
2141
- undefined,
2142
- this.hideThinkingBlock,
2143
- this.getMarkdownThemeWithSettings(),
2144
- );
2145
- this.streamingMessage = event.message;
2146
- this.chatContainer.addChild(this.streamingComponent);
2147
- this.streamingComponent.updateContent(this.streamingMessage);
2148
- this.ui.requestRender();
2149
- }
2150
- break;
2151
-
2152
- case "message_update":
2153
- if (this.streamingComponent && event.message.role === "assistant") {
2154
- this.streamingMessage = event.message;
2155
- this.streamingComponent.updateContent(this.streamingMessage);
2156
-
2157
- for (const content of this.streamingMessage.content) {
2158
- if (content.type === "toolCall") {
2159
- if (!this.pendingTools.has(content.id)) {
2160
- const component = new ToolExecutionComponent(
2161
- content.name,
2162
- content.arguments,
2163
- {
2164
- showImages: this.settingsManager.getShowImages(),
2165
- },
2166
- this.getRegisteredToolDefinition(content.name),
2167
- this.ui,
2168
- );
2169
- component.setExpanded(this.toolOutputExpanded);
2170
- this.chatContainer.addChild(component);
2171
- this.pendingTools.set(content.id, component);
2172
- } else {
2173
- const component = this.pendingTools.get(content.id);
2174
- if (component) {
2175
- component.updateArgs(content.arguments);
2176
- }
2177
- }
2178
- } else if (content.type === "serverToolUse") {
2179
- // Server-side tool (e.g., native web search) — show as pending tool execution
2180
- if (!this.pendingTools.has(content.id)) {
2181
- const component = new ToolExecutionComponent(
2182
- content.name,
2183
- content.input ?? {},
2184
- {
2185
- showImages: this.settingsManager.getShowImages(),
2186
- },
2187
- undefined,
2188
- this.ui,
2189
- );
2190
- component.setExpanded(this.toolOutputExpanded);
2191
- this.chatContainer.addChild(component);
2192
- this.pendingTools.set(content.id, component);
2193
- }
2194
- } else if (content.type === "webSearchResult") {
2195
- // Server-side tool result — resolve the pending server tool execution
2196
- const component = this.pendingTools.get(content.toolUseId);
2197
- if (component) {
2198
- const searchContent = content.content;
2199
- const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
2200
- const resultText = this.formatWebSearchResult(searchContent);
2201
- component.updateResult({
2202
- content: [{ type: "text", text: resultText }],
2203
- isError: !!isError,
2204
- });
2205
- this.pendingTools.delete(content.toolUseId);
2206
- }
2207
- }
2208
- }
2209
- this.ui.requestRender();
2210
- }
2211
- break;
2212
-
2213
- case "message_end":
2214
- if (event.message.role === "user") break;
2215
- if (this.streamingComponent && event.message.role === "assistant") {
2216
- this.streamingMessage = event.message;
2217
- let errorMessage: string | undefined;
2218
- if (this.streamingMessage.stopReason === "aborted") {
2219
- const retryAttempt = this.session.retryAttempt;
2220
- errorMessage =
2221
- retryAttempt > 0
2222
- ? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
2223
- : "Operation aborted";
2224
- this.streamingMessage.errorMessage = errorMessage;
2225
- }
2226
- this.streamingComponent.updateContent(this.streamingMessage);
2227
-
2228
- if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
2229
- if (!errorMessage) {
2230
- errorMessage = this.streamingMessage.errorMessage || "Error";
2231
- }
2232
- for (const [, component] of this.pendingTools.entries()) {
2233
- component.updateResult({
2234
- content: [{ type: "text", text: errorMessage }],
2235
- isError: true,
2236
- });
2237
- }
2238
- this.pendingTools.clear();
2239
- } else {
2240
- // Args are now complete - trigger diff computation for edit tools
2241
- for (const [, component] of this.pendingTools.entries()) {
2242
- component.setArgsComplete();
2243
- }
2244
- }
2245
- this.streamingComponent = undefined;
2246
- this.streamingMessage = undefined;
2247
- this.footer.invalidate();
2248
- }
2249
- this.ui.requestRender();
2250
- break;
2251
-
2252
- case "tool_execution_start": {
2253
- if (!this.pendingTools.has(event.toolCallId)) {
2254
- const component = new ToolExecutionComponent(
2255
- event.toolName,
2256
- event.args,
2257
- {
2258
- showImages: this.settingsManager.getShowImages(),
2259
- },
2260
- this.getRegisteredToolDefinition(event.toolName),
2261
- this.ui,
2262
- );
2263
- component.setExpanded(this.toolOutputExpanded);
2264
- this.chatContainer.addChild(component);
2265
- this.pendingTools.set(event.toolCallId, component);
2266
- this.ui.requestRender();
2267
- }
2268
- break;
2269
- }
2270
-
2271
- case "tool_execution_update": {
2272
- const component = this.pendingTools.get(event.toolCallId);
2273
- if (component) {
2274
- component.updateResult({ ...event.partialResult, isError: false }, true);
2275
- this.ui.requestRender();
2276
- }
2277
- break;
2278
- }
2279
-
2280
- case "tool_execution_end": {
2281
- const component = this.pendingTools.get(event.toolCallId);
2282
- if (component) {
2283
- component.updateResult({ ...event.result, isError: event.isError });
2284
- this.pendingTools.delete(event.toolCallId);
2285
- this.ui.requestRender();
2286
- }
2287
- break;
2288
- }
2289
-
2290
- case "agent_end":
2291
- if (this.loadingAnimation) {
2292
- this.loadingAnimation.stop();
2293
- this.loadingAnimation = undefined;
2294
- this.statusContainer.clear();
2295
- }
2296
- if (this.streamingComponent) {
2297
- this.chatContainer.removeChild(this.streamingComponent);
2298
- this.streamingComponent = undefined;
2299
- this.streamingMessage = undefined;
2300
- }
2301
- this.pendingTools.clear();
2302
-
2303
- await this.checkShutdownRequested();
2304
-
2305
- this.ui.requestRender();
2306
- break;
2307
-
2308
- case "auto_compaction_start": {
2309
- // Keep editor active; submissions are queued during compaction.
2310
- // Set up escape to abort auto-compaction
2311
- this.autoCompactionEscapeHandler = this.defaultEditor.onEscape;
2312
- this.defaultEditor.onEscape = () => {
2313
- this.session.abortCompaction();
2314
- };
2315
- // Show compacting indicator with reason
2316
- this.statusContainer.clear();
2317
- const reasonText = event.reason === "overflow" ? "Context overflow detected, " : "";
2318
- this.autoCompactionLoader = new Loader(
2319
- this.ui,
2320
- (spinner) => theme.fg("accent", spinner),
2321
- (text) => theme.fg("muted", text),
2322
- `${reasonText}Auto-compacting... (${appKey(this.keybindings, "interrupt")} to cancel)`,
2323
- );
2324
- this.statusContainer.addChild(this.autoCompactionLoader);
2325
- this.ui.requestRender();
2326
- break;
2327
- }
2328
-
2329
- case "auto_compaction_end": {
2330
- // Restore escape handler
2331
- if (this.autoCompactionEscapeHandler) {
2332
- this.defaultEditor.onEscape = this.autoCompactionEscapeHandler;
2333
- this.autoCompactionEscapeHandler = undefined;
2334
- }
2335
- // Stop loader
2336
- if (this.autoCompactionLoader) {
2337
- this.autoCompactionLoader.stop();
2338
- this.autoCompactionLoader = undefined;
2339
- this.statusContainer.clear();
2340
- }
2341
- // Handle result
2342
- if (event.aborted) {
2343
- this.showStatus("Auto-compaction cancelled");
2344
- } else if (event.result) {
2345
- // Rebuild chat to show compacted state
2346
- this.chatContainer.clear();
2347
- this.rebuildChatFromMessages();
2348
- // Add compaction component at bottom so user sees it without scrolling
2349
- this.addMessageToChat({
2350
- role: "compactionSummary",
2351
- tokensBefore: event.result.tokensBefore,
2352
- summary: event.result.summary,
2353
- timestamp: Date.now(),
2354
- });
2355
- this.footer.invalidate();
2356
- } else if (event.errorMessage) {
2357
- // Compaction failed (e.g., quota exceeded, API error)
2358
- this.chatContainer.addChild(new Spacer(1));
2359
- this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
2360
- }
2361
- void this.flushCompactionQueue({ willRetry: event.willRetry });
2362
- this.ui.requestRender();
2363
- break;
2364
- }
2365
-
2366
- case "auto_retry_start": {
2367
- // Set up escape to abort retry
2368
- this.retryEscapeHandler = this.defaultEditor.onEscape;
2369
- this.defaultEditor.onEscape = () => {
2370
- this.session.abortRetry();
2371
- };
2372
- // Show retry indicator
2373
- this.statusContainer.clear();
2374
- const delaySeconds = Math.round(event.delayMs / 1000);
2375
- this.retryLoader = new Loader(
2376
- this.ui,
2377
- (spinner) => theme.fg("warning", spinner),
2378
- (text) => theme.fg("muted", text),
2379
- `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (${appKey(this.keybindings, "interrupt")} to cancel)`,
2380
- );
2381
- this.statusContainer.addChild(this.retryLoader);
2382
- this.ui.requestRender();
2383
- break;
2384
- }
2385
-
2386
- case "auto_retry_end": {
2387
- // Restore escape handler
2388
- if (this.retryEscapeHandler) {
2389
- this.defaultEditor.onEscape = this.retryEscapeHandler;
2390
- this.retryEscapeHandler = undefined;
2391
- }
2392
- // Stop loader
2393
- if (this.retryLoader) {
2394
- this.retryLoader.stop();
2395
- this.retryLoader = undefined;
2396
- this.statusContainer.clear();
2397
- }
2398
- // Show error only on final failure (success shows normal response)
2399
- if (!event.success) {
2400
- this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
2401
- }
2402
- this.ui.requestRender();
2403
- break;
2404
- }
2405
-
2406
- case "fallback_provider_switch": {
2407
- this.showStatus(`Switched from ${event.from} → ${event.to} (${event.reason})`);
2408
- this.ui.requestRender();
2409
- break;
2410
- }
2411
-
2412
- case "fallback_provider_restored": {
2413
- this.showStatus(`Restored to ${event.provider}`);
2414
- this.ui.requestRender();
2415
- break;
2416
- }
2417
-
2418
- case "fallback_chain_exhausted": {
2419
- this.showError(event.reason);
2420
- this.ui.requestRender();
2421
- break;
2422
- }
2423
- }
1984
+ await handleAgentEvent(this as any, event);
2424
1985
  }
2425
1986
 
2426
1987
  /** Extract text content from a user message */
@@ -3299,73 +2860,20 @@ export class InteractiveMode {
3299
2860
  }
3300
2861
 
3301
2862
  private async handleModelCommand(searchTerm?: string): Promise<void> {
3302
- if (!searchTerm) {
3303
- this.showModelSelector();
3304
- return;
3305
- }
3306
-
3307
- const model = await this.findExactModelMatch(searchTerm);
3308
- if (model) {
3309
- try {
3310
- await this.session.setModel(model);
3311
- this.footer.invalidate();
3312
- this.updateEditorBorderColor();
3313
- this.showStatus(`Model: ${model.id}`);
3314
- this.checkDaxnutsEasterEgg(model);
3315
- } catch (error) {
3316
- this.showError(error instanceof Error ? error.message : String(error));
3317
- }
3318
- return;
3319
- }
3320
-
3321
- this.showModelSelector(searchTerm);
2863
+ await handleModelCommandController(this, searchTerm);
3322
2864
  }
3323
2865
 
3324
2866
  private async findExactModelMatch(searchTerm: string): Promise<Model<any> | undefined> {
3325
- const term = searchTerm.trim();
3326
- if (!term) return undefined;
3327
-
3328
- let targetProvider: string | undefined;
3329
- let targetModelId = "";
3330
-
3331
- if (term.includes("/")) {
3332
- const parts = term.split("/", 2);
3333
- targetProvider = parts[0]?.trim().toLowerCase();
3334
- targetModelId = parts[1]?.trim().toLowerCase() ?? "";
3335
- } else {
3336
- targetModelId = term.toLowerCase();
3337
- }
3338
-
3339
- if (!targetModelId) return undefined;
3340
-
3341
- const models = await this.getModelCandidates();
3342
- const exactMatches = models.filter((item) => {
3343
- const idMatch = item.id.toLowerCase() === targetModelId;
3344
- const providerMatch = !targetProvider || item.provider.toLowerCase() === targetProvider;
3345
- return idMatch && providerMatch;
3346
- });
3347
-
3348
- return exactMatches.length === 1 ? exactMatches[0] : undefined;
2867
+ return findExactModelMatchController(this, searchTerm);
3349
2868
  }
3350
2869
 
3351
2870
  private async getModelCandidates(): Promise<Model<any>[]> {
3352
- if (this.session.scopedModels.length > 0) {
3353
- return this.session.scopedModels.map((scoped) => scoped.model);
3354
- }
3355
-
3356
- this.session.modelRegistry.refresh();
3357
- try {
3358
- return await this.session.modelRegistry.getAvailable();
3359
- } catch {
3360
- return [];
3361
- }
2871
+ return getModelCandidatesController(this);
3362
2872
  }
3363
2873
 
3364
2874
  /** Update the footer's available provider count from current model candidates */
3365
2875
  private async updateAvailableProviderCount(): Promise<void> {
3366
- const models = await this.getModelCandidates();
3367
- const uniqueProviders = new Set(models.map((m) => m.provider));
3368
- this.footerDataProvider.setAvailableProviderCount(uniqueProviders.size);
2876
+ await updateAvailableProviderCountController(this);
3369
2877
  }
3370
2878
 
3371
2879
  private showModelSelector(initialSearchInput?: string): void {
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.38.0",
3
+ "version": "2.39.0",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -78,6 +78,17 @@ export default function AsyncJobs(pi: ExtensionAPI) {
78
78
  });
79
79
  });
80
80
 
81
+ pi.on("session_before_switch", async () => {
82
+ if (manager) {
83
+ // Cancel all running background jobs — their results are no longer
84
+ // relevant to the new session and would produce wasteful follow-up
85
+ // notifications that trigger empty LLM turns (#1642).
86
+ for (const job of manager.getRunningJobs()) {
87
+ manager.cancel(job.id);
88
+ }
89
+ }
90
+ });
91
+
81
92
  pi.on("session_shutdown", async () => {
82
93
  if (manager) {
83
94
  manager.shutdown();
@@ -33,6 +33,7 @@ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
33
33
  codegen,
34
34
  actionCache,
35
35
  injectionDetection,
36
+ verify,
36
37
  ] = await Promise.all([
37
38
  importExtensionModule<typeof import("./lifecycle.js")>(import.meta.url, "./lifecycle.js"),
38
39
  importExtensionModule<typeof import("./capture.js")>(import.meta.url, "./capture.js"),
@@ -60,6 +61,7 @@ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
60
61
  importExtensionModule<typeof import("./tools/codegen.js")>(import.meta.url, "./tools/codegen.js"),
61
62
  importExtensionModule<typeof import("./tools/action-cache.js")>(import.meta.url, "./tools/action-cache.js"),
62
63
  importExtensionModule<typeof import("./tools/injection-detect.js")>(import.meta.url, "./tools/injection-detect.js"),
64
+ importExtensionModule<typeof import("./tools/verify.js")>(import.meta.url, "./tools/verify.js"),
63
65
  ]);
64
66
 
65
67
  const deps = {
@@ -132,6 +134,7 @@ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
132
134
  codegen.registerCodegenTools(pi, deps);
133
135
  actionCache.registerActionCacheTools(pi, deps);
134
136
  injectionDetection.registerInjectionDetectionTools(pi, deps);
137
+ verify.registerVerifyTools(pi, deps);
135
138
  })().catch((error) => {
136
139
  registrationPromise = null;
137
140
  throw error;