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
@@ -0,0 +1,78 @@
1
+ /**
2
+ * LRU cache with TTL — zero external dependencies.
3
+ *
4
+ * - max: maximum entries before oldest is evicted
5
+ * - ttlMs: time-to-live per entry
6
+ *
7
+ * Uses a Map (insertion-ordered) for O(1) LRU eviction:
8
+ * on every access the entry is deleted and re-inserted at the tail.
9
+ */
10
+ export class LRUTTLCache<V> {
11
+ private readonly max: number;
12
+ private readonly ttlMs: number;
13
+ private readonly store = new Map<string, { value: V; expiresAt: number }>();
14
+ private purgeTimer: ReturnType<typeof setInterval> | null = null;
15
+
16
+ constructor(options: { max: number; ttlMs: number }) {
17
+ this.max = options.max;
18
+ this.ttlMs = options.ttlMs;
19
+ }
20
+
21
+ get(key: string): V | undefined {
22
+ const entry = this.store.get(key);
23
+ if (!entry) return undefined;
24
+ if (Date.now() > entry.expiresAt) {
25
+ this.store.delete(key);
26
+ return undefined;
27
+ }
28
+ // Refresh to tail (most-recently-used)
29
+ this.store.delete(key);
30
+ this.store.set(key, entry);
31
+ return entry.value;
32
+ }
33
+
34
+ set(key: string, value: V): void {
35
+ if (this.store.has(key)) {
36
+ this.store.delete(key);
37
+ } else if (this.store.size >= this.max) {
38
+ const oldest = this.store.keys().next().value;
39
+ if (oldest !== undefined) this.store.delete(oldest);
40
+ }
41
+ this.store.set(key, { value, expiresAt: Date.now() + this.ttlMs });
42
+ }
43
+
44
+ has(key: string): boolean {
45
+ return this.get(key) !== undefined;
46
+ }
47
+
48
+ purgeStale(): void {
49
+ const now = Date.now();
50
+ for (const [key, entry] of this.store) {
51
+ if (now > entry.expiresAt) this.store.delete(key);
52
+ }
53
+ }
54
+
55
+ startPurgeInterval(intervalMs: number): void {
56
+ if (this.purgeTimer !== null) return;
57
+ this.purgeTimer = setInterval(() => this.purgeStale(), intervalMs);
58
+ // Don't keep the process alive just for cache cleanup
59
+ if (this.purgeTimer && typeof this.purgeTimer === "object" && "unref" in this.purgeTimer) {
60
+ (this.purgeTimer as NodeJS.Timeout).unref();
61
+ }
62
+ }
63
+
64
+ stopPurgeInterval(): void {
65
+ if (this.purgeTimer !== null) {
66
+ clearInterval(this.purgeTimer);
67
+ this.purgeTimer = null;
68
+ }
69
+ }
70
+
71
+ clear(): void {
72
+ this.store.clear();
73
+ }
74
+
75
+ get size(): number {
76
+ return this.store.size;
77
+ }
78
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * /search-provider slash command.
3
+ *
4
+ * Lets users switch between tavily, brave, and auto search backends.
5
+ * Supports direct arg (`/search-provider tavily`) or interactive select UI.
6
+ * Tab completion provides the three valid options with key status.
7
+ *
8
+ * All provider logic lives in provider.ts (S01) — this is pure UI wiring.
9
+ */
10
+
11
+ import type { ExtensionAPI } from '@gsd/pi-coding-agent'
12
+ import type { AutocompleteItem } from '@gsd/pi-tui'
13
+ import {
14
+ getTavilyApiKey,
15
+ getBraveApiKey,
16
+ getOllamaApiKey,
17
+ getSearchProviderPreference,
18
+ setSearchProviderPreference,
19
+ resolveSearchProvider,
20
+ type SearchProviderPreference,
21
+ } from './provider.ts'
22
+
23
+ const VALID_PREFERENCES: SearchProviderPreference[] = ['tavily', 'brave', 'ollama', 'auto']
24
+
25
+ function keyStatus(provider: 'tavily' | 'brave' | 'ollama'): string {
26
+ if (provider === 'tavily') return getTavilyApiKey() ? '✓' : '✗'
27
+ if (provider === 'ollama') return getOllamaApiKey() ? '✓' : '✗'
28
+ return getBraveApiKey() ? '✓' : '✗'
29
+ }
30
+
31
+ function buildSelectOptions(): string[] {
32
+ return [
33
+ `tavily (key: ${keyStatus('tavily')})`,
34
+ `brave (key: ${keyStatus('brave')})`,
35
+ `ollama (key: ${keyStatus('ollama')})`,
36
+ `auto`,
37
+ ]
38
+ }
39
+
40
+ function parseSelectChoice(choice: string): SearchProviderPreference {
41
+ if (choice.startsWith('tavily')) return 'tavily'
42
+ if (choice.startsWith('brave')) return 'brave'
43
+ if (choice.startsWith('ollama')) return 'ollama'
44
+ return 'auto'
45
+ }
46
+
47
+ export function registerSearchProviderCommand(pi: ExtensionAPI): void {
48
+ pi.registerCommand('search-provider', {
49
+ description: 'Switch search provider (tavily, brave, ollama, auto)',
50
+
51
+ getArgumentCompletions(prefix: string): AutocompleteItem[] | null {
52
+ const trimmed = prefix.trim().toLowerCase()
53
+ return VALID_PREFERENCES
54
+ .filter((p) => p.startsWith(trimmed))
55
+ .map((p) => {
56
+ let description: string
57
+ if (p === 'auto') {
58
+ description = `Auto-select (tavily: ${keyStatus('tavily')}, brave: ${keyStatus('brave')}, ollama: ${keyStatus('ollama')})`
59
+ } else {
60
+ description = `key: ${keyStatus(p)}`
61
+ }
62
+ return { value: p, label: p, description }
63
+ })
64
+ },
65
+
66
+ async handler(args, ctx) {
67
+ const trimmed = args.trim().toLowerCase()
68
+
69
+ let chosen: SearchProviderPreference
70
+
71
+ if (trimmed && (VALID_PREFERENCES as string[]).includes(trimmed)) {
72
+ // Direct arg — apply immediately, no select UI
73
+ chosen = trimmed as SearchProviderPreference
74
+ } else {
75
+ // No arg or invalid arg — show interactive select
76
+ const current = getSearchProviderPreference()
77
+ const options = buildSelectOptions()
78
+ const result = await ctx.ui.select(
79
+ `Search provider (current: ${current})`,
80
+ options,
81
+ )
82
+
83
+ if (result === undefined) {
84
+ // User cancelled — bail silently
85
+ return
86
+ }
87
+
88
+ chosen = parseSelectChoice(Array.isArray(result) ? result[0] : result)
89
+ }
90
+
91
+ setSearchProviderPreference(chosen)
92
+ const effective = resolveSearchProvider()
93
+ ctx.ui.notify(
94
+ `Search provider set to ${chosen}. Effective provider: ${effective ?? 'none (no API keys)'}`,
95
+ 'info',
96
+ )
97
+ },
98
+ })
99
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Token-efficient output formatting for search results, page content,
3
+ * and LLM context responses.
4
+ */
5
+
6
+ import { extractDomain } from "./url-utils.js";
7
+
8
+ export interface SearchResultFormatted {
9
+ title: string;
10
+ url: string;
11
+ description: string;
12
+ age?: string;
13
+ extra_snippets?: string[];
14
+ [key: string]: unknown;
15
+ }
16
+
17
+ // =============================================================================
18
+ // Adaptive Snippet Budget
19
+ // =============================================================================
20
+
21
+ /**
22
+ * Compute how many extra_snippets to show per result based on total count.
23
+ * Fewer results → more snippets each. More results → fewer snippets each.
24
+ *
25
+ * This keeps total output roughly constant regardless of result count.
26
+ */
27
+ function snippetsPerResult(resultCount: number): number {
28
+ if (resultCount <= 2) return 5; // show all available
29
+ if (resultCount <= 4) return 3;
30
+ if (resultCount <= 6) return 2;
31
+ if (resultCount <= 8) return 1;
32
+ return 0; // 9-10 results: descriptions only
33
+ }
34
+
35
+ // =============================================================================
36
+ // Search Results Formatting
37
+ // =============================================================================
38
+
39
+ export interface FormatSearchOptions {
40
+ cached?: boolean;
41
+ summary?: string;
42
+ queryCorrected?: boolean;
43
+ originalQuery?: string;
44
+ correctedQuery?: string;
45
+ moreResultsAvailable?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Format search results in a compact, token-efficient format.
50
+ *
51
+ * Produces:
52
+ * [1] Python Web Frameworks — example.com (2024-11)
53
+ * Main snippet text...
54
+ * + "additional excerpt 1"
55
+ * + "additional excerpt 2"
56
+ *
57
+ * Snippet count per result adapts to total result count.
58
+ */
59
+ export function formatSearchResults(
60
+ query: string,
61
+ results: SearchResultFormatted[],
62
+ options: FormatSearchOptions = {}
63
+ ): string {
64
+ const parts: string[] = [];
65
+
66
+ // Header
67
+ const cacheTag = options.cached ? " (cached)" : "";
68
+ parts.push(`Search: "${query}"${cacheTag}`);
69
+
70
+ // Spellcheck/query correction notice
71
+ if (options.queryCorrected && options.correctedQuery) {
72
+ parts.push(`Note: Query was corrected to "${options.correctedQuery}" (original: "${options.originalQuery ?? query}")`);
73
+ }
74
+
75
+ parts.push(""); // blank line after header
76
+
77
+ // AI summary block if available (from Brave Summarizer)
78
+ if (options.summary) {
79
+ parts.push(`Summary: ${options.summary}\n`);
80
+ }
81
+
82
+ if (results.length === 0) {
83
+ parts.push("No results found.");
84
+ return parts.join("\n");
85
+ }
86
+
87
+ const maxSnippets = snippetsPerResult(results.length);
88
+
89
+ // Results
90
+ for (let i = 0; i < results.length; i++) {
91
+ const r = results[i];
92
+ const domain = extractDomain(r.url);
93
+ const age = r.age ? ` (${r.age})` : "";
94
+
95
+ // Compact header line: [N] Title — domain (age)
96
+ parts.push(`[${i + 1}] ${r.title} — ${domain}${age}`);
97
+ parts.push(r.url);
98
+
99
+ // Primary description
100
+ if (r.description) {
101
+ parts.push(r.description);
102
+ }
103
+
104
+ // Extra snippets — adaptive count based on total results
105
+ if (maxSnippets > 0 && r.extra_snippets && r.extra_snippets.length > 0) {
106
+ for (const snippet of r.extra_snippets.slice(0, maxSnippets)) {
107
+ const clean = snippet.replace(/\n/g, " ").trim();
108
+ if (clean) parts.push(`+ ${clean}`);
109
+ }
110
+ }
111
+
112
+ parts.push(""); // blank line between results
113
+ }
114
+
115
+ // Pagination hint
116
+ if (options.moreResultsAvailable) {
117
+ parts.push("[More results available — increase count or refine query]");
118
+ }
119
+
120
+ return parts.join("\n");
121
+ }
122
+
123
+ // =============================================================================
124
+ // Page Content Formatting
125
+ // =============================================================================
126
+
127
+ export interface FormatPageOptions {
128
+ title?: string;
129
+ charCount: number;
130
+ truncated: boolean;
131
+ originalChars?: number;
132
+ hasMore?: boolean;
133
+ nextOffset?: number;
134
+ }
135
+
136
+ /**
137
+ * Format extracted page content with metadata header.
138
+ */
139
+ export function formatPageContent(
140
+ url: string,
141
+ content: string,
142
+ options: FormatPageOptions
143
+ ): string {
144
+ const domain = extractDomain(url);
145
+ const title = options.title ? ` — ${options.title}` : "";
146
+ const truncNote = options.truncated && options.originalChars
147
+ ? ` [truncated from ${options.originalChars.toLocaleString()} chars]`
148
+ : "";
149
+ const moreNote = options.hasMore && options.nextOffset
150
+ ? ` [use offset:${options.nextOffset} to continue reading]`
151
+ : "";
152
+
153
+ const header = `Page: ${domain}${title} (${options.charCount.toLocaleString()} chars)${truncNote}${moreNote}\n${url}\n---`;
154
+
155
+ return `${header}\n${content}`;
156
+ }
157
+
158
+ // =============================================================================
159
+ // LLM Context Formatting
160
+ // =============================================================================
161
+
162
+ export interface LLMContextSnippet {
163
+ url: string;
164
+ title: string;
165
+ snippets: string[];
166
+ }
167
+
168
+ export interface LLMContextSource {
169
+ title: string;
170
+ hostname: string;
171
+ age: string[] | null;
172
+ }
173
+
174
+ /**
175
+ * Format LLM Context API response in a compact, agent-optimized format.
176
+ *
177
+ * Output:
178
+ * Context: "query" (N sources, ~Mk tokens)
179
+ *
180
+ * [1] Page Title — domain.com (age)
181
+ * url
182
+ * Snippet text...
183
+ * ---
184
+ * Another snippet...
185
+ */
186
+ export function formatLLMContext(
187
+ query: string,
188
+ grounding: LLMContextSnippet[],
189
+ sources: Record<string, LLMContextSource>,
190
+ options: { cached?: boolean; tokenCount?: number } = {}
191
+ ): string {
192
+ const parts: string[] = [];
193
+
194
+ const cacheTag = options.cached ? " (cached)" : "";
195
+ const tokenTag = options.tokenCount ? ` (~${Math.round(options.tokenCount / 1000)}k tokens)` : "";
196
+ parts.push(`Context: "${query}" (${grounding.length} sources${tokenTag})${cacheTag}`);
197
+ parts.push("");
198
+
199
+ if (grounding.length === 0) {
200
+ parts.push("No relevant content found.");
201
+ return parts.join("\n");
202
+ }
203
+
204
+ for (let i = 0; i < grounding.length; i++) {
205
+ const g = grounding[i];
206
+ const source = sources[g.url];
207
+ const domain = source?.hostname || extractDomain(g.url);
208
+ const age = source?.age?.[2] ? ` (${source.age[2]})` : ""; // [2] is "N days ago" format
209
+
210
+ parts.push(`[${i + 1}] ${g.title || source?.title || "(untitled)"} — ${domain}${age}`);
211
+ parts.push(g.url);
212
+
213
+ // Join snippets with separator
214
+ for (const snippet of g.snippets) {
215
+ const clean = snippet.trim();
216
+ if (clean) parts.push(clean);
217
+ }
218
+
219
+ parts.push(""); // blank line between sources
220
+ }
221
+
222
+ return parts.join("\n");
223
+ }
224
+
225
+ // =============================================================================
226
+ // Multi-Page Formatting
227
+ // =============================================================================
228
+
229
+ /**
230
+ * Format multiple page extractions compactly.
231
+ */
232
+ export function formatMultiplePages(
233
+ pages: Array<{
234
+ url: string;
235
+ title?: string;
236
+ content: string;
237
+ charCount: number;
238
+ error?: string;
239
+ }>
240
+ ): string {
241
+ const parts: string[] = [];
242
+
243
+ for (const page of pages) {
244
+ const domain = extractDomain(page.url);
245
+ if (page.error) {
246
+ parts.push(`[✗] ${domain}: ${page.error}`);
247
+ } else {
248
+ const title = page.title ? ` — ${page.title}` : "";
249
+ parts.push(`[✓] ${domain}${title} (${page.charCount.toLocaleString()} chars)`);
250
+ parts.push(page.url);
251
+ parts.push("---");
252
+ parts.push(page.content);
253
+ }
254
+ parts.push(""); // separator
255
+ }
256
+
257
+ return parts.join("\n");
258
+ }
@@ -0,0 +1,238 @@
1
+ /**
2
+ * HTTP utilities: retry with backoff, abort signal merging, error types, timing.
3
+ */
4
+
5
+ // =============================================================================
6
+ // Error Types
7
+ // =============================================================================
8
+
9
+ /** Structured error for non-2xx HTTP responses. */
10
+ export class HttpError extends Error {
11
+ readonly statusCode: number;
12
+ readonly response?: Response;
13
+
14
+ constructor(message: string, statusCode: number, response?: Response) {
15
+ super(message);
16
+ this.name = "HttpError";
17
+ this.statusCode = statusCode;
18
+ this.response = response;
19
+ Object.setPrototypeOf(this, HttpError.prototype);
20
+ }
21
+ }
22
+
23
+ /** Categorized error types for agent-friendly error handling. */
24
+ export type SearchErrorKind =
25
+ | "auth_error" // 401/403 — bad or missing API key
26
+ | "rate_limited" // 429 — too many requests
27
+ | "network_error" // DNS, timeout, connection refused
28
+ | "server_error" // 5xx
29
+ | "invalid_request" // 400, bad params
30
+ | "not_found" // 404
31
+ | "unknown";
32
+
33
+ export function classifyError(err: unknown): { kind: SearchErrorKind; message: string; retryAfterMs?: number } {
34
+ if (err instanceof HttpError) {
35
+ const code = err.statusCode;
36
+ if (code === 401 || code === 403) {
37
+ return { kind: "auth_error", message: `HTTP ${code}: Invalid or missing API key. Check your API key with secure_env_collect.` };
38
+ }
39
+ if (code === 429) {
40
+ let retryAfterMs: number | undefined;
41
+ const retryAfter = err.response?.headers.get("Retry-After");
42
+ if (retryAfter) {
43
+ const seconds = parseFloat(retryAfter);
44
+ if (!isNaN(seconds)) retryAfterMs = seconds * 1000;
45
+ }
46
+ return { kind: "rate_limited", message: `Rate limited (HTTP 429). ${retryAfterMs ? `Retry after ${Math.ceil(retryAfterMs / 1000)}s.` : "Wait before retrying."}`, retryAfterMs };
47
+ }
48
+ if (code === 400) {
49
+ return { kind: "invalid_request", message: `Bad request (HTTP 400): ${err.message}` };
50
+ }
51
+ if (code === 404) return { kind: "not_found", message: `Not found (HTTP 404)` };
52
+ if (code >= 500) return { kind: "server_error", message: `Server error (HTTP ${code}): ${err.message}` };
53
+ return { kind: "unknown", message: `HTTP ${code}: ${err.message}` };
54
+ }
55
+ if (err instanceof TypeError) {
56
+ return { kind: "network_error", message: `Network error: ${(err as Error).message}` };
57
+ }
58
+ const msg = (err as Error)?.message ?? String(err);
59
+ if (msg.includes("abort") || msg.includes("timeout")) {
60
+ return { kind: "network_error", message: `Request timed out` };
61
+ }
62
+ return { kind: "unknown", message: msg };
63
+ }
64
+
65
+ // =============================================================================
66
+ // Rate Limit Info
67
+ // =============================================================================
68
+
69
+ export interface RateLimitInfo {
70
+ remaining?: number;
71
+ limit?: number;
72
+ reset?: number; // epoch seconds
73
+ }
74
+
75
+ /** Extract rate limit headers from a Brave API response. */
76
+ export function extractRateLimitInfo(response: Response): RateLimitInfo | undefined {
77
+ const remaining = response.headers.get("x-ratelimit-remaining");
78
+ const limit = response.headers.get("x-ratelimit-limit");
79
+ const reset = response.headers.get("x-ratelimit-reset");
80
+ if (!remaining && !limit) return undefined;
81
+ return {
82
+ remaining: remaining ? parseInt(remaining, 10) : undefined,
83
+ limit: limit ? parseInt(limit, 10) : undefined,
84
+ reset: reset ? parseInt(reset, 10) : undefined,
85
+ };
86
+ }
87
+
88
+ // =============================================================================
89
+ // Timing
90
+ // =============================================================================
91
+
92
+ export interface TimedResponse {
93
+ response: Response;
94
+ latencyMs: number;
95
+ rateLimit?: RateLimitInfo;
96
+ }
97
+
98
+ // =============================================================================
99
+ // Retry Logic
100
+ // =============================================================================
101
+
102
+ function isRetryable(error: unknown): boolean {
103
+ if (error instanceof HttpError) {
104
+ return error.statusCode === 429 || error.statusCode >= 500;
105
+ }
106
+ if (error instanceof TypeError) return true;
107
+ return false;
108
+ }
109
+
110
+ function sleep(ms: number): Promise<void> {
111
+ return new Promise((resolve) => setTimeout(resolve, ms));
112
+ }
113
+
114
+ /** Merge multiple AbortSignals — aborts as soon as any fires. */
115
+ export function anySignal(signals: AbortSignal[]): AbortSignal {
116
+ const controller = new AbortController();
117
+ for (const sig of signals) {
118
+ if (sig.aborted) {
119
+ controller.abort(sig.reason);
120
+ break;
121
+ }
122
+ sig.addEventListener("abort", () => controller.abort(sig.reason), { once: true });
123
+ }
124
+ return controller.signal;
125
+ }
126
+
127
+ /**
128
+ * Fetch with automatic retry and full-jitter exponential backoff.
129
+ *
130
+ * - maxRetries: additional attempts after the first (total = maxRetries + 1)
131
+ * - Respects Retry-After header on 429 responses
132
+ * - Each attempt uses a 30-second AbortSignal timeout
133
+ * - Non-retryable errors thrown immediately
134
+ */
135
+ export async function fetchWithRetry(
136
+ url: string,
137
+ options: RequestInit,
138
+ maxRetries: number = 2
139
+ ): Promise<Response> {
140
+ let lastError: unknown;
141
+
142
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
143
+ const timeoutController = new AbortController();
144
+ const timeoutId = setTimeout(() => timeoutController.abort(), 30_000);
145
+
146
+ const callerSignal = options.signal as AbortSignal | undefined;
147
+ const signal = callerSignal
148
+ ? anySignal([callerSignal, timeoutController.signal])
149
+ : timeoutController.signal;
150
+
151
+ try {
152
+ const response = await fetch(url, { ...options, signal });
153
+ clearTimeout(timeoutId);
154
+
155
+ if (!response.ok) {
156
+ throw new HttpError(
157
+ `HTTP ${response.status}: ${response.statusText}`,
158
+ response.status,
159
+ response
160
+ );
161
+ }
162
+ return response;
163
+ } catch (err) {
164
+ clearTimeout(timeoutId);
165
+ lastError = err;
166
+
167
+ if (!isRetryable(err)) throw err;
168
+
169
+ if (attempt < maxRetries) {
170
+ let delayMs: number;
171
+ if (err instanceof HttpError && err.statusCode === 429 && err.response) {
172
+ const retryAfter = err.response.headers.get("Retry-After");
173
+ if (retryAfter) {
174
+ const seconds = parseFloat(retryAfter);
175
+ delayMs = isNaN(seconds) ? 1000 : seconds * 1000;
176
+ } else {
177
+ delayMs = Math.random() * Math.min(32_000, 1_000 * 2 ** attempt);
178
+ }
179
+ } else {
180
+ delayMs = Math.random() * Math.min(32_000, 1_000 * 2 ** attempt);
181
+ }
182
+ await sleep(delayMs);
183
+ }
184
+ }
185
+ }
186
+
187
+ throw lastError;
188
+ }
189
+
190
+ /**
191
+ * Simple fetch with timeout, no retry. For content extraction where
192
+ * we want to fail fast.
193
+ */
194
+ export async function fetchSimple(
195
+ url: string,
196
+ options: RequestInit & { timeoutMs?: number } = {}
197
+ ): Promise<Response> {
198
+ const { timeoutMs = 15_000, ...fetchOpts } = options;
199
+ const controller = new AbortController();
200
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
201
+
202
+ const callerSignal = fetchOpts.signal as AbortSignal | undefined;
203
+ const signal = callerSignal
204
+ ? anySignal([callerSignal, controller.signal])
205
+ : controller.signal;
206
+
207
+ try {
208
+ const response = await fetch(url, { ...fetchOpts, signal });
209
+ clearTimeout(timeoutId);
210
+ if (!response.ok) {
211
+ throw new HttpError(
212
+ `HTTP ${response.status}: ${response.statusText}`,
213
+ response.status,
214
+ response
215
+ );
216
+ }
217
+ return response;
218
+ } catch (err) {
219
+ clearTimeout(timeoutId);
220
+ throw err;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Fetch with retry AND timing/rate-limit extraction.
226
+ * Wraps fetchWithRetry and returns latency + rate limit info.
227
+ */
228
+ export async function fetchWithRetryTimed(
229
+ url: string,
230
+ options: RequestInit,
231
+ maxRetries: number = 2
232
+ ): Promise<TimedResponse> {
233
+ const start = performance.now();
234
+ const response = await fetchWithRetry(url, options, maxRetries);
235
+ const latencyMs = Math.round(performance.now() - start);
236
+ const rateLimit = extractRateLimitInfo(response);
237
+ return { response, latencyMs, rateLimit };
238
+ }