lsd-pi 1.2.4 → 1.3.6

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 (357) hide show
  1. package/README.md +22 -16
  2. package/dist/app-paths.d.ts +4 -0
  3. package/dist/app-paths.js +4 -0
  4. package/dist/bedrock-auth.d.ts +4 -0
  5. package/dist/bedrock-auth.js +4 -0
  6. package/dist/bundled-extension-paths.d.ts +4 -0
  7. package/dist/bundled-extension-paths.js +4 -0
  8. package/dist/cli-theme.d.ts +2 -2
  9. package/dist/cli-theme.js +13 -14
  10. package/dist/cli.js +43 -3
  11. package/dist/codex-rotate-settings.d.ts +4 -0
  12. package/dist/codex-rotate-settings.js +4 -0
  13. package/dist/help-text.d.ts +4 -0
  14. package/dist/help-text.js +4 -0
  15. package/dist/lsd-brand.d.ts +4 -0
  16. package/dist/lsd-brand.js +4 -0
  17. package/dist/onboarding-llm.d.ts +5 -0
  18. package/dist/onboarding-llm.js +5 -0
  19. package/dist/project-sessions.d.ts +4 -0
  20. package/dist/project-sessions.js +4 -0
  21. package/dist/resources/agents/generic.md +1 -0
  22. package/dist/resources/agents/scout.md +8 -1
  23. package/dist/resources/agents/worker.md +1 -0
  24. package/dist/resources/extensions/ask-user-questions.js +70 -0
  25. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +6 -16
  26. package/dist/resources/extensions/browser-tools/tools/codegen.js +5 -5
  27. package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
  28. package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
  29. package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
  30. package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
  31. package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
  32. package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
  33. package/dist/resources/extensions/browser-tools/utils.js +1 -1
  34. package/dist/resources/extensions/mac-tools/index.js +19 -34
  35. package/dist/resources/extensions/memory/index.js +20 -2
  36. package/dist/resources/extensions/shared/interview-ui.js +103 -20
  37. package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
  38. package/dist/resources/extensions/slash-commands/fast.js +73 -0
  39. package/dist/resources/extensions/slash-commands/index.js +2 -0
  40. package/dist/resources/extensions/slash-commands/plan.js +54 -28
  41. package/dist/resources/extensions/slash-commands/tools.js +40 -4
  42. package/dist/resources/extensions/subagent/agent-switcher-component.js +208 -0
  43. package/dist/resources/extensions/subagent/agent-switcher-model.js +107 -0
  44. package/dist/resources/extensions/subagent/background-job-manager.js +24 -6
  45. package/dist/resources/extensions/subagent/background-runner.js +4 -0
  46. package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
  47. package/dist/resources/extensions/subagent/index.js +715 -370
  48. package/dist/resources/extensions/subagent/launch-helpers.js +19 -5
  49. package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
  50. package/dist/resources/extensions/voice/index.js +96 -36
  51. package/dist/resources/extensions/voice/push-to-talk.js +26 -0
  52. package/dist/shared-paths.d.ts +4 -0
  53. package/dist/shared-paths.js +4 -0
  54. package/dist/shared-preferences.d.ts +4 -0
  55. package/dist/shared-preferences.js +4 -0
  56. package/dist/startup-model-validation.d.ts +1 -1
  57. package/dist/startup-timings.d.ts +4 -0
  58. package/dist/startup-timings.js +4 -0
  59. package/dist/update-check.d.ts +4 -0
  60. package/dist/update-check.js +4 -0
  61. package/dist/update-cmd.d.ts +4 -0
  62. package/dist/update-cmd.js +4 -0
  63. package/dist/welcome-screen.js +4 -4
  64. package/dist/wizard.d.ts +4 -0
  65. package/dist/wizard.js +4 -0
  66. package/package.json +1 -1
  67. package/packages/pi-agent-core/dist/agent.d.ts +28 -0
  68. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  69. package/packages/pi-agent-core/dist/agent.js +105 -5
  70. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  71. package/packages/pi-agent-core/dist/types.d.ts +13 -2
  72. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  73. package/packages/pi-agent-core/dist/types.js.map +1 -1
  74. package/packages/pi-agent-core/src/agent.ts +142 -6
  75. package/packages/pi-agent-core/src/types.ts +12 -3
  76. package/packages/pi-ai/dist/adaptive/classifier.d.ts +29 -0
  77. package/packages/pi-ai/dist/adaptive/classifier.d.ts.map +1 -0
  78. package/packages/pi-ai/dist/adaptive/classifier.js +72 -0
  79. package/packages/pi-ai/dist/adaptive/classifier.js.map +1 -0
  80. package/packages/pi-ai/dist/adaptive/classifier.test.d.ts +2 -0
  81. package/packages/pi-ai/dist/adaptive/classifier.test.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/adaptive/classifier.test.js +32 -0
  83. package/packages/pi-ai/dist/adaptive/classifier.test.js.map +1 -0
  84. package/packages/pi-ai/dist/index.d.ts +1 -0
  85. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  86. package/packages/pi-ai/dist/index.js +1 -0
  87. package/packages/pi-ai/dist/index.js.map +1 -1
  88. package/packages/pi-ai/dist/providers/amazon-bedrock.js +0 -2
  89. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  90. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  91. package/packages/pi-ai/dist/providers/anthropic-shared.js +0 -2
  92. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  93. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
  94. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  95. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  96. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/providers/google-gemini-cli.js +0 -4
  98. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/google-vertex.js +0 -5
  100. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  101. package/packages/pi-ai/dist/providers/google.js +0 -5
  102. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  103. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +35 -2
  104. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -6
  106. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  107. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
  108. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
  109. package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
  110. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  111. package/packages/pi-ai/dist/providers/openai-completions.js +0 -1
  112. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  113. package/packages/pi-ai/dist/providers/openai-responses.d.ts +9 -2
  114. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  115. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
  116. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
  117. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
  118. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
  119. package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
  120. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  121. package/packages/pi-ai/dist/providers/openai-shared.d.ts +0 -1
  122. package/packages/pi-ai/dist/providers/openai-shared.d.ts.map +1 -1
  123. package/packages/pi-ai/dist/providers/openai-shared.js +0 -4
  124. package/packages/pi-ai/dist/providers/openai-shared.js.map +1 -1
  125. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/providers/simple-options.js +2 -1
  127. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  128. package/packages/pi-ai/dist/types.d.ts +6 -2
  129. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  130. package/packages/pi-ai/dist/types.js.map +1 -1
  131. package/packages/pi-ai/src/adaptive/classifier.test.ts +38 -0
  132. package/packages/pi-ai/src/adaptive/classifier.ts +107 -0
  133. package/packages/pi-ai/src/index.ts +1 -0
  134. package/packages/pi-ai/src/providers/amazon-bedrock.ts +0 -2
  135. package/packages/pi-ai/src/providers/anthropic-shared.ts +0 -2
  136. package/packages/pi-ai/src/providers/azure-openai-responses.ts +1 -1
  137. package/packages/pi-ai/src/providers/google-gemini-cli.ts +0 -4
  138. package/packages/pi-ai/src/providers/google-vertex.ts +0 -5
  139. package/packages/pi-ai/src/providers/google.ts +0 -5
  140. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
  141. package/packages/pi-ai/src/providers/openai-codex-responses.ts +48 -7
  142. package/packages/pi-ai/src/providers/openai-completions.ts +1 -2
  143. package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
  144. package/packages/pi-ai/src/providers/openai-responses.ts +27 -4
  145. package/packages/pi-ai/src/providers/openai-shared.ts +0 -3
  146. package/packages/pi-ai/src/providers/simple-options.ts +2 -1
  147. package/packages/pi-ai/src/types.ts +6 -2
  148. package/packages/pi-coding-agent/dist/cli/args.js +2 -2
  149. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +7 -2
  151. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/agent-session.js +53 -20
  153. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
  155. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
  157. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/lsp/lsp.md +3 -1
  159. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/sdk.js +36 -8
  161. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/sdk.test.js +37 -0
  163. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +8 -0
  165. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/session-manager.js +4 -0
  167. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +18 -7
  169. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
  171. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
  172. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
  173. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
  174. package/packages/pi-coding-agent/dist/core/settings-manager.js +32 -2
  175. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  178. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  180. package/packages/pi-coding-agent/dist/core/slash-commands.js +2 -1
  181. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/system-prompt.js +12 -3
  184. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
  186. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
  187. package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
  188. package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
  189. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
  190. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
  191. package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
  192. package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
  193. package/packages/pi-coding-agent/dist/core/tools/grep.js +1 -1
  194. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
  196. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/tools/index.js +2 -0
  198. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +10 -1
  200. package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -1
  201. package/packages/pi-coding-agent/dist/core/tools/pty.js +29 -3
  202. package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +26 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
  207. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +2 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +19 -2
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +11 -2
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +49 -6
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js +1 -2
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +18 -4
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +13 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +49 -0
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
  237. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +197 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
  240. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +106 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
  245. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  249. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  250. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  251. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +88 -2
  252. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  253. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +2 -2
  257. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -6
  259. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +1 -1
  261. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/print-mode.js +6 -0
  264. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  265. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +20 -0
  267. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/tests/path-display.test.js +15 -0
  269. package/packages/pi-coding-agent/dist/tests/path-display.test.js.map +1 -1
  270. package/packages/pi-coding-agent/package.json +1 -1
  271. package/packages/pi-coding-agent/src/cli/args.ts +2 -2
  272. package/packages/pi-coding-agent/src/core/agent-session.ts +58 -21
  273. package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
  274. package/packages/pi-coding-agent/src/core/lsp/lsp.md +3 -1
  275. package/packages/pi-coding-agent/src/core/sdk.test.ts +45 -0
  276. package/packages/pi-coding-agent/src/core/sdk.ts +39 -8
  277. package/packages/pi-coding-agent/src/core/session-manager.ts +12 -0
  278. package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
  279. package/packages/pi-coding-agent/src/core/settings-manager.ts +50 -9
  280. package/packages/pi-coding-agent/src/core/skills.ts +4 -1
  281. package/packages/pi-coding-agent/src/core/slash-commands.ts +2 -1
  282. package/packages/pi-coding-agent/src/core/system-prompt.ts +14 -3
  283. package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
  284. package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
  285. package/packages/pi-coding-agent/src/core/tools/grep.ts +1 -1
  286. package/packages/pi-coding-agent/src/core/tools/index.ts +3 -0
  287. package/packages/pi-coding-agent/src/core/tools/pty.ts +45 -6
  288. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +31 -0
  289. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
  290. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
  291. package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +1 -1
  292. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +18 -2
  293. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -9
  294. package/packages/pi-coding-agent/src/modes/interactive/components/thinking-selector.ts +1 -2
  295. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1154 -1136
  296. package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +64 -0
  297. package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +228 -0
  298. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +494 -389
  299. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  300. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
  301. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +103 -3
  302. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
  303. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +11 -7
  304. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +1 -1
  305. package/packages/pi-coding-agent/src/modes/print-mode.ts +6 -0
  306. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +29 -0
  307. package/packages/pi-coding-agent/src/tests/path-display.test.ts +17 -0
  308. package/packages/pi-tui/dist/components/loader.d.ts +5 -2
  309. package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
  310. package/packages/pi-tui/dist/components/loader.js +33 -3
  311. package/packages/pi-tui/dist/components/loader.js.map +1 -1
  312. package/packages/pi-tui/src/components/loader.ts +31 -3
  313. package/packages/rpc-client/src/index.ts +1 -1
  314. package/packages/rpc-client/src/rpc-client.ts +29 -0
  315. package/packages/rpc-client/src/rpc-types.ts +1 -1
  316. package/pkg/dist/modes/interactive/theme/theme.d.ts +2 -2
  317. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  318. package/pkg/dist/modes/interactive/theme/theme.js +10 -6
  319. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  320. package/pkg/dist/modes/interactive/theme/themes.js +1 -1
  321. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  322. package/pkg/package.json +1 -1
  323. package/src/resources/agents/generic.md +1 -0
  324. package/src/resources/agents/scout.md +8 -1
  325. package/src/resources/agents/worker.md +1 -0
  326. package/src/resources/extensions/ask-user-questions.ts +88 -0
  327. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +6 -16
  328. package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
  329. package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
  330. package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
  331. package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
  332. package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
  333. package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
  334. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
  335. package/src/resources/extensions/browser-tools/utils.ts +1 -1
  336. package/src/resources/extensions/mac-tools/index.ts +19 -34
  337. package/src/resources/extensions/memory/index.ts +22 -2
  338. package/src/resources/extensions/shared/interview-ui.ts +108 -15
  339. package/src/resources/extensions/shared/tests/ask-user-freetext.test.ts +61 -0
  340. package/src/resources/extensions/shared/tests/custom-ui-fallbacks.test.ts +46 -0
  341. package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
  342. package/src/resources/extensions/slash-commands/fast.ts +89 -0
  343. package/src/resources/extensions/slash-commands/index.ts +2 -0
  344. package/src/resources/extensions/slash-commands/plan.ts +59 -30
  345. package/src/resources/extensions/slash-commands/tools.ts +43 -4
  346. package/src/resources/extensions/subagent/agent-switcher-component.ts +228 -0
  347. package/src/resources/extensions/subagent/agent-switcher-model.ts +160 -0
  348. package/src/resources/extensions/subagent/background-job-manager.ts +57 -6
  349. package/src/resources/extensions/subagent/background-runner.ts +8 -0
  350. package/src/resources/extensions/subagent/background-types.ts +4 -0
  351. package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
  352. package/src/resources/extensions/subagent/index.ts +998 -493
  353. package/src/resources/extensions/subagent/launch-helpers.ts +15 -4
  354. package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
  355. package/src/resources/extensions/voice/index.ts +308 -238
  356. package/src/resources/extensions/voice/push-to-talk.ts +42 -0
  357. package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
@@ -43,6 +43,11 @@ export interface QuestionOption {
43
43
  description: string;
44
44
  }
45
45
 
46
+ export interface QuestionShowWhen {
47
+ questionId: string;
48
+ selectedAnyOf: string[];
49
+ }
50
+
46
51
  export interface Question {
47
52
  id: string;
48
53
  header: string;
@@ -50,6 +55,8 @@ export interface Question {
50
55
  options: QuestionOption[];
51
56
  /** If true, user can toggle multiple options with SPACE, confirm with ENTER */
52
57
  allowMultiple?: boolean;
58
+ /** Show this question only when a prior question matches one of selectedAnyOf labels */
59
+ showWhen?: QuestionShowWhen;
53
60
  }
54
61
 
55
62
  export interface RoundResult {
@@ -107,6 +114,19 @@ export interface WrapUpOptions {
107
114
  const OTHER_OPTION_LABEL = "None of the above";
108
115
  const OTHER_OPTION_DESCRIPTION = "Select to type your own answer.";
109
116
 
117
+ function matchesShowWhen(
118
+ question: Question,
119
+ answers: Record<string, { selected: string | string[]; notes: string }>,
120
+ ): boolean {
121
+ if (!question.showWhen) return true;
122
+ const controlling = answers[question.showWhen.questionId];
123
+ if (!controlling) return false;
124
+ const selected = Array.isArray(controlling.selected)
125
+ ? controlling.selected
126
+ : [controlling.selected];
127
+ return selected.some((value) => question.showWhen?.selectedAnyOf.includes(value));
128
+ }
129
+
110
130
  async function runSequentialInterviewFallback(
111
131
  questions: Question[],
112
132
  ctx: ExtensionCommandContext,
@@ -114,6 +134,9 @@ async function runSequentialInterviewFallback(
114
134
  const answers: Record<string, { selected: string | string[]; notes: string }> = {};
115
135
 
116
136
  for (const q of questions) {
137
+ if (!matchesShowWhen(q, answers)) {
138
+ continue;
139
+ }
117
140
  const options = q.options.map((o) => o.label);
118
141
  if (!q.allowMultiple) {
119
142
  options.push(OTHER_OPTION_LABEL);
@@ -252,7 +275,6 @@ export async function showInterviewRound(
252
275
  notesVisible: false,
253
276
  }));
254
277
 
255
- const isMultiQuestion = questions.length > 1;
256
278
  let currentIdx = 0;
257
279
  let focusNotes = false;
258
280
  let showingReview = false;
@@ -295,13 +317,64 @@ export async function showInterviewRound(
295
317
  getEditor().setText(states[currentIdx].notes);
296
318
  }
297
319
 
320
+ function selectedLabelsForIndex(idx: number): string[] {
321
+ if (isMultiSelect(idx)) {
322
+ return Array.from(states[idx].checkedIndices).sort((a, b) => a - b).map((optionIdx) => questions[idx].options[optionIdx]?.label).filter((v): v is string => !!v);
323
+ }
324
+ const committed = states[idx].committedIndex;
325
+ if (committed === null) return [];
326
+ if (committed < questions[idx].options.length) return [questions[idx].options[committed]?.label].filter((v): v is string => !!v);
327
+ if (committed === noneOrDoneIdx(idx)) return [OTHER_OPTION_LABEL];
328
+ return [];
329
+ }
330
+
331
+ function visibleQuestionIndices(): number[] {
332
+ const visible: number[] = [];
333
+ for (let i = 0; i < questions.length; i++) {
334
+ const q = questions[i];
335
+ if (!q.showWhen) {
336
+ visible.push(i);
337
+ continue;
338
+ }
339
+ const controllingIdx = questions.findIndex((candidate) => candidate.id === q.showWhen?.questionId);
340
+ if (controllingIdx < 0 || !visible.includes(controllingIdx)) continue;
341
+ const selected = selectedLabelsForIndex(controllingIdx);
342
+ if (selected.some((label) => q.showWhen?.selectedAnyOf.includes(label))) {
343
+ visible.push(i);
344
+ }
345
+ }
346
+ return visible;
347
+ }
348
+
298
349
  function isQuestionAnswered(idx: number): boolean {
299
350
  if (isMultiSelect(idx)) return states[idx].checkedIndices.size > 0;
300
351
  return states[idx].committedIndex !== null;
301
352
  }
302
353
 
303
354
  function allAnswered(): boolean {
304
- return questions.every((_, i) => isQuestionAnswered(i));
355
+ const visible = visibleQuestionIndices();
356
+ return visible.length > 0 && visible.every((i) => isQuestionAnswered(i));
357
+ }
358
+
359
+ function clearQuestionState(idx: number) {
360
+ states[idx].cursorIndex = 0;
361
+ states[idx].committedIndex = null;
362
+ states[idx].checkedIndices.clear();
363
+ states[idx].notes = "";
364
+ states[idx].notesVisible = false;
365
+ }
366
+
367
+ function reconcileVisibility() {
368
+ const visible = new Set(visibleQuestionIndices());
369
+ for (let i = 0; i < questions.length; i++) {
370
+ if (!visible.has(i)) clearQuestionState(i);
371
+ }
372
+ if (!visible.has(currentIdx)) {
373
+ const fallback = visibleQuestionIndices()[0] ?? 0;
374
+ currentIdx = fallback;
375
+ loadStateToEditor();
376
+ focusNotes = false;
377
+ }
305
378
  }
306
379
 
307
380
  function switchQuestion(newIdx: number) {
@@ -315,7 +388,9 @@ export async function showInterviewRound(
315
388
 
316
389
  function buildResult(): RoundResult {
317
390
  const answers: Record<string, { selected: string | string[]; notes: string }> = {};
391
+ const visible = new Set(visibleQuestionIndices());
318
392
  for (let i = 0; i < questions.length; i++) {
393
+ if (!visible.has(i)) continue;
319
394
  const q = questions[i];
320
395
  const st = states[i];
321
396
  const notes = st.notes.trim();
@@ -348,6 +423,8 @@ export async function showInterviewRound(
348
423
  states[currentIdx].committedIndex = states[currentIdx].cursorIndex;
349
424
  }
350
425
 
426
+ reconcileVisibility();
427
+
351
428
  // Auto-open the notes field when "None of the above" is selected
352
429
  // so the user can immediately provide a free-text explanation
353
430
  // instead of being trapped in a re-asking loop (bug #2715).
@@ -360,10 +437,14 @@ export async function showInterviewRound(
360
437
  return;
361
438
  }
362
439
 
363
- if (isMultiQuestion && currentIdx < questions.length - 1) {
364
- let next = currentIdx + 1;
365
- for (let i = 0; i < questions.length; i++) {
366
- const candidate = (currentIdx + 1 + i) % questions.length;
440
+ const visible = visibleQuestionIndices();
441
+ const currentVisiblePos = visible.indexOf(currentIdx);
442
+ const isMultiQuestion = visible.length > 1;
443
+
444
+ if (isMultiQuestion && currentVisiblePos >= 0 && currentVisiblePos < visible.length - 1) {
445
+ let next = visible[currentVisiblePos + 1] ?? visible[0] ?? currentIdx;
446
+ for (let i = 0; i < visible.length; i++) {
447
+ const candidate = visible[(currentVisiblePos + 1 + i) % visible.length] ?? currentIdx;
367
448
  if (!isQuestionAnswered(candidate)) { next = candidate; break; }
368
449
  }
369
450
  switchQuestion(next);
@@ -396,7 +477,8 @@ export async function showInterviewRound(
396
477
  if (showingReview) {
397
478
  if (matchesKey(data, Key.escape) || matchesKey(data, Key.left)) {
398
479
  showingReview = false;
399
- switchQuestion(questions.length - 1);
480
+ const visible = visibleQuestionIndices();
481
+ switchQuestion(visible[visible.length - 1] ?? 0);
400
482
  return;
401
483
  }
402
484
  if (matchesKey(data, Key.enter) || matchesKey(data, Key.right) || matchesKey(data, Key.space)) {
@@ -447,9 +529,14 @@ export async function showInterviewRound(
447
529
  }
448
530
 
449
531
  // ── Multi-question navigation ────────────────────────────────
532
+ const visible = visibleQuestionIndices();
533
+ const isMultiQuestion = visible.length > 1;
450
534
  if (isMultiQuestion) {
451
- if (matchesKey(data, Key.left)) { switchQuestion((currentIdx - 1 + questions.length) % questions.length); return; }
452
- if (matchesKey(data, Key.right)) { switchQuestion((currentIdx + 1) % questions.length); return; }
535
+ const visiblePos = visible.indexOf(currentIdx);
536
+ if (visiblePos >= 0) {
537
+ if (matchesKey(data, Key.left)) { switchQuestion(visible[(visiblePos - 1 + visible.length) % visible.length] ?? currentIdx); return; }
538
+ if (matchesKey(data, Key.right)) { switchQuestion(visible[(visiblePos + 1) % visible.length] ?? currentIdx); return; }
539
+ }
453
540
  }
454
541
 
455
542
  // ── Cursor navigation ────────────────────────────────────────
@@ -488,7 +575,8 @@ export async function showInterviewRound(
488
575
 
489
576
  push(ui.bar(), ui.blank(), ui.header(` ${opts.reviewHeadline ?? "Review your answers"}`), ui.blank());
490
577
 
491
- for (let i = 0; i < questions.length; i++) {
578
+ const visible = visibleQuestionIndices();
579
+ for (const i of visible) {
492
580
  const q = questions[i];
493
581
  const st = states[i];
494
582
 
@@ -571,6 +659,11 @@ export async function showInterviewRound(
571
659
  const lines: string[] = [];
572
660
  const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
573
661
 
662
+ reconcileVisibility();
663
+ const visible = visibleQuestionIndices();
664
+ const isMultiQuestion = visible.length > 1;
665
+ const visiblePos = Math.max(visible.indexOf(currentIdx), 0);
666
+
574
667
  const q = questions[currentIdx];
575
668
  const st = states[currentIdx];
576
669
  const multiSel = isMultiSelect(currentIdx);
@@ -579,13 +672,13 @@ export async function showInterviewRound(
579
672
 
580
673
  // ── Progress header ────────────────────────────────────────────
581
674
  if (isMultiQuestion) {
582
- const unanswered = questions.filter((_, i) => !isQuestionAnswered(i)).length;
583
- const answeredSet = new Set(questions.map((_, i) => i).filter(i => isQuestionAnswered(i)));
584
- push(ui.questionTabs(questions.map(q => q.header), currentIdx, answeredSet));
675
+ const unanswered = visible.filter((i) => !isQuestionAnswered(i)).length;
676
+ const answeredSet = new Set(visible.map((i, visibleIndex) => (isQuestionAnswered(i) ? visibleIndex : -1)).filter((i) => i >= 0));
677
+ push(ui.questionTabs(visible.map((i) => questions[i]?.header ?? ""), visiblePos, answeredSet));
585
678
  push(ui.blank());
586
679
  const progressParts = [
587
680
  opts.progress,
588
- `Question ${currentIdx + 1}/${questions.length}`,
681
+ `Question ${visiblePos + 1}/${visible.length}`,
589
682
  unanswered > 0 ? `${unanswered} unanswered` : null,
590
683
  ].filter(Boolean).join(" • ");
591
684
  if (progressParts) push(ui.meta(` ${progressParts}`));
@@ -647,7 +740,7 @@ export async function showInterviewRound(
647
740
 
648
741
  // ── Footer hints ───────────────────────────────────────────────
649
742
  push(ui.blank());
650
- const isLast = !isMultiQuestion || currentIdx === questions.length - 1;
743
+ const isLast = !isMultiQuestion || visiblePos === visible.length - 1;
651
744
  const hints: string[] = [];
652
745
  if (focusNotes) {
653
746
  hints.push("enter to confirm");
@@ -131,6 +131,67 @@ describe("ask-user-questions RPC fallback free-text", () => {
131
131
  assert.deepStrictEqual(parsed.answers.q1.answers, ["Option A"]);
132
132
  });
133
133
 
134
+ it("skips conditional follow-up when controlling answer does not match", async () => {
135
+ const tool = captureTool();
136
+ const { ctx, selectCalls } = makeMockCtx({
137
+ selectReturns: ["Review plan"],
138
+ });
139
+
140
+ const result = await tool.execute(
141
+ "call-conditional-skip",
142
+ {
143
+ questions: [
144
+ makeQuestion("plan_mode_approval_action", ["Approve plan", "Review plan"]),
145
+ {
146
+ ...makeQuestion("plan_mode_approval_permission", ["Auto mode", "Bypass mode"]),
147
+ showWhen: {
148
+ questionId: "plan_mode_approval_action",
149
+ selectedAnyOf: ["Approve plan"],
150
+ },
151
+ },
152
+ ],
153
+ },
154
+ undefined,
155
+ undefined,
156
+ ctx,
157
+ );
158
+
159
+ assert.equal(selectCalls.length, 1);
160
+ const parsed = JSON.parse(result.content[0]?.text ?? "{}");
161
+ assert.ok(parsed.answers.plan_mode_approval_action);
162
+ assert.equal(parsed.answers.plan_mode_approval_permission, undefined);
163
+ });
164
+
165
+ it("asks conditional follow-up when controlling answer matches", async () => {
166
+ const tool = captureTool();
167
+ const { ctx, selectCalls } = makeMockCtx({
168
+ selectReturns: ["Approve plan", "Auto mode"],
169
+ });
170
+
171
+ const result = await tool.execute(
172
+ "call-conditional-show",
173
+ {
174
+ questions: [
175
+ makeQuestion("plan_mode_approval_action", ["Approve plan", "Review plan"]),
176
+ {
177
+ ...makeQuestion("plan_mode_approval_permission", ["Auto mode", "Bypass mode"]),
178
+ showWhen: {
179
+ questionId: "plan_mode_approval_action",
180
+ selectedAnyOf: ["Approve plan"],
181
+ },
182
+ },
183
+ ],
184
+ },
185
+ undefined,
186
+ undefined,
187
+ ctx,
188
+ );
189
+
190
+ assert.equal(selectCalls.length, 2);
191
+ const parsed = JSON.parse(result.content[0]?.text ?? "{}");
192
+ assert.deepStrictEqual(parsed.answers.plan_mode_approval_permission.answers, ["Auto mode"]);
193
+ });
194
+
134
195
  it("handles cancelled free-text input gracefully", async () => {
135
196
  const tool = captureTool();
136
197
  const { ctx, inputCalls } = makeMockCtx({
@@ -73,6 +73,52 @@ describe("custom UI fallback paths", () => {
73
73
  assert.equal(result.answers.q1?.notes, "My custom answer");
74
74
  });
75
75
 
76
+ it("showInterviewRound skips conditional question when condition is not met", async () => {
77
+ const selectCalls: Array<{ title: string; options: string[] }> = [];
78
+ const ctx = {
79
+ hasUI: true,
80
+ ui: {
81
+ custom: async () => undefined,
82
+ select: async (title: string, options: string[]) => {
83
+ selectCalls.push({ title, options });
84
+ if (title.startsWith("Q1:")) return "Review";
85
+ return options[0];
86
+ },
87
+ input: async () => "",
88
+ },
89
+ };
90
+
91
+ const result = await showInterviewRound(
92
+ [
93
+ {
94
+ id: "q1",
95
+ header: "Q1",
96
+ question: "Approve?",
97
+ options: [
98
+ { label: "Approve", description: "approve" },
99
+ { label: "Review", description: "review" },
100
+ ],
101
+ },
102
+ {
103
+ id: "q2",
104
+ header: "Q2",
105
+ question: "Mode?",
106
+ options: [
107
+ { label: "Auto", description: "auto" },
108
+ { label: "Bypass", description: "bypass" },
109
+ ],
110
+ showWhen: { questionId: "q1", selectedAnyOf: ["Approve"] },
111
+ },
112
+ ],
113
+ {},
114
+ ctx as any,
115
+ );
116
+
117
+ assert.equal(selectCalls.length, 1, "conditional question should be skipped in fallback mode");
118
+ assert.equal(result.answers.q1?.selected, "Review");
119
+ assert.equal(result.answers.q2, undefined);
120
+ });
121
+
76
122
  it("collectOneSecret respects null from secure UI and does not fall back to plaintext input", async () => {
77
123
  let inputCalls = 0;
78
124
  const notifications: Array<{ message: string; type?: string }> = [];
@@ -2,11 +2,11 @@
2
2
  "id": "slash-commands",
3
3
  "name": "Slash Commands",
4
4
  "version": "1.0.0",
5
- "description": "Bundled slash commands for context inspection, planning, and lazy tool search",
5
+ "description": "Bundled slash commands for context inspection, planning, fast mode, and lazy tool search",
6
6
  "tier": "bundled",
7
7
  "requires": { "platform": ">=2.29.0" },
8
8
  "provides": {
9
- "commands": ["audit", "clear", "context", "plan", "execute", "cancel-plan", "tools"],
9
+ "commands": ["audit", "clear", "context", "fast", "plan", "execute", "cancel-plan", "tools"],
10
10
  "tools": ["tool_search", "tool_enable"],
11
11
  "flags": ["plan"]
12
12
  }
@@ -0,0 +1,89 @@
1
+ import { getAgentDir, SettingsManager, type ExtensionAPI, type ExtensionCommandContext } from "@gsd/pi-coding-agent";
2
+
3
+ type FastCommandAction = "toggle" | "on" | "off" | "status" | "invalid";
4
+
5
+ function parseFastCommandAction(args: string): FastCommandAction {
6
+ const normalized = args.trim().toLowerCase();
7
+ if (!normalized) return "toggle";
8
+ if (normalized === "on") return "on";
9
+ if (normalized === "off") return "off";
10
+ if (normalized === "status") return "status";
11
+ return "invalid";
12
+ }
13
+
14
+ function supportsFastMode(model: ExtensionCommandContext["model"]): boolean {
15
+ if (!model) return false;
16
+ if (model.api === "openai-codex-responses") return true;
17
+ if (model.api !== "openai-responses") return false;
18
+ if (model.provider !== "openai") return false;
19
+ return model.capabilities?.supportsServiceTier === true;
20
+ }
21
+
22
+ function getModelLabel(model: ExtensionCommandContext["model"]): string {
23
+ if (!model) return "no active model";
24
+ return `${model.provider}/${model.id}`;
25
+ }
26
+
27
+ function getSettingsManager(): SettingsManager {
28
+ return SettingsManager.create(process.cwd(), getAgentDir());
29
+ }
30
+
31
+ export const __testing = {
32
+ parseFastCommandAction,
33
+ supportsFastMode,
34
+ };
35
+
36
+ export default function fastCommand(pi: ExtensionAPI) {
37
+ pi.registerCommand("fast", {
38
+ description: "Toggle fast mode for OpenAI/Codex models (service_tier=priority)",
39
+ getArgumentCompletions(prefix: string) {
40
+ const options = [
41
+ { value: "on", label: "on", description: "Enable fast mode" },
42
+ { value: "off", label: "off", description: "Disable fast mode" },
43
+ { value: "status", label: "status", description: "Show current fast-mode status" },
44
+ ];
45
+ const query = prefix.trim().toLowerCase();
46
+ return options.filter((item) => item.value.startsWith(query));
47
+ },
48
+ async handler(args: string, ctx: ExtensionCommandContext) {
49
+ const settings = getSettingsManager() as SettingsManager & {
50
+ getFastMode: () => boolean;
51
+ setFastMode: (enabled: boolean) => void;
52
+ };
53
+ const current = settings.getFastMode();
54
+ const action = parseFastCommandAction(args);
55
+ const model = ctx.model;
56
+ const supported = supportsFastMode(model);
57
+ const modelLabel = getModelLabel(model);
58
+
59
+ if (action === "invalid") {
60
+ ctx.ui.notify("Usage: /fast [on|off|status]", "warning");
61
+ return;
62
+ }
63
+
64
+ if (action === "status") {
65
+ ctx.ui.notify(
66
+ `Fast mode: ${current ? "ON" : "OFF"} · model ${modelLabel} is ${supported ? "supported" : "unsupported"}`,
67
+ "info",
68
+ );
69
+ return;
70
+ }
71
+
72
+ const next = action === "toggle" ? !current : action === "on";
73
+ settings.setFastMode(next);
74
+
75
+ if (!supported) {
76
+ ctx.ui.notify(
77
+ `Fast mode: ${next ? "ON" : "OFF"} (saved). Current model ${modelLabel} does not support fast mode.`,
78
+ "warning",
79
+ );
80
+ return;
81
+ }
82
+
83
+ ctx.ui.notify(
84
+ `Fast mode: ${next ? "ON" : "OFF"} (saved). ${next ? "Requests will include service_tier=priority." : "Requests will omit service_tier."}`,
85
+ "info",
86
+ );
87
+ },
88
+ });
89
+ }
@@ -2,6 +2,7 @@ import type { ExtensionAPI } from "@gsd/pi-coding-agent";
2
2
  import auditCommand from "./audit.js";
3
3
  import clearCommand from "./clear.js";
4
4
  import contextCommand from "./context.js";
5
+ import fastCommand from "./fast.js";
5
6
  import initCommand from "./init.js";
6
7
  import planCommand from "./plan.js";
7
8
  import toolSearchExtension from "./tools.js";
@@ -10,6 +11,7 @@ export default function slashCommands(pi: ExtensionAPI) {
10
11
  auditCommand(pi);
11
12
  clearCommand(pi);
12
13
  contextCommand(pi);
14
+ fastCommand(pi);
13
15
  initCommand(pi);
14
16
  planCommand(pi);
15
17
  toolSearchExtension(pi);
@@ -38,9 +38,8 @@ const BLOCKED_TOOLS = new Set([
38
38
  "async_bash",
39
39
  "bg_shell",
40
40
  "browser_navigate",
41
- "browser_go_back",
42
- "browser_go_forward",
43
- "browser_reload",
41
+ "browser_pages",
42
+ "browser_frames",
44
43
  "browser_click",
45
44
  "browser_drag",
46
45
  "browser_type",
@@ -51,18 +50,13 @@ const BLOCKED_TOOLS = new Set([
51
50
  "browser_select_option",
52
51
  "browser_set_checked",
53
52
  "browser_set_viewport",
54
- "browser_click_ref",
55
- "browser_hover_ref",
56
- "browser_fill_ref",
53
+ "browser_ref",
57
54
  "browser_act",
58
55
  "browser_batch",
59
56
  "browser_fill_form",
60
- "browser_mock_route",
61
- "browser_block_urls",
62
- "browser_clear_routes",
57
+ "browser_network",
63
58
  "browser_emulate_device",
64
- "browser_save_state",
65
- "browser_restore_state",
59
+ "browser_state",
66
60
  "browser_generate_test",
67
61
  "browser_verify",
68
62
  "write",
@@ -80,6 +74,7 @@ const REVISE_LABEL = "Revise plan";
80
74
  const CANCEL_LABEL = "Cancel";
81
75
  const DEFAULT_PLAN_REVIEW_AGENT = "generic";
82
76
  const DEFAULT_PLAN_CODING_AGENT = "worker";
77
+ const MIN_PLAN_CONFIDENCE = 8;
83
78
 
84
79
  type PlanApprovalStatus = "pending" | "approved" | "revising" | "cancelled";
85
80
  type RestorablePermissionMode = Exclude<PermissionMode, "plan">;
@@ -455,6 +450,9 @@ function buildPlanModeSystemPrompt(): string {
455
450
  "You are currently in plan mode.",
456
451
  "Investigate, clarify scope, and produce a persisted execution plan before making source changes.",
457
452
  "If requirements are ambiguous or constraints are missing, ask concise clarifying questions before drafting or saving a plan.",
453
+ `Before writing or updating a plan artifact, make sure your confidence is at least ${MIN_PLAN_CONFIDENCE}/10. If confidence is lower, investigate more or ask clarifying questions first.`,
454
+ "Include an explicit confidence line in every saved plan, for example: \"Confidence: 8/10\" or higher.",
455
+ "When adjusting an existing saved plan, prefer the edit tool for targeted changes. Rewrite the whole file only when the structure changes substantially or an exact edit is impractical.",
458
456
  "Do not modify source files or run side-effect commands while plan mode is active.",
459
457
  "Persist plan artifacts under .lsd/plan/.",
460
458
  ];
@@ -483,28 +481,19 @@ function buildNewSessionOptionLabel(): string {
483
481
  }
484
482
 
485
483
  function buildApprovalActionInstructions(): string {
486
- return [
487
- "Ask for plan approval now using ask_user_questions.",
488
- `One single-select question with id \"${PLAN_APPROVAL_ACTION_QUESTION_ID}\". Ask what to do next with the plan.`,
489
- `Options: ${APPROVE_LABEL}, ${REVIEW_LABEL}, ${REVISE_LABEL}.`,
490
- `Do not include \"${CANCEL_LABEL}\" as an explicit option — if the user wants to cancel they should choose \"None of the above\" and type \"${CANCEL_LABEL}\" in the note.`,
491
- "Do not restate the plan. Just show the question.",
492
- ].join(" ");
493
- }
494
-
495
- function buildApprovalModeInstructions(): string {
496
484
  const autoSwitchEnabled = readAutoSwitchPlanModelSetting();
497
485
  const showNewSessionOption = autoSwitchEnabled;
498
486
  const newSessionLabel = buildNewSessionOptionLabel();
499
487
 
500
- const options = showNewSessionOption
501
- ? `${APPROVE_AUTO_LABEL}, ${APPROVE_BYPASS_LABEL}, ${APPROVE_AUTO_SUBAGENT_LABEL}, ${APPROVE_BYPASS_SUBAGENT_LABEL}, ${newSessionLabel}`
502
- : `${APPROVE_AUTO_LABEL}, ${APPROVE_BYPASS_LABEL}, ${APPROVE_AUTO_SUBAGENT_LABEL}, ${APPROVE_BYPASS_SUBAGENT_LABEL}`;
503
-
504
488
  return [
505
- "Plan approved. Now ask which execution mode to use via ask_user_questions.",
506
- `One single-select question with id \"${PLAN_APPROVAL_PERMISSION_QUESTION_ID}\".`,
507
- `Options: ${options}.`,
489
+ "Ask for plan approval now via exactly one ask_user_questions tool call.",
490
+ `Question 1 (single-select) id \"${PLAN_APPROVAL_ACTION_QUESTION_ID}\": ask what to do next with the plan. In the question text, tell the user that if they choose \"${REVISE_LABEL}\" they should type exact requested changes in the dialog notes field before submitting.`,
491
+ `Question 1 options: ${APPROVE_LABEL}, ${REVIEW_LABEL}, ${REVISE_LABEL}. Put "${APPROVE_LABEL}" first with a "(Recommended)" suffix in the description, not in the label.`,
492
+ `Do not include \"${CANCEL_LABEL}\" as an explicit option — if the user wants to cancel they should choose \"None of the above\" and type \"${CANCEL_LABEL}\" in the note.`,
493
+ `Question 2 (single-select) id \"${PLAN_APPROVAL_PERMISSION_QUESTION_ID}\": ask which execution mode to use.`,
494
+ `Question 2 options: ${APPROVE_AUTO_LABEL} (Recommended), ${APPROVE_BYPASS_LABEL}, ${APPROVE_AUTO_SUBAGENT_LABEL}, ${APPROVE_BYPASS_SUBAGENT_LABEL}${showNewSessionOption ? `, ${newSessionLabel}` : ""}.`,
495
+ `Set question 2 showWhen.questionId to \"${PLAN_APPROVAL_ACTION_QUESTION_ID}\" and showWhen.selectedAnyOf to [\"${APPROVE_LABEL}\"] so it appears only when the user selects Approve plan.`,
496
+ "Do not restate the plan in a normal assistant response. Just call ask_user_questions.",
508
497
  ].join(" ");
509
498
  }
510
499
 
@@ -569,6 +558,25 @@ function buildReviewSteeringMessage(planPath: string, planMarkdown?: string): st
569
558
  return details.join("\n\n");
570
559
  }
571
560
 
561
+ function buildRevisionSteeringMessage(planPath: string, requestedChanges?: string): string {
562
+ const details = [
563
+ `The user selected \"${REVISE_LABEL}\" for ${planPath}.`,
564
+ "Revise the existing saved plan instead of drafting a fresh replacement unless a full rewrite is genuinely necessary.",
565
+ "Prefer the edit tool for targeted adjustments to the current plan artifact. Use write only if the structure changes substantially or an exact edit is impractical.",
566
+ `Before saving the revised plan, make sure confidence is at least ${MIN_PLAN_CONFIDENCE}/10. If lower, investigate more or ask clarifying questions first.`,
567
+ "Keep an explicit confidence line in the plan, for example: \"Confidence: 8/10\" or higher.",
568
+ ];
569
+
570
+ if (requestedChanges) {
571
+ details.push(`User-requested changes: ${requestedChanges}`);
572
+ } else {
573
+ details.push("No concrete revision note was provided yet. Ask one concise clarifying question about what should change before editing the plan.");
574
+ }
575
+
576
+ details.push("After revising the saved plan artifact, ask for approval again.");
577
+ return details.join("\n\n");
578
+ }
579
+
572
580
  function approvalSelectionToExecutionMode(
573
581
  selected: string | undefined,
574
582
  ): { permissionMode: RestorablePermissionMode; executeWithSubagent: boolean } | undefined {
@@ -598,6 +606,12 @@ function getAnswerValues(answer: AskUserAnswer | undefined): string[] {
598
606
  return values;
599
607
  }
600
608
 
609
+ function getAnswerNote(answer: AskUserAnswer | undefined): string | undefined {
610
+ if (typeof answer?.notes !== "string") return undefined;
611
+ const note = answer.notes.trim();
612
+ return note.length > 0 ? note : undefined;
613
+ }
614
+
601
615
  function selectionRequestsCancel(selected: string[]): boolean {
602
616
  return selected.some((value) => {
603
617
  if (typeof value !== "string") return false;
@@ -621,6 +635,8 @@ export const __testing = {
621
635
  buildApprovalSteeringMessage,
622
636
  buildPlanPreviewMessage,
623
637
  buildReviewSteeringMessage,
638
+ buildRevisionSteeringMessage,
639
+ buildPlanModeSystemPrompt,
624
640
  buildAutoSuggestPlanModeSystemPrompt,
625
641
  readAutoSuggestPlanModeSetting,
626
642
  PLAN_SUGGEST_QUESTION_ID,
@@ -813,8 +829,16 @@ export default function planCommand(pi: ExtensionAPI) {
813
829
  if (!actionSelection) return;
814
830
 
815
831
  if (actionSelection.includes(APPROVE_LABEL)) {
816
- // Steer the second question — handle in the next tool_result cycle
817
- pi.sendUserMessage(buildApprovalModeInstructions(), { deliverAs: "steer" });
832
+ const executionMode = approvalSelectionToExecutionMode(permissionValues[0]) ?? {
833
+ permissionMode: DEFAULT_APPROVAL_PERMISSION_MODE,
834
+ executeWithSubagent: false,
835
+ };
836
+ state = { ...state, targetPermissionMode: executionMode.permissionMode };
837
+ if (executionMode.executeWithSubagent) {
838
+ const modeLabel = executionMode.permissionMode === "danger-full-access" ? "bypass" : "auto";
839
+ ctx.ui?.notify?.(`Plan approved: subagent(${modeLabel})`, "info");
840
+ }
841
+ await approvePlan(pi, ctx, executionMode.permissionMode, executionMode.executeWithSubagent);
818
842
  return;
819
843
  }
820
844
 
@@ -831,9 +855,14 @@ export default function planCommand(pi: ExtensionAPI) {
831
855
  }
832
856
 
833
857
  if (actionSelection.includes(REVISE_LABEL)) {
858
+ const requestedChanges = getAnswerNote(actionAnswer);
834
859
  enablePlanMode(pi, ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined, {
835
860
  approvalStatus: "revising",
836
861
  });
862
+ pi.sendUserMessage(
863
+ buildRevisionSteeringMessage(state.latestPlanPath ?? "the latest plan", requestedChanges),
864
+ { deliverAs: "steer" },
865
+ );
837
866
  }
838
867
  });
839
868