gsd-pi 2.10.12 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (504) hide show
  1. package/dist/bundled-extension-paths.d.ts +2 -0
  2. package/dist/bundled-extension-paths.js +10 -0
  3. package/dist/loader.js +45 -28
  4. package/dist/onboarding.js +100 -0
  5. package/dist/resource-loader.js +12 -3
  6. package/dist/resources/GSD-WORKFLOW.md +661 -0
  7. package/dist/resources/agents/researcher.md +29 -0
  8. package/dist/resources/agents/scout.md +56 -0
  9. package/dist/resources/agents/worker.md +31 -0
  10. package/dist/resources/extensions/ask-user-questions.ts +289 -0
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.ts +211 -0
  12. package/dist/resources/extensions/async-jobs/await-tool.ts +101 -0
  13. package/dist/resources/extensions/async-jobs/cancel-job-tool.ts +34 -0
  14. package/dist/resources/extensions/async-jobs/index.ts +133 -0
  15. package/dist/resources/extensions/async-jobs/job-manager.ts +211 -0
  16. package/dist/resources/extensions/bg-shell/index.ts +3179 -0
  17. package/dist/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  18. package/dist/resources/extensions/browser-tools/capture.ts +165 -0
  19. package/dist/resources/extensions/browser-tools/core.d.ts +205 -0
  20. package/dist/resources/extensions/browser-tools/core.js +1057 -0
  21. package/dist/resources/extensions/browser-tools/evaluate-helpers.ts +184 -0
  22. package/dist/resources/extensions/browser-tools/index.ts +51 -0
  23. package/dist/resources/extensions/browser-tools/lifecycle.ts +270 -0
  24. package/dist/resources/extensions/browser-tools/package.json +24 -0
  25. package/dist/resources/extensions/browser-tools/refs.ts +264 -0
  26. package/dist/resources/extensions/browser-tools/settle.ts +197 -0
  27. package/dist/resources/extensions/browser-tools/state.ts +408 -0
  28. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +652 -0
  29. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +614 -0
  30. package/dist/resources/extensions/browser-tools/tools/assertions.ts +342 -0
  31. package/dist/resources/extensions/browser-tools/tools/forms.ts +801 -0
  32. package/dist/resources/extensions/browser-tools/tools/inspection.ts +492 -0
  33. package/dist/resources/extensions/browser-tools/tools/intent.ts +614 -0
  34. package/dist/resources/extensions/browser-tools/tools/interaction.ts +865 -0
  35. package/dist/resources/extensions/browser-tools/tools/navigation.ts +232 -0
  36. package/dist/resources/extensions/browser-tools/tools/pages.ts +303 -0
  37. package/dist/resources/extensions/browser-tools/tools/refs.ts +541 -0
  38. package/dist/resources/extensions/browser-tools/tools/screenshot.ts +83 -0
  39. package/dist/resources/extensions/browser-tools/tools/session.ts +400 -0
  40. package/dist/resources/extensions/browser-tools/tools/wait.ts +247 -0
  41. package/dist/resources/extensions/browser-tools/utils.ts +660 -0
  42. package/dist/resources/extensions/context7/index.ts +428 -0
  43. package/dist/resources/extensions/context7/package.json +11 -0
  44. package/dist/resources/extensions/get-secrets-from-user.ts +625 -0
  45. package/dist/resources/extensions/google-search/index.ts +336 -0
  46. package/dist/resources/extensions/google-search/package.json +9 -0
  47. package/dist/resources/extensions/gsd/activity-log.ts +116 -0
  48. package/dist/resources/extensions/gsd/auto.ts +3732 -0
  49. package/dist/resources/extensions/gsd/commands.ts +544 -0
  50. package/dist/resources/extensions/gsd/crash-recovery.ts +105 -0
  51. package/dist/resources/extensions/gsd/dashboard-overlay.ts +564 -0
  52. package/dist/resources/extensions/gsd/dispatch-guard.ts +78 -0
  53. package/dist/resources/extensions/gsd/docs/preferences-reference.md +278 -0
  54. package/dist/resources/extensions/gsd/doctor.ts +766 -0
  55. package/dist/resources/extensions/gsd/exit-command.ts +18 -0
  56. package/dist/resources/extensions/gsd/files.ts +857 -0
  57. package/dist/resources/extensions/gsd/git-service.ts +904 -0
  58. package/dist/resources/extensions/gsd/gitignore.ts +200 -0
  59. package/dist/resources/extensions/gsd/guided-flow.ts +1054 -0
  60. package/dist/resources/extensions/gsd/index.ts +735 -0
  61. package/dist/resources/extensions/gsd/metrics.ts +374 -0
  62. package/dist/resources/extensions/gsd/migrate/command.ts +218 -0
  63. package/dist/resources/extensions/gsd/migrate/index.ts +42 -0
  64. package/dist/resources/extensions/gsd/migrate/parser.ts +323 -0
  65. package/dist/resources/extensions/gsd/migrate/parsers.ts +624 -0
  66. package/dist/resources/extensions/gsd/migrate/preview.ts +48 -0
  67. package/dist/resources/extensions/gsd/migrate/transformer.ts +346 -0
  68. package/dist/resources/extensions/gsd/migrate/types.ts +370 -0
  69. package/dist/resources/extensions/gsd/migrate/validator.ts +55 -0
  70. package/dist/resources/extensions/gsd/migrate/writer.ts +539 -0
  71. package/dist/resources/extensions/gsd/native-git-bridge.ts +181 -0
  72. package/dist/resources/extensions/gsd/native-parser-bridge.ts +135 -0
  73. package/dist/resources/extensions/gsd/observability-validator.ts +408 -0
  74. package/dist/resources/extensions/gsd/package.json +11 -0
  75. package/dist/resources/extensions/gsd/paths.ts +339 -0
  76. package/dist/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  77. package/dist/resources/extensions/gsd/preferences.ts +988 -0
  78. package/dist/resources/extensions/gsd/prompt-loader.ts +92 -0
  79. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
  80. package/dist/resources/extensions/gsd/prompts/complete-slice.md +27 -0
  81. package/dist/resources/extensions/gsd/prompts/discuss.md +237 -0
  82. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
  83. package/dist/resources/extensions/gsd/prompts/execute-task.md +63 -0
  84. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -0
  85. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -0
  86. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +61 -0
  87. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +3 -0
  88. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +29 -0
  89. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -0
  90. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +13 -0
  91. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
  92. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +78 -0
  93. package/dist/resources/extensions/gsd/prompts/plan-slice.md +55 -0
  94. package/dist/resources/extensions/gsd/prompts/queue.md +100 -0
  95. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
  96. package/dist/resources/extensions/gsd/prompts/replan-slice.md +39 -0
  97. package/dist/resources/extensions/gsd/prompts/research-milestone.md +37 -0
  98. package/dist/resources/extensions/gsd/prompts/research-slice.md +28 -0
  99. package/dist/resources/extensions/gsd/prompts/review-migration.md +66 -0
  100. package/dist/resources/extensions/gsd/prompts/run-uat.md +109 -0
  101. package/dist/resources/extensions/gsd/prompts/system.md +190 -0
  102. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
  103. package/dist/resources/extensions/gsd/roadmap-slices.ts +50 -0
  104. package/dist/resources/extensions/gsd/session-forensics.ts +500 -0
  105. package/dist/resources/extensions/gsd/skill-discovery.ts +137 -0
  106. package/dist/resources/extensions/gsd/state.ts +569 -0
  107. package/dist/resources/extensions/gsd/templates/context.md +76 -0
  108. package/dist/resources/extensions/gsd/templates/decisions.md +8 -0
  109. package/dist/resources/extensions/gsd/templates/milestone-summary.md +73 -0
  110. package/dist/resources/extensions/gsd/templates/plan.md +131 -0
  111. package/dist/resources/extensions/gsd/templates/preferences.md +23 -0
  112. package/dist/resources/extensions/gsd/templates/project.md +31 -0
  113. package/dist/resources/extensions/gsd/templates/reassessment.md +28 -0
  114. package/dist/resources/extensions/gsd/templates/requirements.md +81 -0
  115. package/dist/resources/extensions/gsd/templates/research.md +46 -0
  116. package/dist/resources/extensions/gsd/templates/roadmap.md +118 -0
  117. package/dist/resources/extensions/gsd/templates/secrets-manifest.md +22 -0
  118. package/dist/resources/extensions/gsd/templates/slice-context.md +58 -0
  119. package/dist/resources/extensions/gsd/templates/slice-summary.md +99 -0
  120. package/dist/resources/extensions/gsd/templates/state.md +19 -0
  121. package/dist/resources/extensions/gsd/templates/task-plan.md +52 -0
  122. package/dist/resources/extensions/gsd/templates/task-summary.md +57 -0
  123. package/dist/resources/extensions/gsd/templates/uat.md +54 -0
  124. package/dist/resources/extensions/gsd/tests/activity-log-prune.test.ts +297 -0
  125. package/dist/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  126. package/dist/resources/extensions/gsd/tests/auto-draft-pause.test.ts +109 -0
  127. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +45 -0
  128. package/dist/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +196 -0
  129. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
  130. package/dist/resources/extensions/gsd/tests/collect-from-manifest.test.ts +469 -0
  131. package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +200 -0
  132. package/dist/resources/extensions/gsd/tests/cost-projection.test.ts +134 -0
  133. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +312 -0
  134. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +299 -0
  135. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +660 -0
  136. package/dist/resources/extensions/gsd/tests/discuss-prompt.test.ts +27 -0
  137. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +91 -0
  138. package/dist/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +170 -0
  139. package/dist/resources/extensions/gsd/tests/doctor.test.ts +594 -0
  140. package/dist/resources/extensions/gsd/tests/draft-promotion.test.ts +169 -0
  141. package/dist/resources/extensions/gsd/tests/exit-command.test.ts +50 -0
  142. package/dist/resources/extensions/gsd/tests/git-service.test.ts +1788 -0
  143. package/dist/resources/extensions/gsd/tests/idle-recovery.test.ts +577 -0
  144. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +580 -0
  145. package/dist/resources/extensions/gsd/tests/manifest-status.test.ts +283 -0
  146. package/dist/resources/extensions/gsd/tests/metrics-io.test.ts +176 -0
  147. package/dist/resources/extensions/gsd/tests/metrics.test.ts +188 -0
  148. package/dist/resources/extensions/gsd/tests/migrate-command.test.ts +369 -0
  149. package/dist/resources/extensions/gsd/tests/migrate-parser.test.ts +757 -0
  150. package/dist/resources/extensions/gsd/tests/migrate-transformer.test.ts +635 -0
  151. package/dist/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +414 -0
  152. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +297 -0
  153. package/dist/resources/extensions/gsd/tests/migrate-writer.test.ts +398 -0
  154. package/dist/resources/extensions/gsd/tests/must-have-parser.test.ts +291 -0
  155. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  156. package/dist/resources/extensions/gsd/tests/next-milestone-id.test.ts +66 -0
  157. package/dist/resources/extensions/gsd/tests/orphaned-branch.test.ts +353 -0
  158. package/dist/resources/extensions/gsd/tests/parsers.test.ts +1664 -0
  159. package/dist/resources/extensions/gsd/tests/plan-milestone.test.ts +133 -0
  160. package/dist/resources/extensions/gsd/tests/plan-quality-validator.test.ts +363 -0
  161. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  162. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  163. package/dist/resources/extensions/gsd/tests/queue-draft-detection.test.ts +126 -0
  164. package/dist/resources/extensions/gsd/tests/reassess-prompt.test.ts +142 -0
  165. package/dist/resources/extensions/gsd/tests/regex-hardening.test.ts +281 -0
  166. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
  167. package/dist/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
  168. package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +493 -0
  169. package/dist/resources/extensions/gsd/tests/requirements.test.ts +106 -0
  170. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +35 -0
  171. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
  172. package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +41 -0
  173. package/dist/resources/extensions/gsd/tests/run-uat.test.ts +316 -0
  174. package/dist/resources/extensions/gsd/tests/secure-env-collect.test.ts +185 -0
  175. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +123 -0
  176. package/dist/resources/extensions/gsd/tests/test-helpers.ts +61 -0
  177. package/dist/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +219 -0
  178. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +234 -0
  179. package/dist/resources/extensions/gsd/tests/workspace-index.test.ts +75 -0
  180. package/dist/resources/extensions/gsd/tests/worktree-integration.test.ts +234 -0
  181. package/dist/resources/extensions/gsd/tests/worktree-manager.test.ts +141 -0
  182. package/dist/resources/extensions/gsd/tests/worktree.test.ts +597 -0
  183. package/dist/resources/extensions/gsd/tests/write-gate.test.ts +122 -0
  184. package/dist/resources/extensions/gsd/types.ts +296 -0
  185. package/dist/resources/extensions/gsd/unit-runtime.ts +184 -0
  186. package/dist/resources/extensions/gsd/workspace-index.ts +189 -0
  187. package/dist/resources/extensions/gsd/worktree-command.ts +845 -0
  188. package/dist/resources/extensions/gsd/worktree-manager.ts +459 -0
  189. package/dist/resources/extensions/gsd/{worktree.js → worktree.ts} +92 -61
  190. package/dist/resources/extensions/mac-tools/index.ts +852 -0
  191. package/dist/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  192. package/dist/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  193. package/dist/resources/extensions/mcporter/index.ts +429 -0
  194. package/dist/resources/extensions/remote-questions/config.ts +81 -0
  195. package/dist/resources/extensions/remote-questions/discord-adapter.ts +128 -0
  196. package/dist/resources/extensions/remote-questions/format.ts +163 -0
  197. package/dist/resources/extensions/remote-questions/manager.ts +192 -0
  198. package/dist/resources/extensions/remote-questions/remote-command.ts +361 -0
  199. package/dist/resources/extensions/remote-questions/slack-adapter.ts +92 -0
  200. package/dist/resources/extensions/remote-questions/status.ts +31 -0
  201. package/dist/resources/extensions/remote-questions/store.ts +77 -0
  202. package/dist/resources/extensions/remote-questions/types.ts +75 -0
  203. package/dist/resources/extensions/search-the-web/cache.ts +78 -0
  204. package/dist/resources/extensions/search-the-web/command-search-provider.ts +99 -0
  205. package/dist/resources/extensions/search-the-web/format.ts +258 -0
  206. package/dist/resources/extensions/search-the-web/http.ts +238 -0
  207. package/dist/resources/extensions/search-the-web/index.ts +65 -0
  208. package/dist/resources/extensions/search-the-web/native-search.ts +165 -0
  209. package/dist/resources/extensions/search-the-web/provider.ts +136 -0
  210. package/dist/resources/extensions/search-the-web/tavily.ts +116 -0
  211. package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +581 -0
  212. package/dist/resources/extensions/search-the-web/tool-llm-context.ts +620 -0
  213. package/dist/resources/extensions/search-the-web/tool-search.ts +635 -0
  214. package/dist/resources/extensions/search-the-web/url-utils.ts +91 -0
  215. package/dist/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  216. package/dist/resources/extensions/shared/confirm-ui.ts +126 -0
  217. package/dist/resources/extensions/shared/interview-ui.ts +613 -0
  218. package/dist/resources/extensions/shared/next-action-ui.ts +197 -0
  219. package/dist/resources/extensions/shared/progress-widget.ts +282 -0
  220. package/dist/resources/extensions/shared/terminal.ts +23 -0
  221. package/dist/resources/extensions/shared/thinking-widget.ts +107 -0
  222. package/dist/resources/extensions/shared/ui.ts +400 -0
  223. package/dist/resources/extensions/shared/wizard-ui.ts +551 -0
  224. package/dist/resources/extensions/slash-commands/audit.ts +88 -0
  225. package/dist/resources/extensions/slash-commands/clear.ts +10 -0
  226. package/dist/resources/extensions/slash-commands/create-extension.ts +297 -0
  227. package/dist/resources/extensions/slash-commands/create-slash-command.ts +234 -0
  228. package/dist/resources/extensions/slash-commands/index.ts +12 -0
  229. package/dist/resources/extensions/subagent/agents.ts +126 -0
  230. package/dist/resources/extensions/subagent/index.ts +1072 -0
  231. package/dist/resources/extensions/subagent/isolation.ts +498 -0
  232. package/dist/resources/extensions/ttsr/index.ts +163 -0
  233. package/dist/resources/extensions/ttsr/rule-loader.ts +121 -0
  234. package/dist/resources/extensions/ttsr/ttsr-interrupt.md +6 -0
  235. package/dist/resources/extensions/ttsr/ttsr-manager.ts +430 -0
  236. package/dist/resources/extensions/universal-config/discovery.ts +78 -0
  237. package/dist/resources/extensions/universal-config/format.ts +160 -0
  238. package/dist/resources/extensions/universal-config/index.ts +118 -0
  239. package/dist/resources/extensions/universal-config/package.json +11 -0
  240. package/dist/resources/extensions/universal-config/scanners.ts +579 -0
  241. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +111 -0
  242. package/dist/resources/extensions/universal-config/tests/format.test.ts +111 -0
  243. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +438 -0
  244. package/dist/resources/extensions/universal-config/tools.ts +60 -0
  245. package/dist/resources/extensions/universal-config/types.ts +116 -0
  246. package/dist/resources/extensions/voice/index.ts +271 -0
  247. package/dist/resources/extensions/voice/speech-recognizer.py +504 -0
  248. package/dist/resources/extensions/voice/speech-recognizer.swift +154 -0
  249. package/dist/resources/skills/debug-like-expert/SKILL.md +231 -0
  250. package/dist/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  251. package/dist/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  252. package/dist/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  253. package/dist/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  254. package/dist/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  255. package/dist/resources/skills/frontend-design/SKILL.md +45 -0
  256. package/dist/resources/skills/github-workflows/SKILL.md +92 -0
  257. package/dist/resources/skills/github-workflows/references/gh/SKILL.md +255 -0
  258. package/dist/resources/skills/github-workflows/references/gh/references/issue-stories.md +204 -0
  259. package/dist/resources/skills/github-workflows/references/gh/references/labels.md +170 -0
  260. package/dist/resources/skills/github-workflows/references/gh/references/milestones.md +158 -0
  261. package/dist/resources/skills/github-workflows/references/gh/references/projects-v2.md +177 -0
  262. package/dist/resources/skills/github-workflows/references/gh/scripts/experiment_cleanup.py +191 -0
  263. package/dist/resources/skills/github-workflows/references/gh/scripts/github_project_setup.py +799 -0
  264. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  265. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +608 -0
  266. package/dist/resources/skills/swiftui/SKILL.md +208 -0
  267. package/dist/resources/skills/swiftui/references/animations.md +921 -0
  268. package/dist/resources/skills/swiftui/references/architecture.md +1561 -0
  269. package/dist/resources/skills/swiftui/references/layout-system.md +1186 -0
  270. package/dist/resources/skills/swiftui/references/navigation.md +1492 -0
  271. package/dist/resources/skills/swiftui/references/networking-async.md +214 -0
  272. package/dist/resources/skills/swiftui/references/performance.md +1706 -0
  273. package/dist/resources/skills/swiftui/references/platform-integration.md +204 -0
  274. package/dist/resources/skills/swiftui/references/state-management.md +1443 -0
  275. package/dist/resources/skills/swiftui/references/swiftdata.md +297 -0
  276. package/dist/resources/skills/swiftui/references/testing-debugging.md +247 -0
  277. package/dist/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  278. package/dist/resources/skills/swiftui/workflows/add-feature.md +191 -0
  279. package/dist/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  280. package/dist/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  281. package/dist/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  282. package/dist/resources/skills/swiftui/workflows/ship-app.md +203 -0
  283. package/dist/resources/skills/swiftui/workflows/write-tests.md +235 -0
  284. package/dist/wizard.js +1 -0
  285. package/package.json +8 -6
  286. package/packages/native/dist/fd/index.d.ts +1 -0
  287. package/packages/native/dist/fd/index.js +1 -0
  288. package/packages/native/dist/grep/index.d.ts +2 -1
  289. package/packages/native/dist/grep/index.js +1 -0
  290. package/packages/native/src/__tests__/fd.test.mjs +35 -0
  291. package/packages/native/src/__tests__/grep.test.mjs +26 -9
  292. package/packages/native/src/fd/index.ts +1 -0
  293. package/packages/native/src/grep/index.ts +3 -2
  294. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  295. package/packages/pi-agent-core/dist/agent-loop.js +169 -55
  296. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  297. package/packages/pi-agent-core/dist/agent.d.ts +13 -1
  298. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  299. package/packages/pi-agent-core/dist/agent.js +16 -0
  300. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  301. package/packages/pi-agent-core/dist/types.d.ts +91 -1
  302. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  303. package/packages/pi-agent-core/dist/types.js.map +1 -1
  304. package/packages/pi-agent-core/src/agent-loop.ts +273 -63
  305. package/packages/pi-agent-core/src/agent.ts +24 -0
  306. package/packages/pi-agent-core/src/types.ts +98 -0
  307. package/packages/pi-ai/dist/env-api-keys.js +2 -0
  308. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  309. package/packages/pi-ai/dist/models.generated.d.ts +314 -0
  310. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  311. package/packages/pi-ai/dist/models.generated.js +236 -0
  312. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  313. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  314. package/packages/pi-ai/dist/providers/google-gemini-cli.js +6 -0
  315. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  316. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  317. package/packages/pi-ai/dist/providers/openai-completions.js +2 -0
  318. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  319. package/packages/pi-ai/dist/types.d.ts +1 -1
  320. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  321. package/packages/pi-ai/dist/types.js.map +1 -1
  322. package/packages/pi-ai/src/env-api-keys.ts +2 -0
  323. package/packages/pi-ai/src/models.generated.ts +236 -0
  324. package/packages/pi-ai/src/providers/google-gemini-cli.ts +8 -0
  325. package/packages/pi-ai/src/providers/openai-completions.ts +3 -0
  326. package/packages/pi-ai/src/types.ts +2 -1
  327. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  328. package/packages/pi-coding-agent/dist/cli/args.js +1 -0
  329. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +26 -0
  331. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/agent-session.js +137 -22
  333. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +20 -0
  335. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/core/auth-storage.js +43 -0
  337. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts +51 -0
  339. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +119 -0
  341. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts +2 -0
  343. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +183 -0
  345. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  347. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  349. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  352. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +12 -1
  354. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/core/session-manager.js +37 -1
  356. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts +2 -0
  358. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts.map +1 -0
  359. package/packages/pi-coding-agent/dist/core/session-manager.test.js +61 -0
  360. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -0
  361. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  362. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  363. package/packages/pi-coding-agent/dist/core/settings-manager.js +47 -0
  364. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  365. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/core/tools/find.js +1 -0
  367. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +6 -15
  370. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +18 -1
  372. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  373. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +46 -4
  374. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  375. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  376. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -2
  377. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  378. package/packages/pi-coding-agent/src/cli/args.ts +1 -0
  379. package/packages/pi-coding-agent/src/core/agent-session.ts +167 -22
  380. package/packages/pi-coding-agent/src/core/auth-storage.ts +45 -0
  381. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +229 -0
  382. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +165 -0
  383. package/packages/pi-coding-agent/src/core/index.ts +1 -0
  384. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  385. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -0
  386. package/packages/pi-coding-agent/src/core/session-manager.ts +51 -1
  387. package/packages/pi-coding-agent/src/core/settings-manager.ts +66 -0
  388. package/packages/pi-coding-agent/src/core/tools/find.ts +1 -0
  389. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +6 -16
  390. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +49 -4
  391. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -2
  392. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  393. package/packages/pi-tui/dist/autocomplete.js +3 -4
  394. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  395. package/packages/pi-tui/dist/components/editor.d.ts +7 -0
  396. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  397. package/packages/pi-tui/dist/components/editor.js +57 -50
  398. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  399. package/packages/pi-tui/src/autocomplete.ts +3 -5
  400. package/packages/pi-tui/src/components/editor.ts +70 -49
  401. package/pkg/dist/core/export-html/ansi-to-html.d.ts +22 -0
  402. package/pkg/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  403. package/pkg/dist/core/export-html/ansi-to-html.js +249 -0
  404. package/pkg/dist/core/export-html/ansi-to-html.js.map +1 -0
  405. package/pkg/dist/core/export-html/index.d.ts +37 -0
  406. package/pkg/dist/core/export-html/index.d.ts.map +1 -0
  407. package/pkg/dist/core/export-html/index.js +223 -0
  408. package/pkg/dist/core/export-html/index.js.map +1 -0
  409. package/pkg/dist/core/export-html/template.css +971 -0
  410. package/pkg/dist/core/export-html/template.html +54 -0
  411. package/pkg/dist/core/export-html/template.js +1583 -0
  412. package/pkg/dist/core/export-html/tool-renderer.d.ts +38 -0
  413. package/pkg/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  414. package/pkg/dist/core/export-html/tool-renderer.js +70 -0
  415. package/pkg/dist/core/export-html/tool-renderer.js.map +1 -0
  416. package/pkg/dist/core/export-html/vendor/highlight.min.js +1213 -0
  417. package/pkg/dist/core/export-html/vendor/marked.min.js +6 -0
  418. package/scripts/postinstall.js +30 -17
  419. package/src/resources/extensions/bg-shell/index.ts +51 -7
  420. package/src/resources/extensions/gsd/activity-log.ts +61 -14
  421. package/src/resources/extensions/gsd/auto.ts +288 -19
  422. package/src/resources/extensions/gsd/commands.ts +9 -3
  423. package/src/resources/extensions/gsd/dashboard-overlay.ts +55 -12
  424. package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -0
  425. package/src/resources/extensions/gsd/doctor.ts +60 -3
  426. package/src/resources/extensions/gsd/files.ts +45 -1
  427. package/src/resources/extensions/gsd/git-service.ts +46 -41
  428. package/src/resources/extensions/gsd/guided-flow.ts +81 -9
  429. package/src/resources/extensions/gsd/index.ts +62 -1
  430. package/src/resources/extensions/gsd/native-git-bridge.ts +181 -0
  431. package/src/resources/extensions/gsd/paths.ts +35 -4
  432. package/src/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  433. package/src/resources/extensions/gsd/preferences.ts +232 -1
  434. package/src/resources/extensions/gsd/prompt-loader.ts +45 -3
  435. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  436. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -3
  437. package/src/resources/extensions/gsd/prompts/discuss.md +10 -8
  438. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  439. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
  440. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
  441. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  442. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
  443. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
  444. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
  445. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
  446. package/src/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
  447. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -3
  448. package/src/resources/extensions/gsd/prompts/queue.md +3 -1
  449. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  450. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  451. package/src/resources/extensions/gsd/state.ts +130 -18
  452. package/src/resources/extensions/gsd/templates/context.md +1 -1
  453. package/src/resources/extensions/gsd/templates/state.md +3 -3
  454. package/src/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  455. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +5 -1
  456. package/src/resources/extensions/gsd/tests/doctor.test.ts +115 -1
  457. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +6 -2
  458. package/src/resources/extensions/gsd/tests/git-service.test.ts +35 -35
  459. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +8 -8
  460. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +4 -1
  461. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  462. package/src/resources/extensions/gsd/tests/orphaned-branch.test.ts +5 -5
  463. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  464. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  465. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
  466. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +6 -0
  467. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +4 -4
  468. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +4 -4
  469. package/src/resources/extensions/gsd/tests/worktree.test.ts +19 -19
  470. package/src/resources/extensions/gsd/types.ts +109 -0
  471. package/src/resources/extensions/gsd/worktree-manager.ts +72 -13
  472. package/src/resources/extensions/gsd/worktree.ts +5 -4
  473. package/src/resources/extensions/search-the-web/command-search-provider.ts +8 -4
  474. package/src/resources/extensions/search-the-web/native-search.ts +32 -23
  475. package/src/resources/extensions/search-the-web/provider.ts +19 -2
  476. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
  477. package/src/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
  478. package/src/resources/extensions/search-the-web/tool-search.ts +62 -3
  479. package/src/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  480. package/src/resources/extensions/subagent/index.ts +2 -1
  481. package/src/resources/skills/github-workflows/SKILL.md +5 -0
  482. package/dist/resources/extensions/gsd/git-service.d.ts +0 -225
  483. package/dist/resources/extensions/gsd/git-service.js +0 -756
  484. package/dist/resources/extensions/gsd/preferences.d.ts +0 -122
  485. package/dist/resources/extensions/gsd/preferences.js +0 -616
  486. package/dist/resources/extensions/gsd/worktree.d.ts +0 -106
  487. package/dist/resources/extensions/remote-questions/config.d.ts +0 -14
  488. package/dist/resources/extensions/remote-questions/config.js +0 -68
  489. package/dist/resources/extensions/remote-questions/discord-adapter.d.ts +0 -17
  490. package/dist/resources/extensions/remote-questions/discord-adapter.js +0 -118
  491. package/dist/resources/extensions/remote-questions/format.d.ts +0 -38
  492. package/dist/resources/extensions/remote-questions/format.js +0 -119
  493. package/dist/resources/extensions/remote-questions/manager.d.ts +0 -23
  494. package/dist/resources/extensions/remote-questions/manager.js +0 -161
  495. package/dist/resources/extensions/remote-questions/remote-command.d.ts +0 -6
  496. package/dist/resources/extensions/remote-questions/remote-command.js +0 -358
  497. package/dist/resources/extensions/remote-questions/slack-adapter.d.ts +0 -15
  498. package/dist/resources/extensions/remote-questions/slack-adapter.js +0 -82
  499. package/dist/resources/extensions/remote-questions/status.d.ts +0 -9
  500. package/dist/resources/extensions/remote-questions/status.js +0 -25
  501. package/dist/resources/extensions/remote-questions/store.d.ts +0 -11
  502. package/dist/resources/extensions/remote-questions/store.js +0 -66
  503. package/dist/resources/extensions/remote-questions/types.d.ts +0 -68
  504. package/dist/resources/extensions/remote-questions/types.js +0 -4
@@ -31,14 +31,14 @@ function run(command: string, cwd: string): string {
31
31
 
32
32
  const base = mkdtempSync(join(tmpdir(), "gsd-branch-test-"));
33
33
  run("git init -b main", base);
34
- run("git config user.name 'Pi Test'", base);
35
- run("git config user.email 'pi@example.com'", base);
34
+ run('git config user.name "Pi Test"', base);
35
+ run('git config user.email "pi@example.com"', base);
36
36
  mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
37
37
  writeFileSync(join(base, "README.md"), "hello\n", "utf-8");
38
38
  writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), `# M001: Demo\n\n## Slices\n- [ ] **S01: Slice One** \`risk:low\` \`depends:[]\`\n > After this: demo works\n`, "utf-8");
39
39
  writeFileSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md"), `# S01: Slice One\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Must-Haves\n- done\n\n## Tasks\n- [ ] **T01: Implement** \`est:10m\`\n do it\n`, "utf-8");
40
40
  run("git add .", base);
41
- run("git commit -m 'chore: init'", base);
41
+ run('git commit -m "chore: init"', base);
42
42
 
43
43
  async function main(): Promise<void> {
44
44
  console.log("\n=== ensureSliceBranch ===");
@@ -85,7 +85,7 @@ async function main(): Promise<void> {
85
85
  ensureSliceBranch(base, "M001", "S01");
86
86
  writeFileSync(join(base, "README.md"), "hello from slice\n", "utf-8");
87
87
  run("git add README.md", base);
88
- run("git commit -m 'feat: slice change'", base);
88
+ run('git commit -m "feat: slice change"', base);
89
89
  switchToMain(base);
90
90
 
91
91
  const merge = mergeSliceToMain(base, "M001", "S01", "Slice One");
@@ -107,7 +107,7 @@ async function main(): Promise<void> {
107
107
  "- [ ] **S02: Slice Two** `risk:low` `depends:[]`", " > Demo 2",
108
108
  ].join("\n") + "\n", "utf-8");
109
109
  run("git add .", base);
110
- run("git commit -m 'chore: add S02'", base);
110
+ run('git commit -m "chore: add S02"', base);
111
111
 
112
112
  ensureSliceBranch(base, "M001", "S02");
113
113
  writeFileSync(join(base, "feature.txt"), "new feature\n", "utf-8");
@@ -168,11 +168,11 @@ async function main(): Promise<void> {
168
168
  console.log("\n=== ensureSliceBranch from non-main working branch ===");
169
169
  const base2 = mkdtempSync(join(tmpdir(), "gsd-branch-base-test-"));
170
170
  run("git init -b main", base2);
171
- run("git config user.name 'Pi Test'", base2);
172
- run("git config user.email 'pi@example.com'", base2);
171
+ run('git config user.name "Pi Test"', base2);
172
+ run('git config user.email "pi@example.com"', base2);
173
173
  writeFileSync(join(base2, "README.md"), "hello\n", "utf-8");
174
174
  run("git add .", base2);
175
- run("git commit -m 'chore: init'", base2);
175
+ run('git commit -m "chore: init"', base2);
176
176
 
177
177
  // Create a "developer" branch with planning artifacts (like the real scenario)
178
178
  run("git checkout -b developer", base2);
@@ -183,7 +183,7 @@ async function main(): Promise<void> {
183
183
  "- [ ] **S01: Config Fix** `risk:low` `depends:[]`", " > Fix config",
184
184
  ].join("\n") + "\n", "utf-8");
185
185
  run("git add .", base2);
186
- run("git commit -m 'docs(M001): context and roadmap'", base2);
186
+ run('git commit -m "docs(M001): context and roadmap"', base2);
187
187
 
188
188
  // Verify main does NOT have the artifacts
189
189
  const mainRoadmap = run("git show main:.gsd/milestones/M001/M001-ROADMAP.md 2>&1 || echo MISSING", base2);
@@ -211,8 +211,8 @@ async function main(): Promise<void> {
211
211
  console.log("\n=== ensureSliceBranch from slice branch falls back to main ===");
212
212
  const base3 = mkdtempSync(join(tmpdir(), "gsd-branch-chain-test-"));
213
213
  run("git init -b main", base3);
214
- run("git config user.name 'Pi Test'", base3);
215
- run("git config user.email 'pi@example.com'", base3);
214
+ run('git config user.name "Pi Test"', base3);
215
+ run('git config user.email "pi@example.com"', base3);
216
216
  mkdirSync(join(base3, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
217
217
  mkdirSync(join(base3, ".gsd", "milestones", "M001", "slices", "S02", "tasks"), { recursive: true });
218
218
  writeFileSync(join(base3, "README.md"), "hello\n", "utf-8");
@@ -222,7 +222,7 @@ async function main(): Promise<void> {
222
222
  "- [ ] **S02: Second** `risk:low` `depends:[]`", " > second",
223
223
  ].join("\n") + "\n", "utf-8");
224
224
  run("git add .", base3);
225
- run("git commit -m 'chore: init'", base3);
225
+ run('git commit -m "chore: init"', base3);
226
226
 
227
227
  ensureSliceBranch(base3, "M001", "S01");
228
228
  assertEq(getCurrentBranch(base3), "gsd/M001/S01", "on S01 slice branch");
@@ -379,7 +379,7 @@ async function main(): Promise<void> {
379
379
  // User creates feature branch
380
380
  run("git checkout -b feature/big-change", repo);
381
381
  writeFileSync(join(repo, "setup.txt"), "feature setup\n");
382
- run("git add -A && git commit -m 'feat: initial setup'", repo);
382
+ run('git add -A && git commit -m "feat: initial setup"', repo);
383
383
 
384
384
  // auto.ts startup: capture + set milestone
385
385
  captureIntegrationBranch(repo, "M001");
@@ -397,7 +397,7 @@ async function main(): Promise<void> {
397
397
  "multi: S01 inherited feature branch content");
398
398
 
399
399
  writeFileSync(join(repo, "s01-work.txt"), "s01 output\n");
400
- run("git add -A && git commit -m 'feat(S01): work'", repo);
400
+ run('git add -A && git commit -m "feat(S01): work"', repo);
401
401
 
402
402
  switchToMain(repo);
403
403
  assertEq(getCurrentBranch(repo), "feature/big-change",
@@ -428,7 +428,7 @@ async function main(): Promise<void> {
428
428
  "multi: S02 has S01 output (inherited via feature branch)");
429
429
 
430
430
  writeFileSync(join(repo, "s02-work.txt"), "s02 output\n");
431
- run("git add -A && git commit -m 'feat(S02): work'", repo);
431
+ run('git add -A && git commit -m "feat(S02): work"', repo);
432
432
 
433
433
  switchToMain(repo);
434
434
  assertEq(getCurrentBranch(repo), "feature/big-change",
@@ -479,7 +479,7 @@ async function main(): Promise<void> {
479
479
  // Create a slice and do some work
480
480
  ensureSliceBranch(repo, "M001", "S01");
481
481
  writeFileSync(join(repo, "work.txt"), "wip\n");
482
- run("git add -A && git commit -m 'wip'", repo);
482
+ run('git add -A && git commit -m "wip"', repo);
483
483
 
484
484
  // Simulate "restart" — clear milestone ID (fresh service instance)
485
485
  setActiveMilestoneId(repo, null);
@@ -530,7 +530,7 @@ async function main(): Promise<void> {
530
530
  // Full lifecycle on main still works
531
531
  ensureSliceBranch(repo, "M001", "S01");
532
532
  writeFileSync(join(repo, "feature.txt"), "new\n");
533
- run("git add -A && git commit -m 'feat: work'", repo);
533
+ run('git add -A && git commit -m "feat: work"', repo);
534
534
 
535
535
  switchToMain(repo);
536
536
  assertEq(getCurrentBranch(repo), "main",
@@ -564,7 +564,7 @@ async function main(): Promise<void> {
564
564
 
565
565
  run("git checkout -b dev-branch", repo);
566
566
  writeFileSync(join(repo, "dev-only.txt"), "from dev\n");
567
- run("git add -A && git commit -m 'dev setup'", repo);
567
+ run('git add -A && git commit -m "dev setup"', repo);
568
568
 
569
569
  captureIntegrationBranch(repo, "M001");
570
570
  setActiveMilestoneId(repo, "M001");
@@ -572,7 +572,7 @@ async function main(): Promise<void> {
572
572
  // Create S01 (from dev-branch)
573
573
  ensureSliceBranch(repo, "M001", "S01");
574
574
  writeFileSync(join(repo, "s01.txt"), "s01\n");
575
- run("git add -A && git commit -m 's01 work'", repo);
575
+ run('git add -A && git commit -m "s01 work"', repo);
576
576
 
577
577
  // While on S01, create S02 — should fall back to integration branch
578
578
  ensureSliceBranch(repo, "M001", "S02");
@@ -185,3 +185,112 @@ export interface GSDState {
185
185
  tasks?: { done: number; total: number };
186
186
  };
187
187
  }
188
+
189
+ // ─── Post-Unit Hook Types ─────────────────────────────────────────────────
190
+
191
+ export interface PostUnitHookConfig {
192
+ /** Unique hook identifier — used in idempotency keys and logging. */
193
+ name: string;
194
+ /** Unit types that trigger this hook (e.g., ["execute-task"]). */
195
+ after: string[];
196
+ /** Prompt sent to the LLM session. Supports {milestoneId}, {sliceId}, {taskId} substitutions. */
197
+ prompt: string;
198
+ /** Max times this hook can fire for the same trigger unit. Default 1, max 10. */
199
+ max_cycles?: number;
200
+ /** Model override for hook sessions. */
201
+ model?: string;
202
+ /** Expected output file name (relative to task/slice dir). Used for idempotency — skip if exists. */
203
+ artifact?: string;
204
+ /** If this file is produced instead of artifact, re-run the trigger unit then re-run hooks. */
205
+ retry_on?: string;
206
+ /** Agent definition file to use. */
207
+ agent?: string;
208
+ /** Set false to disable without removing config. Default true. */
209
+ enabled?: boolean;
210
+ }
211
+
212
+ export interface HookExecutionState {
213
+ /** Hook name. */
214
+ hookName: string;
215
+ /** The unit type that triggered this hook. */
216
+ triggerUnitType: string;
217
+ /** The unit ID that triggered this hook. */
218
+ triggerUnitId: string;
219
+ /** Current cycle (1-based). */
220
+ cycle: number;
221
+ /** Whether the hook completed with a retry signal (retry_on artifact found). */
222
+ pendingRetry: boolean;
223
+ }
224
+
225
+ export interface HookDispatchResult {
226
+ /** Hook name for display. */
227
+ hookName: string;
228
+ /** The prompt to send. */
229
+ prompt: string;
230
+ /** Model override, if configured. */
231
+ model?: string;
232
+ /** Synthetic unit type, e.g. "hook/code-review". */
233
+ unitType: string;
234
+ /** The trigger unit's ID, reused for the hook. */
235
+ unitId: string;
236
+ }
237
+
238
+ // ─── Pre-Dispatch Hook Types ──────────────────────────────────────────────
239
+
240
+ export interface PreDispatchHookConfig {
241
+ /** Unique hook identifier. */
242
+ name: string;
243
+ /** Unit types this hook intercepts before dispatch (e.g., ["execute-task"]). */
244
+ before: string[];
245
+ /** Action to take: "modify" mutates the prompt, "skip" skips the unit, "replace" swaps it. */
246
+ action: 'modify' | 'skip' | 'replace';
247
+ /** For "modify": text prepended to the unit prompt. Supports {milestoneId}, {sliceId}, {taskId}. */
248
+ prepend?: string;
249
+ /** For "modify": text appended to the unit prompt. Supports {milestoneId}, {sliceId}, {taskId}. */
250
+ append?: string;
251
+ /** For "replace": the replacement prompt. Supports {milestoneId}, {sliceId}, {taskId}. */
252
+ prompt?: string;
253
+ /** For "replace": override the unit type label. */
254
+ unit_type?: string;
255
+ /** For "skip": optional condition file — only skip if this file exists (relative to unit dir). */
256
+ skip_if?: string;
257
+ /** Model override when this hook fires. */
258
+ model?: string;
259
+ /** Set false to disable without removing config. Default true. */
260
+ enabled?: boolean;
261
+ }
262
+
263
+ export interface PreDispatchResult {
264
+ /** What happened: the unit proceeds with modifications, was skipped, or was replaced. */
265
+ action: 'proceed' | 'skip' | 'replace';
266
+ /** Modified/replacement prompt (for "proceed" and "replace"). */
267
+ prompt?: string;
268
+ /** Override unit type (for "replace"). */
269
+ unitType?: string;
270
+ /** Model override. */
271
+ model?: string;
272
+ /** Names of hooks that fired, for logging. */
273
+ firedHooks: string[];
274
+ }
275
+
276
+ // ─── Hook State Persistence Types ─────────────────────────────────────────
277
+
278
+ export interface PersistedHookState {
279
+ /** Cycle counts keyed as "hookName/triggerUnitType/triggerUnitId". */
280
+ cycleCounts: Record<string, number>;
281
+ /** Timestamp of last state save. */
282
+ savedAt: string;
283
+ }
284
+
285
+ export interface HookStatusEntry {
286
+ /** Hook name. */
287
+ name: string;
288
+ /** Hook type: "post" or "pre". */
289
+ type: 'post' | 'pre';
290
+ /** Whether hook is enabled. */
291
+ enabled: boolean;
292
+ /** What unit types it targets. */
293
+ targets: string[];
294
+ /** Current cycle counts for active triggers. */
295
+ activeCycles: Record<string, number>;
296
+ }
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { existsSync, mkdirSync, realpathSync } from "node:fs";
19
19
  import { execSync } from "node:child_process";
20
- import { join, relative, resolve } from "node:path";
20
+ import { join, resolve } from "node:path";
21
21
 
22
22
  // ─── Types ─────────────────────────────────────────────────────────────────
23
23
 
@@ -46,13 +46,27 @@ export interface WorktreeDiffSummary {
46
46
 
47
47
  // ─── Git Helpers ───────────────────────────────────────────────────────────
48
48
 
49
- /** Env overlay that suppresses all interactive git credential prompts. */
49
+ /** Env overlay that suppresses interactive git credential prompts and git-svn noise. */
50
50
  const GIT_NO_PROMPT_ENV = {
51
51
  ...process.env,
52
52
  GIT_TERMINAL_PROMPT: "0",
53
53
  GIT_ASKPASS: "",
54
+ GIT_SVN_ID: "",
54
55
  };
55
56
 
57
+ /**
58
+ * Strip git-svn noise from error messages.
59
+ * Some systems have a buggy git-svn Perl module that emits warnings
60
+ * on every git invocation. See #404.
61
+ */
62
+ function filterGitSvnNoise(message: string): string {
63
+ return message
64
+ .replace(/Duplicate specification "[^"]*" for option "[^"]*"\n?/g, "")
65
+ .replace(/Unable to determine upstream SVN information from .*\n?/g, "")
66
+ .replace(/Perhaps the repository is empty\. at .*git-svn.*\n?/g, "")
67
+ .trim();
68
+ }
69
+
56
70
  function runGit(cwd: string, args: string[], opts: { allowFailure?: boolean } = {}): string {
57
71
  try {
58
72
  return execSync(`git ${args.join(" ")}`, {
@@ -64,10 +78,18 @@ function runGit(cwd: string, args: string[], opts: { allowFailure?: boolean } =
64
78
  } catch (error) {
65
79
  if (opts.allowFailure) return "";
66
80
  const message = error instanceof Error ? error.message : String(error);
67
- throw new Error(`git ${args.join(" ")} failed in ${cwd}: ${message}`);
81
+ throw new Error(`git ${args.join(" ")} failed in ${cwd}: ${filterGitSvnNoise(message)}`);
68
82
  }
69
83
  }
70
84
 
85
+ function normalizePathForComparison(path: string): string {
86
+ const normalized = path
87
+ .replaceAll("\\", "/")
88
+ .replace(/^\/\/\?\//, "")
89
+ .replace(/\/+$/, "");
90
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
91
+ }
92
+
71
93
  export function getMainBranch(basePath: string): string {
72
94
  const symbolic = runGit(basePath, ["symbolic-ref", "refs/remotes/origin/HEAD"], { allowFailure: true });
73
95
  if (symbolic) {
@@ -156,15 +178,29 @@ export function createWorktree(basePath: string, name: string): WorktreeInfo {
156
178
  * Parses `git worktree list` and filters to those under .gsd/worktrees/.
157
179
  */
158
180
  export function listWorktrees(basePath: string): WorktreeInfo[] {
159
- // Resolve real paths to handle symlinks (e.g. /tmp → /private/tmp on macOS)
160
- const resolvedBase = existsSync(basePath) ? realpathSync(basePath) : resolve(basePath);
161
- const wtDir = join(resolvedBase, ".gsd", "worktrees");
181
+ const baseVariants = [resolve(basePath)];
182
+ if (existsSync(basePath)) {
183
+ baseVariants.push(realpathSync(basePath));
184
+ }
185
+ const seenRoots = new Set<string>();
186
+ const worktreeRoots = baseVariants
187
+ .map(baseVariant => {
188
+ const path = join(baseVariant, ".gsd", "worktrees");
189
+ return {
190
+ normalized: normalizePathForComparison(path),
191
+ };
192
+ })
193
+ .filter(root => {
194
+ if (seenRoots.has(root.normalized)) return false;
195
+ seenRoots.add(root.normalized);
196
+ return true;
197
+ });
162
198
  const rawList = runGit(basePath, ["worktree", "list", "--porcelain"]);
163
199
 
164
200
  if (!rawList.trim()) return [];
165
201
 
166
202
  const worktrees: WorktreeInfo[] = [];
167
- const entries = rawList.split("\n\n").filter(Boolean);
203
+ const entries = rawList.replaceAll("\r\n", "\n").split("\n\n").filter(Boolean);
168
204
 
169
205
  for (const entry of entries) {
170
206
  const lines = entry.split("\n");
@@ -175,19 +211,42 @@ export function listWorktrees(basePath: string): WorktreeInfo[] {
175
211
 
176
212
  const entryPath = wtLine.replace("worktree ", "");
177
213
  const branch = branchLine.replace("branch refs/heads/", "");
214
+ const branchWorktreeName = branch.startsWith("worktree/") ? branch.slice("worktree/".length) : null;
215
+ const entryVariants = [resolve(entryPath)];
216
+ if (existsSync(entryPath)) {
217
+ entryVariants.push(realpathSync(entryPath));
218
+ }
219
+ const normalizedEntryVariants = [...new Set(entryVariants.map(normalizePathForComparison))];
220
+ const matchedRoot = worktreeRoots.find(root =>
221
+ normalizedEntryVariants.some(entryVariant => entryVariant.startsWith(`${root.normalized}/`)),
222
+ );
223
+ const matchesBranchLeaf = branchWorktreeName
224
+ ? normalizedEntryVariants.some(entryVariant => entryVariant.split("/").pop() === branchWorktreeName)
225
+ : false;
178
226
 
179
227
  // Only include worktrees under .gsd/worktrees/
180
- if (!entryPath.startsWith(wtDir)) continue;
228
+ if (!matchedRoot && !matchesBranchLeaf) continue;
229
+
230
+ const matchedEntryPath = normalizedEntryVariants.find(entryVariant =>
231
+ matchedRoot ? entryVariant.startsWith(`${matchedRoot.normalized}/`) : false,
232
+ );
233
+ let name = matchedRoot ? matchedEntryPath?.slice(matchedRoot.normalized.length + 1) ?? "" : "";
234
+
235
+ // Git on Windows can report a path form that does not map cleanly back to the
236
+ // repo root even when the branch naming is still authoritative.
237
+ if ((!name || name.includes("/")) && branchWorktreeName && matchesBranchLeaf) {
238
+ name = branchWorktreeName;
239
+ }
240
+
241
+ if (!name || name.includes("/")) continue;
181
242
 
182
- const name = relative(wtDir, entryPath);
183
- // Skip nested paths — only direct children
184
- if (name.includes("/") || name.includes("\\")) continue;
243
+ const resolvedEntryPath = existsSync(entryPath) ? realpathSync(entryPath) : resolve(entryPath);
185
244
 
186
245
  worktrees.push({
187
246
  name,
188
- path: entryPath,
247
+ path: resolvedEntryPath,
189
248
  branch,
190
- exists: existsSync(entryPath),
249
+ exists: existsSync(resolvedEntryPath),
191
250
  });
192
251
  }
193
252
 
@@ -72,11 +72,12 @@ export function captureIntegrationBranch(basePath: string, milestoneId: string):
72
72
  * Returns null if not inside a GSD worktree (.gsd/worktrees/<name>/).
73
73
  */
74
74
  export function detectWorktreeName(basePath: string): string | null {
75
- const marker = `${sep}.gsd${sep}worktrees${sep}`;
76
- const idx = basePath.indexOf(marker);
75
+ const normalizedPath = basePath.replaceAll("\\", "/");
76
+ const marker = "/.gsd/worktrees/";
77
+ const idx = normalizedPath.indexOf(marker);
77
78
  if (idx === -1) return null;
78
- const afterMarker = basePath.slice(idx + marker.length);
79
- const name = afterMarker.split(sep)[0] ?? afterMarker.split("/")[0];
79
+ const afterMarker = normalizedPath.slice(idx + marker.length);
80
+ const name = afterMarker.split("/")[0];
80
81
  return name || null;
81
82
  }
82
83
 
@@ -13,16 +13,18 @@ import type { AutocompleteItem } from '@gsd/pi-tui'
13
13
  import {
14
14
  getTavilyApiKey,
15
15
  getBraveApiKey,
16
+ getOllamaApiKey,
16
17
  getSearchProviderPreference,
17
18
  setSearchProviderPreference,
18
19
  resolveSearchProvider,
19
20
  type SearchProviderPreference,
20
21
  } from './provider.ts'
21
22
 
22
- const VALID_PREFERENCES: SearchProviderPreference[] = ['tavily', 'brave', 'auto']
23
+ const VALID_PREFERENCES: SearchProviderPreference[] = ['tavily', 'brave', 'ollama', 'auto']
23
24
 
24
- function keyStatus(provider: 'tavily' | 'brave'): string {
25
+ function keyStatus(provider: 'tavily' | 'brave' | 'ollama'): string {
25
26
  if (provider === 'tavily') return getTavilyApiKey() ? '✓' : '✗'
27
+ if (provider === 'ollama') return getOllamaApiKey() ? '✓' : '✗'
26
28
  return getBraveApiKey() ? '✓' : '✗'
27
29
  }
28
30
 
@@ -30,6 +32,7 @@ function buildSelectOptions(): string[] {
30
32
  return [
31
33
  `tavily (key: ${keyStatus('tavily')})`,
32
34
  `brave (key: ${keyStatus('brave')})`,
35
+ `ollama (key: ${keyStatus('ollama')})`,
33
36
  `auto`,
34
37
  ]
35
38
  }
@@ -37,12 +40,13 @@ function buildSelectOptions(): string[] {
37
40
  function parseSelectChoice(choice: string): SearchProviderPreference {
38
41
  if (choice.startsWith('tavily')) return 'tavily'
39
42
  if (choice.startsWith('brave')) return 'brave'
43
+ if (choice.startsWith('ollama')) return 'ollama'
40
44
  return 'auto'
41
45
  }
42
46
 
43
47
  export function registerSearchProviderCommand(pi: ExtensionAPI): void {
44
48
  pi.registerCommand('search-provider', {
45
- description: 'Switch search provider (tavily, brave, auto)',
49
+ description: 'Switch search provider (tavily, brave, ollama, auto)',
46
50
 
47
51
  getArgumentCompletions(prefix: string): AutocompleteItem[] | null {
48
52
  const trimmed = prefix.trim().toLowerCase()
@@ -51,7 +55,7 @@ export function registerSearchProviderCommand(pi: ExtensionAPI): void {
51
55
  .map((p) => {
52
56
  let description: string
53
57
  if (p === 'auto') {
54
- description = `Auto-select (tavily: ${keyStatus('tavily')}, brave: ${keyStatus('brave')})`
58
+ description = `Auto-select (tavily: ${keyStatus('tavily')}, brave: ${keyStatus('brave')}, ollama: ${keyStatus('ollama')})`
55
59
  } else {
56
60
  description = `key: ${keyStatus(p)}`
57
61
  }
@@ -8,6 +8,9 @@
8
8
  /** Tool names for the Brave-backed custom search tools */
9
9
  export const BRAVE_TOOL_NAMES = ["search-the-web", "search_and_read"];
10
10
 
11
+ /** All custom search tool names that should be disabled when native search is active */
12
+ export const CUSTOM_SEARCH_TOOL_NAMES = ["search-the-web", "search_and_read", "google_search"];
13
+
11
14
  /** Thinking block types that require signature validation by the API */
12
15
  const THINKING_TYPES = new Set(["thinking", "redacted_thinking"]);
13
16
 
@@ -57,27 +60,30 @@ export function stripThinkingFromHistory(
57
60
  */
58
61
  export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic: () => boolean } {
59
62
  let isAnthropicProvider = false;
63
+ let modelSelectFired = false;
60
64
 
61
65
  // Track provider changes via model selection — also handles diagnostics
62
66
  // since model_select fires AFTER session_start and knows the provider.
63
67
  pi.on("model_select", async (event: any, ctx: any) => {
68
+ modelSelectFired = true;
64
69
  const wasAnthropic = isAnthropicProvider;
65
70
  isAnthropicProvider = event.model.provider === "anthropic";
66
71
 
67
72
  const hasBrave = !!process.env.BRAVE_API_KEY;
68
73
 
69
- // When Anthropic + no Brave key: disable custom search tools (they'd fail)
70
- if (isAnthropicProvider && !hasBrave) {
74
+ // When Anthropic: disable all custom search tools native web_search is
75
+ // server-side and more reliable. Custom tools cause confusion and failures.
76
+ if (isAnthropicProvider) {
71
77
  const active = pi.getActiveTools();
72
78
  pi.setActiveTools(
73
- active.filter((t: string) => !BRAVE_TOOL_NAMES.includes(t))
79
+ active.filter((t: string) => !CUSTOM_SEARCH_TOOL_NAMES.includes(t))
74
80
  );
75
- } else if (!isAnthropicProvider && wasAnthropic && !hasBrave) {
76
- // Switching away from Anthropic without Brave — re-enable so the user
77
- // sees the "missing key" error rather than tools silently vanishing.
78
- // Only add tools not already active to avoid duplicates on repeated toggles.
81
+ } else if (!isAnthropicProvider && wasAnthropic) {
82
+ // Switching away from Anthropic — re-enable custom search tools (they
83
+ // were disabled while native search was active). If keys are missing,
84
+ // user sees the error rather than tools silently vanishing.
79
85
  const active = pi.getActiveTools();
80
- const toAdd = BRAVE_TOOL_NAMES.filter((t) => !active.includes(t));
86
+ const toAdd = CUSTOM_SEARCH_TOOL_NAMES.filter((t) => !active.includes(t));
81
87
  if (toAdd.length > 0) {
82
88
  pi.setActiveTools([...active, ...toAdd]);
83
89
  }
@@ -99,11 +105,17 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
99
105
  const payload = event.payload as Record<string, unknown>;
100
106
  if (!payload) return;
101
107
 
102
- // Only inject native web search for confirmed Anthropic provider.
103
- // model_select sets isAnthropicProvider via the provider field.
104
- // Model name prefix is NOT sufficient other providers (GitHub Copilot,
105
- // AWS Bedrock, etc.) serve claude-* models through non-Anthropic APIs.
106
- if (!isAnthropicProvider) return;
108
+ // Detect Anthropic provider. Prefer the model_select flag when available,
109
+ // but fall back to checking the model name in the payload. model_select
110
+ // may not fire when the session restores with the same model already set
111
+ // (modelsAreEqual guard in the SDK suppresses the event). When model_select
112
+ // HAS fired and said "not Anthropic" (e.g. Copilot serving claude-*),
113
+ // respect that — don't override with model name heuristic.
114
+ const modelName = typeof payload.model === "string" ? payload.model : "";
115
+ const isAnthropic = modelSelectFired
116
+ ? isAnthropicProvider
117
+ : modelName.startsWith("claude-");
118
+ if (!isAnthropic) return;
107
119
 
108
120
  // Strip thinking blocks from history to avoid signature validation errors
109
121
  // caused by the SDK dropping server_tool_use/web_search_tool_result blocks.
@@ -119,16 +131,13 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
119
131
  // Don't double-inject if already present
120
132
  if (tools.some((t) => t.type === "web_search_20250305")) return;
121
133
 
122
- // When no Brave key, remove Brave-based search tool definitions from the
123
- // payload so Claude doesn't see (and try to call) broken tools.
124
- // This is more reliable than setActiveTools since model_select may not fire.
125
- const hasBrave = !!process.env.BRAVE_API_KEY;
126
- if (!hasBrave) {
127
- tools = tools.filter(
128
- (t) => !BRAVE_TOOL_NAMES.includes(t.name as string)
129
- );
130
- payload.tools = tools;
131
- }
134
+ // Always remove custom search tool definitions from Anthropic requests.
135
+ // Native web_search is server-side and more reliable keeping both confuses
136
+ // the model and causes it to pick custom tools which can fail with network errors.
137
+ tools = tools.filter(
138
+ (t) => !CUSTOM_SEARCH_TOOL_NAMES.includes(t.name as string)
139
+ );
140
+ payload.tools = tools;
132
141
 
133
142
  tools.push({
134
143
  type: "web_search_20250305",
@@ -18,10 +18,10 @@ import { join } from 'path'
18
18
  // where the relative import '../../../app-paths.ts' doesn't resolve.
19
19
  const authFilePath = join(homedir(), '.gsd', 'agent', 'auth.json')
20
20
 
21
- export type SearchProvider = 'tavily' | 'brave'
21
+ export type SearchProvider = 'tavily' | 'brave' | 'ollama'
22
22
  export type SearchProviderPreference = SearchProvider | 'auto'
23
23
 
24
- const VALID_PREFERENCES = new Set<string>(['tavily', 'brave', 'auto'])
24
+ const VALID_PREFERENCES = new Set<string>(['tavily', 'brave', 'ollama', 'auto'])
25
25
  const PREFERENCE_KEY = 'search_provider'
26
26
 
27
27
  /** Returns the Tavily API key from the environment, or empty string if not set. */
@@ -34,6 +34,11 @@ export function getBraveApiKey(): string {
34
34
  return process.env.BRAVE_API_KEY || ''
35
35
  }
36
36
 
37
+ /** Returns the Ollama API key from the environment, or empty string if not set. */
38
+ export function getOllamaApiKey(): string {
39
+ return process.env.OLLAMA_API_KEY || ''
40
+ }
41
+
37
42
  /**
38
43
  * Read the user's search provider preference from auth.json.
39
44
  * Returns 'auto' if no preference is stored or the stored value is invalid.
@@ -78,9 +83,11 @@ export function setSearchProviderPreference(pref: SearchProviderPreference, auth
78
83
  export function resolveSearchProvider(overridePreference?: string): SearchProvider | null {
79
84
  const tavilyKey = getTavilyApiKey()
80
85
  const braveKey = getBraveApiKey()
86
+ const ollamaKey = getOllamaApiKey()
81
87
 
82
88
  const hasTavily = tavilyKey.length > 0
83
89
  const hasBrave = braveKey.length > 0
90
+ const hasOllama = ollamaKey.length > 0
84
91
 
85
92
  // Determine effective preference
86
93
  let pref: SearchProviderPreference
@@ -100,18 +107,28 @@ export function resolveSearchProvider(overridePreference?: string): SearchProvid
100
107
  if (pref === 'auto') {
101
108
  if (hasTavily) return 'tavily'
102
109
  if (hasBrave) return 'brave'
110
+ if (hasOllama) return 'ollama'
103
111
  return null
104
112
  }
105
113
 
106
114
  if (pref === 'tavily') {
107
115
  if (hasTavily) return 'tavily'
108
116
  if (hasBrave) return 'brave'
117
+ if (hasOllama) return 'ollama'
109
118
  return null
110
119
  }
111
120
 
112
121
  if (pref === 'brave') {
113
122
  if (hasBrave) return 'brave'
114
123
  if (hasTavily) return 'tavily'
124
+ if (hasOllama) return 'ollama'
125
+ return null
126
+ }
127
+
128
+ if (pref === 'ollama') {
129
+ if (hasOllama) return 'ollama'
130
+ if (hasTavily) return 'tavily'
131
+ if (hasBrave) return 'brave'
115
132
  return null
116
133
  }
117
134