@kr78/pi-coding-agent 17.2.10

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 (1593) hide show
  1. package/CHANGELOG.md +14861 -0
  2. package/README.md +35 -0
  3. package/dist/CHANGELOG-chtdxh1z.md +14861 -0
  4. package/dist/cli.js +20483 -0
  5. package/dist/template-c2hyaytt.js +1653 -0
  6. package/dist/template-f8wx9vfn.css +1355 -0
  7. package/dist/template-qat058wr.html +55 -0
  8. package/dist/tool-views.generated-jdfmzwmn.js +35 -0
  9. package/examples/README.md +21 -0
  10. package/examples/custom-tools/README.md +104 -0
  11. package/examples/custom-tools/hello/index.ts +20 -0
  12. package/examples/extensions/README.md +142 -0
  13. package/examples/extensions/api-demo.ts +78 -0
  14. package/examples/extensions/chalk-logger.ts +25 -0
  15. package/examples/extensions/hello.ts +31 -0
  16. package/examples/extensions/pirate.ts +43 -0
  17. package/examples/extensions/plan-mode.ts +549 -0
  18. package/examples/extensions/reload-runtime.ts +38 -0
  19. package/examples/extensions/thinking-note.ts +13 -0
  20. package/examples/extensions/tools.ts +145 -0
  21. package/examples/extensions/with-deps/index.ts +36 -0
  22. package/examples/extensions/with-deps/package-lock.json +31 -0
  23. package/examples/extensions/with-deps/package.json +17 -0
  24. package/examples/hooks/README.md +56 -0
  25. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  26. package/examples/hooks/confirm-destructive.ts +58 -0
  27. package/examples/hooks/custom-compaction.ts +115 -0
  28. package/examples/hooks/dirty-repo-guard.ts +51 -0
  29. package/examples/hooks/file-trigger.ts +40 -0
  30. package/examples/hooks/git-checkpoint.ts +52 -0
  31. package/examples/hooks/handoff.ts +149 -0
  32. package/examples/hooks/permission-gate.ts +33 -0
  33. package/examples/hooks/protected-paths.ts +29 -0
  34. package/examples/hooks/qna.ts +118 -0
  35. package/examples/hooks/status-line.ts +39 -0
  36. package/examples/sdk/01-minimal.ts +21 -0
  37. package/examples/sdk/02-custom-model.ts +49 -0
  38. package/examples/sdk/03-custom-prompt.ts +46 -0
  39. package/examples/sdk/04-skills.ts +43 -0
  40. package/examples/sdk/06-extensions.ts +82 -0
  41. package/examples/sdk/06-hooks.ts +61 -0
  42. package/examples/sdk/07-context-files.ts +35 -0
  43. package/examples/sdk/08-prompt-templates.ts +41 -0
  44. package/examples/sdk/08-slash-commands.ts +46 -0
  45. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  46. package/examples/sdk/11-sessions.ts +47 -0
  47. package/examples/sdk/12-redis-sessions.ts +54 -0
  48. package/examples/sdk/13-sql-sessions.ts +61 -0
  49. package/examples/sdk/README.md +169 -0
  50. package/package.json +587 -0
  51. package/scripts/bench-guard.ts +71 -0
  52. package/scripts/bench-title-models.ts +332 -0
  53. package/scripts/build-binary.ts +117 -0
  54. package/scripts/bundle-dist.ts +123 -0
  55. package/scripts/compile-binary.ts +69 -0
  56. package/scripts/embed-mupdf-wasm.ts +67 -0
  57. package/scripts/format-prompts.ts +68 -0
  58. package/scripts/generate-aria-snapshot.ts +134 -0
  59. package/scripts/generate-docs-index.ts +58 -0
  60. package/scripts/generate-share-viewer.ts +34 -0
  61. package/scripts/legacy-pi-virtual-module.ts +205 -0
  62. package/scripts/measure-prompt-tokens.ts +63 -0
  63. package/scripts/omp +51 -0
  64. package/scripts/omp.ts +19 -0
  65. package/scripts/security-compare.ts +40 -0
  66. package/src/advisor/advise-tool.ts +234 -0
  67. package/src/advisor/config.ts +341 -0
  68. package/src/advisor/emission-guard.ts +172 -0
  69. package/src/advisor/index.ts +6 -0
  70. package/src/advisor/runtime.ts +1412 -0
  71. package/src/advisor/transcript-recorder.ts +215 -0
  72. package/src/advisor/watchdog.ts +135 -0
  73. package/src/async/index.ts +1 -0
  74. package/src/async/job-manager.ts +847 -0
  75. package/src/auto-thinking/classifier.ts +220 -0
  76. package/src/autolearn/controller.ts +152 -0
  77. package/src/autolearn/managed-skills.ts +255 -0
  78. package/src/autoresearch/command-resume.md +14 -0
  79. package/src/autoresearch/dashboard.ts +436 -0
  80. package/src/autoresearch/git.ts +331 -0
  81. package/src/autoresearch/helpers.ts +218 -0
  82. package/src/autoresearch/index.ts +541 -0
  83. package/src/autoresearch/prompt-setup.md +43 -0
  84. package/src/autoresearch/prompt.md +103 -0
  85. package/src/autoresearch/resume-message.md +10 -0
  86. package/src/autoresearch/state.ts +273 -0
  87. package/src/autoresearch/storage.ts +700 -0
  88. package/src/autoresearch/tools/init-experiment.ts +268 -0
  89. package/src/autoresearch/tools/log-experiment.ts +520 -0
  90. package/src/autoresearch/tools/run-experiment.ts +407 -0
  91. package/src/autoresearch/tools/update-notes.ts +109 -0
  92. package/src/autoresearch/types.ts +168 -0
  93. package/src/capability/context-file.ts +44 -0
  94. package/src/capability/extension-module.ts +34 -0
  95. package/src/capability/extension.ts +47 -0
  96. package/src/capability/fs.ts +117 -0
  97. package/src/capability/hook.ts +40 -0
  98. package/src/capability/index.ts +467 -0
  99. package/src/capability/instruction.ts +37 -0
  100. package/src/capability/mcp.ts +115 -0
  101. package/src/capability/prompt.ts +35 -0
  102. package/src/capability/rule-buckets.ts +66 -0
  103. package/src/capability/rule.ts +298 -0
  104. package/src/capability/settings.ts +34 -0
  105. package/src/capability/skill.ts +69 -0
  106. package/src/capability/slash-command.ts +40 -0
  107. package/src/capability/ssh.ts +41 -0
  108. package/src/capability/system-prompt.ts +34 -0
  109. package/src/capability/tool.ts +38 -0
  110. package/src/capability/types.ts +187 -0
  111. package/src/cleanse/agent.ts +226 -0
  112. package/src/cleanse/balance.ts +79 -0
  113. package/src/cleanse/checkers.ts +996 -0
  114. package/src/cleanse/index.ts +190 -0
  115. package/src/cleanse/loop.ts +51 -0
  116. package/src/cleanse/parsers.ts +726 -0
  117. package/src/cleanse/progress.ts +50 -0
  118. package/src/cleanse/prompts/assignment.md +47 -0
  119. package/src/cleanse/types.ts +72 -0
  120. package/src/cli/agents-cli.ts +138 -0
  121. package/src/cli/args.ts +368 -0
  122. package/src/cli/auth-broker-cli.ts +940 -0
  123. package/src/cli/auth-gateway-cli.ts +674 -0
  124. package/src/cli/bench-cli.ts +990 -0
  125. package/src/cli/browser-relay-cli.ts +119 -0
  126. package/src/cli/classify-install-target.ts +76 -0
  127. package/src/cli/claude-trace-cli.ts +795 -0
  128. package/src/cli/command-help.ts +107 -0
  129. package/src/cli/commands/init-xdg.ts +27 -0
  130. package/src/cli/completion-gen.ts +550 -0
  131. package/src/cli/config-cli.ts +459 -0
  132. package/src/cli/dry-balance-cli.ts +864 -0
  133. package/src/cli/extension-flags.ts +48 -0
  134. package/src/cli/file-processor.ts +132 -0
  135. package/src/cli/flag-tables.ts +367 -0
  136. package/src/cli/gallery-cli.ts +272 -0
  137. package/src/cli/gallery-fixtures/agentic.ts +420 -0
  138. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  139. package/src/cli/gallery-fixtures/edit.ts +254 -0
  140. package/src/cli/gallery-fixtures/fs.ts +245 -0
  141. package/src/cli/gallery-fixtures/index.ts +40 -0
  142. package/src/cli/gallery-fixtures/interaction.ts +46 -0
  143. package/src/cli/gallery-fixtures/memory.ts +81 -0
  144. package/src/cli/gallery-fixtures/misc.ts +177 -0
  145. package/src/cli/gallery-fixtures/search.ts +135 -0
  146. package/src/cli/gallery-fixtures/shell.ts +241 -0
  147. package/src/cli/gallery-fixtures/types.ts +57 -0
  148. package/src/cli/gallery-fixtures/web.ts +158 -0
  149. package/src/cli/gallery-screenshot.ts +279 -0
  150. package/src/cli/gc-cli.ts +1566 -0
  151. package/src/cli/grep-cli.ts +161 -0
  152. package/src/cli/grievances-cli.ts +256 -0
  153. package/src/cli/help-extra.ts +89 -0
  154. package/src/cli/initial-message.ts +58 -0
  155. package/src/cli/models-cli.ts +385 -0
  156. package/src/cli/plugin-cli.ts +996 -0
  157. package/src/cli/profile-alias.ts +369 -0
  158. package/src/cli/profile-bootstrap.ts +233 -0
  159. package/src/cli/read-cli.ts +99 -0
  160. package/src/cli/session-picker.ts +110 -0
  161. package/src/cli/setup-cli.ts +312 -0
  162. package/src/cli/setup-model-picker.ts +43 -0
  163. package/src/cli/shell-cli.ts +176 -0
  164. package/src/cli/ssh-cli.ts +179 -0
  165. package/src/cli/startup-cwd.ts +58 -0
  166. package/src/cli/stats-cli.ts +229 -0
  167. package/src/cli/thinking-levels.ts +7 -0
  168. package/src/cli/tiny-models-cli.ts +153 -0
  169. package/src/cli/ttsr-cli.ts +1013 -0
  170. package/src/cli/update-cli.ts +1184 -0
  171. package/src/cli/usage-cli.ts +1084 -0
  172. package/src/cli/usage-error.ts +7 -0
  173. package/src/cli/web-search-cli.ts +144 -0
  174. package/src/cli/worktree-cli.ts +311 -0
  175. package/src/cli-commands.ts +308 -0
  176. package/src/cli.ts +428 -0
  177. package/src/collab/crypto.ts +63 -0
  178. package/src/collab/display-name.ts +13 -0
  179. package/src/collab/guest.ts +764 -0
  180. package/src/collab/host.ts +692 -0
  181. package/src/collab/protocol.ts +296 -0
  182. package/src/collab/relay-client.ts +282 -0
  183. package/src/collab/replication-shrink.ts +111 -0
  184. package/src/commands/acp.ts +35 -0
  185. package/src/commands/agents.ts +58 -0
  186. package/src/commands/auth-broker.ts +100 -0
  187. package/src/commands/auth-gateway.ts +70 -0
  188. package/src/commands/bench.ts +64 -0
  189. package/src/commands/browser-relay.ts +53 -0
  190. package/src/commands/cleanse.ts +45 -0
  191. package/src/commands/commit.ts +47 -0
  192. package/src/commands/complete.ts +67 -0
  193. package/src/commands/completions.ts +61 -0
  194. package/src/commands/config.ts +52 -0
  195. package/src/commands/dry-balance.ts +43 -0
  196. package/src/commands/gallery.ts +61 -0
  197. package/src/commands/gc.ts +47 -0
  198. package/src/commands/grep.ts +49 -0
  199. package/src/commands/grievances.ts +52 -0
  200. package/src/commands/install.ts +107 -0
  201. package/src/commands/join.ts +40 -0
  202. package/src/commands/launch-help.ts +116 -0
  203. package/src/commands/launch.ts +34 -0
  204. package/src/commands/models.ts +61 -0
  205. package/src/commands/plugin.ts +79 -0
  206. package/src/commands/read.ts +39 -0
  207. package/src/commands/say.ts +146 -0
  208. package/src/commands/setup.ts +68 -0
  209. package/src/commands/share.ts +71 -0
  210. package/src/commands/shell.ts +30 -0
  211. package/src/commands/ssh.ts +61 -0
  212. package/src/commands/stats.ts +30 -0
  213. package/src/commands/tiny-models.ts +36 -0
  214. package/src/commands/token.ts +164 -0
  215. package/src/commands/ttsr.ts +125 -0
  216. package/src/commands/update.ts +34 -0
  217. package/src/commands/usage.ts +55 -0
  218. package/src/commands/web-search.ts +43 -0
  219. package/src/commands/worktree.ts +63 -0
  220. package/src/commit/agentic/agent.ts +322 -0
  221. package/src/commit/agentic/fallback.ts +96 -0
  222. package/src/commit/agentic/index.ts +383 -0
  223. package/src/commit/agentic/lock-files.ts +107 -0
  224. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  225. package/src/commit/agentic/prompts/session-user.md +25 -0
  226. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  227. package/src/commit/agentic/prompts/system.md +38 -0
  228. package/src/commit/agentic/state.ts +60 -0
  229. package/src/commit/agentic/tools/analyze-file.ts +148 -0
  230. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  231. package/src/commit/agentic/tools/git-hunk.ts +52 -0
  232. package/src/commit/agentic/tools/git-overview.ts +62 -0
  233. package/src/commit/agentic/tools/index.ts +54 -0
  234. package/src/commit/agentic/tools/propose-changelog.ts +147 -0
  235. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  236. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  237. package/src/commit/agentic/tools/schemas.ts +11 -0
  238. package/src/commit/agentic/tools/split-commit.ts +241 -0
  239. package/src/commit/agentic/topo-sort.ts +44 -0
  240. package/src/commit/agentic/trivial.ts +51 -0
  241. package/src/commit/agentic/validation.ts +183 -0
  242. package/src/commit/analysis/conventional.ts +64 -0
  243. package/src/commit/analysis/index.ts +4 -0
  244. package/src/commit/analysis/scope.ts +242 -0
  245. package/src/commit/analysis/summary.ts +107 -0
  246. package/src/commit/analysis/validation.ts +66 -0
  247. package/src/commit/changelog/detect.ts +40 -0
  248. package/src/commit/changelog/generate.ts +101 -0
  249. package/src/commit/changelog/index.ts +234 -0
  250. package/src/commit/changelog/parse.ts +44 -0
  251. package/src/commit/cli.ts +85 -0
  252. package/src/commit/git/diff.ts +148 -0
  253. package/src/commit/index.ts +5 -0
  254. package/src/commit/map-reduce/index.ts +69 -0
  255. package/src/commit/map-reduce/map-phase.ts +193 -0
  256. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  257. package/src/commit/map-reduce/utils.ts +9 -0
  258. package/src/commit/message.ts +11 -0
  259. package/src/commit/model-selection.ts +95 -0
  260. package/src/commit/pipeline.ts +244 -0
  261. package/src/commit/prompts/analysis-system.md +148 -0
  262. package/src/commit/prompts/analysis-user.md +38 -0
  263. package/src/commit/prompts/changelog-system.md +50 -0
  264. package/src/commit/prompts/changelog-user.md +18 -0
  265. package/src/commit/prompts/file-observer-system.md +24 -0
  266. package/src/commit/prompts/file-observer-user.md +8 -0
  267. package/src/commit/prompts/reduce-system.md +50 -0
  268. package/src/commit/prompts/reduce-user.md +17 -0
  269. package/src/commit/prompts/summary-retry.md +3 -0
  270. package/src/commit/prompts/summary-system.md +38 -0
  271. package/src/commit/prompts/summary-user.md +13 -0
  272. package/src/commit/prompts/types-description.md +2 -0
  273. package/src/commit/shared-llm.ts +70 -0
  274. package/src/commit/types.ts +118 -0
  275. package/src/commit/utils/exclusions.ts +42 -0
  276. package/src/commit/utils.ts +58 -0
  277. package/src/config/api-key-resolver.ts +81 -0
  278. package/src/config/append-only-context-mode.ts +76 -0
  279. package/src/config/config-file.ts +347 -0
  280. package/src/config/inline-tool-descriptors-mode.ts +26 -0
  281. package/src/config/keybindings.ts +703 -0
  282. package/src/config/mcp-schema.json +252 -0
  283. package/src/config/model-discovery.ts +1077 -0
  284. package/src/config/model-registry.ts +2822 -0
  285. package/src/config/model-resolver.ts +2115 -0
  286. package/src/config/model-roles.ts +113 -0
  287. package/src/config/models-config-schema-bundle.ts +315 -0
  288. package/src/config/models-config-schema.ts +14 -0
  289. package/src/config/models-config.ts +130 -0
  290. package/src/config/prompt-templates.ts +205 -0
  291. package/src/config/provider-globals.ts +25 -0
  292. package/src/config/resolve-config-value.ts +95 -0
  293. package/src/config/service-tier.ts +146 -0
  294. package/src/config/settings-schema.ts +5887 -0
  295. package/src/config/settings.ts +2394 -0
  296. package/src/config.ts +242 -0
  297. package/src/cursor-bridge-tools.ts +81 -0
  298. package/src/cursor.ts +960 -0
  299. package/src/dap/client.ts +1043 -0
  300. package/src/dap/config.ts +480 -0
  301. package/src/dap/defaults.json +212 -0
  302. package/src/dap/index.ts +4 -0
  303. package/src/dap/session.ts +1841 -0
  304. package/src/dap/types.ts +611 -0
  305. package/src/debug/index.ts +584 -0
  306. package/src/debug/log-formatting.ts +58 -0
  307. package/src/debug/log-viewer.ts +966 -0
  308. package/src/debug/profiler.ts +168 -0
  309. package/src/debug/protocol-probe.ts +267 -0
  310. package/src/debug/raw-sse-buffer.ts +421 -0
  311. package/src/debug/raw-sse.ts +312 -0
  312. package/src/debug/remote-debugger.ts +151 -0
  313. package/src/debug/report-bundle.ts +411 -0
  314. package/src/debug/system-info.ts +111 -0
  315. package/src/debug/terminal-info.ts +124 -0
  316. package/src/discovery/agent-plugin-format.ts +551 -0
  317. package/src/discovery/agent-plugins.ts +341 -0
  318. package/src/discovery/agents-md.ts +67 -0
  319. package/src/discovery/agents.ts +300 -0
  320. package/src/discovery/at-imports.ts +273 -0
  321. package/src/discovery/builtin-defaults.ts +39 -0
  322. package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
  323. package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
  324. package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
  325. package/src/discovery/builtin-rules/go-ioutil.md +37 -0
  326. package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
  327. package/src/discovery/builtin-rules/go-new-expr.md +44 -0
  328. package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
  329. package/src/discovery/builtin-rules/go-range-int.md +45 -0
  330. package/src/discovery/builtin-rules/index.ts +74 -0
  331. package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
  332. package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
  333. package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
  334. package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
  335. package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
  336. package/src/discovery/builtin-rules/rs-result-type.md +20 -0
  337. package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
  338. package/src/discovery/builtin-rules/ts-import-type.md +43 -0
  339. package/src/discovery/builtin-rules/ts-no-any.md +66 -0
  340. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
  341. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
  342. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +56 -0
  343. package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
  344. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  345. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  346. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  347. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
  348. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  349. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  350. package/src/discovery/builtin.ts +945 -0
  351. package/src/discovery/claude-plugins.ts +640 -0
  352. package/src/discovery/claude.ts +591 -0
  353. package/src/discovery/cline.ts +83 -0
  354. package/src/discovery/codex.ts +553 -0
  355. package/src/discovery/contained-path.ts +78 -0
  356. package/src/discovery/cursor.ts +223 -0
  357. package/src/discovery/gemini.ts +386 -0
  358. package/src/discovery/github.ts +337 -0
  359. package/src/discovery/helpers.ts +1183 -0
  360. package/src/discovery/index.ts +82 -0
  361. package/src/discovery/mcp-json.ts +182 -0
  362. package/src/discovery/omp-extension-roots.ts +272 -0
  363. package/src/discovery/omp-plugins.ts +409 -0
  364. package/src/discovery/opencode.ts +441 -0
  365. package/src/discovery/plugin-dir-roots.ts +28 -0
  366. package/src/discovery/ssh.ts +153 -0
  367. package/src/discovery/substitute-plugin-root.ts +77 -0
  368. package/src/discovery/vscode.ts +106 -0
  369. package/src/discovery/windsurf.ts +149 -0
  370. package/src/edit/apply-patch/index.ts +87 -0
  371. package/src/edit/apply-patch/parser.ts +174 -0
  372. package/src/edit/diff.ts +1008 -0
  373. package/src/edit/edit-clipboard.ts +23 -0
  374. package/src/edit/file-snapshot-store.ts +148 -0
  375. package/src/edit/hashline/block-resolver.ts +33 -0
  376. package/src/edit/hashline/diff.ts +402 -0
  377. package/src/edit/hashline/execute.ts +313 -0
  378. package/src/edit/hashline/filesystem.ts +247 -0
  379. package/src/edit/hashline/index.ts +5 -0
  380. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  381. package/src/edit/hashline/params.ts +12 -0
  382. package/src/edit/index.ts +699 -0
  383. package/src/edit/modes/apply-patch.lark +19 -0
  384. package/src/edit/modes/apply-patch.ts +53 -0
  385. package/src/edit/modes/patch.ts +1958 -0
  386. package/src/edit/modes/replace.ts +1191 -0
  387. package/src/edit/normalize.ts +345 -0
  388. package/src/edit/notebook.ts +242 -0
  389. package/src/edit/read-file.ts +25 -0
  390. package/src/edit/renderer.ts +998 -0
  391. package/src/edit/snapshot-details.ts +77 -0
  392. package/src/edit/streaming.ts +710 -0
  393. package/src/eval/agent-bridge.ts +224 -0
  394. package/src/eval/backend-helpers.ts +48 -0
  395. package/src/eval/backend.ts +71 -0
  396. package/src/eval/bridge-timeout.ts +65 -0
  397. package/src/eval/budget-bridge.ts +48 -0
  398. package/src/eval/completion-bridge.ts +212 -0
  399. package/src/eval/concurrency-bridge.ts +34 -0
  400. package/src/eval/executor-base.ts +562 -0
  401. package/src/eval/idle-timeout.ts +91 -0
  402. package/src/eval/index.ts +6 -0
  403. package/src/eval/jl/executor.ts +551 -0
  404. package/src/eval/jl/index.ts +54 -0
  405. package/src/eval/jl/kernel.ts +236 -0
  406. package/src/eval/jl/prelude.jl +736 -0
  407. package/src/eval/jl/prelude.ts +3 -0
  408. package/src/eval/jl/runner.jl +666 -0
  409. package/src/eval/jl/runtime.ts +118 -0
  410. package/src/eval/js/context-manager.ts +872 -0
  411. package/src/eval/js/executor.ts +178 -0
  412. package/src/eval/js/index.ts +41 -0
  413. package/src/eval/js/process-entry.ts +31 -0
  414. package/src/eval/js/shared/helpers.ts +170 -0
  415. package/src/eval/js/shared/indirect-eval.ts +30 -0
  416. package/src/eval/js/shared/local-module-loader.ts +364 -0
  417. package/src/eval/js/shared/prelude.ts +2 -0
  418. package/src/eval/js/shared/prelude.txt +294 -0
  419. package/src/eval/js/shared/rewrite-imports.ts +550 -0
  420. package/src/eval/js/shared/runtime.ts +590 -0
  421. package/src/eval/js/shared/types.ts +18 -0
  422. package/src/eval/js/tool-bridge.ts +163 -0
  423. package/src/eval/js/worker-core.ts +380 -0
  424. package/src/eval/js/worker-entry.ts +37 -0
  425. package/src/eval/js/worker-protocol.ts +47 -0
  426. package/src/eval/kernel-base.ts +569 -0
  427. package/src/eval/py/display.ts +71 -0
  428. package/src/eval/py/executor.ts +643 -0
  429. package/src/eval/py/index.ts +57 -0
  430. package/src/eval/py/kernel.ts +235 -0
  431. package/src/eval/py/prelude.py +673 -0
  432. package/src/eval/py/prelude.ts +3 -0
  433. package/src/eval/py/runner.py +1425 -0
  434. package/src/eval/py/runtime.ts +276 -0
  435. package/src/eval/py/spawn-options.ts +134 -0
  436. package/src/eval/py/tool-bridge.ts +201 -0
  437. package/src/eval/rb/executor.ts +511 -0
  438. package/src/eval/rb/index.ts +54 -0
  439. package/src/eval/rb/kernel.ts +231 -0
  440. package/src/eval/rb/prelude.rb +551 -0
  441. package/src/eval/rb/prelude.ts +3 -0
  442. package/src/eval/rb/runner.rb +581 -0
  443. package/src/eval/rb/runtime.ts +132 -0
  444. package/src/eval/runtime-env.ts +104 -0
  445. package/src/eval/session-id.ts +8 -0
  446. package/src/eval/types.ts +48 -0
  447. package/src/exa/index.ts +2 -0
  448. package/src/exa/mcp-client.ts +370 -0
  449. package/src/exa/types.ts +69 -0
  450. package/src/exec/bash-executor.ts +627 -0
  451. package/src/exec/direnv.ts +145 -0
  452. package/src/exec/exec.ts +53 -0
  453. package/src/exec/non-interactive-env.ts +118 -0
  454. package/src/export/custom-share.ts +65 -0
  455. package/src/export/html/args.ts +20 -0
  456. package/src/export/html/index.ts +320 -0
  457. package/src/export/html/share-loader.js +102 -0
  458. package/src/export/html/template.css +1355 -0
  459. package/src/export/html/template.html +55 -0
  460. package/src/export/html/template.js +1653 -0
  461. package/src/export/html/tool-views.generated.js +35 -0
  462. package/src/export/html/vendor/highlight.min.js +1213 -0
  463. package/src/export/html/vendor/marked.min.js +6 -0
  464. package/src/export/html/web-palette.ts +126 -0
  465. package/src/export/share.ts +686 -0
  466. package/src/export/ttsr.ts +590 -0
  467. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  468. package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
  469. package/src/extensibility/custom-commands/index.ts +2 -0
  470. package/src/extensibility/custom-commands/loader.ts +242 -0
  471. package/src/extensibility/custom-commands/types.ts +119 -0
  472. package/src/extensibility/custom-tools/index.ts +7 -0
  473. package/src/extensibility/custom-tools/loader.ts +301 -0
  474. package/src/extensibility/custom-tools/types.ts +287 -0
  475. package/src/extensibility/custom-tools/wrapper.ts +50 -0
  476. package/src/extensibility/extensions/compact-handler.ts +40 -0
  477. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  478. package/src/extensibility/extensions/index.ts +16 -0
  479. package/src/extensibility/extensions/load-errors.ts +13 -0
  480. package/src/extensibility/extensions/loader.ts +663 -0
  481. package/src/extensibility/extensions/managed-timers.ts +83 -0
  482. package/src/extensibility/extensions/model-api.ts +39 -0
  483. package/src/extensibility/extensions/runner.ts +1396 -0
  484. package/src/extensibility/extensions/types.ts +1603 -0
  485. package/src/extensibility/extensions/wrapper.ts +395 -0
  486. package/src/extensibility/hooks/index.ts +6 -0
  487. package/src/extensibility/hooks/loader.ts +243 -0
  488. package/src/extensibility/hooks/runner.ts +425 -0
  489. package/src/extensibility/hooks/tool-wrapper.ts +124 -0
  490. package/src/extensibility/hooks/types.ts +612 -0
  491. package/src/extensibility/legacy-pi-ai-shim.ts +161 -0
  492. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1460 -0
  493. package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
  494. package/src/extensibility/legacy-typebox.ts +101 -0
  495. package/src/extensibility/plugins/bun-git-cache.ts +91 -0
  496. package/src/extensibility/plugins/doctor.ts +65 -0
  497. package/src/extensibility/plugins/git-url.ts +367 -0
  498. package/src/extensibility/plugins/index.ts +9 -0
  499. package/src/extensibility/plugins/installer.ts +201 -0
  500. package/src/extensibility/plugins/legacy-pi-compat.ts +2643 -0
  501. package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
  502. package/src/extensibility/plugins/loader.ts +535 -0
  503. package/src/extensibility/plugins/manager.ts +1142 -0
  504. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  505. package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
  506. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  507. package/src/extensibility/plugins/marketplace/manager.ts +927 -0
  508. package/src/extensibility/plugins/marketplace/registry.ts +192 -0
  509. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  510. package/src/extensibility/plugins/marketplace/types.ts +192 -0
  511. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  512. package/src/extensibility/plugins/parser.ts +107 -0
  513. package/src/extensibility/plugins/runtime-config.ts +9 -0
  514. package/src/extensibility/plugins/types.ts +194 -0
  515. package/src/extensibility/session-handler-types.ts +21 -0
  516. package/src/extensibility/shared-events.ts +401 -0
  517. package/src/extensibility/skills.ts +531 -0
  518. package/src/extensibility/slash-commands.ts +131 -0
  519. package/src/extensibility/tool-event-input.ts +80 -0
  520. package/src/extensibility/tool-proxy.ts +28 -0
  521. package/src/extensibility/utils.ts +192 -0
  522. package/src/goals/index.ts +3 -0
  523. package/src/goals/runtime.ts +521 -0
  524. package/src/goals/state.ts +37 -0
  525. package/src/goals/tools/goal-tool.ts +251 -0
  526. package/src/hindsight/backend.ts +354 -0
  527. package/src/hindsight/bank.ts +156 -0
  528. package/src/hindsight/client.ts +680 -0
  529. package/src/hindsight/config.ts +193 -0
  530. package/src/hindsight/content.ts +266 -0
  531. package/src/hindsight/index.ts +8 -0
  532. package/src/hindsight/mental-models.ts +429 -0
  533. package/src/hindsight/seeds.json +32 -0
  534. package/src/hindsight/state.ts +530 -0
  535. package/src/hindsight/transcript.ts +71 -0
  536. package/src/index.ts +66 -0
  537. package/src/internal-urls/agent-protocol.ts +180 -0
  538. package/src/internal-urls/artifact-protocol.ts +151 -0
  539. package/src/internal-urls/docs-index.ts +102 -0
  540. package/src/internal-urls/filesystem-resource.ts +34 -0
  541. package/src/internal-urls/history-protocol.ts +198 -0
  542. package/src/internal-urls/index.ts +28 -0
  543. package/src/internal-urls/issue-pr-protocol.ts +594 -0
  544. package/src/internal-urls/json-query.ts +126 -0
  545. package/src/internal-urls/local-protocol.ts +471 -0
  546. package/src/internal-urls/mcp-protocol.ts +168 -0
  547. package/src/internal-urls/memory-protocol.ts +380 -0
  548. package/src/internal-urls/omp-protocol.ts +94 -0
  549. package/src/internal-urls/parse.ts +103 -0
  550. package/src/internal-urls/registry-helpers.ts +132 -0
  551. package/src/internal-urls/router.ts +153 -0
  552. package/src/internal-urls/rule-protocol.ts +45 -0
  553. package/src/internal-urls/security-protocol.ts +261 -0
  554. package/src/internal-urls/skill-protocol.ts +131 -0
  555. package/src/internal-urls/ssh-protocol.ts +368 -0
  556. package/src/internal-urls/types.ts +196 -0
  557. package/src/internal-urls/vault-protocol.ts +940 -0
  558. package/src/internal-urls/xd-protocol.ts +46 -0
  559. package/src/irc/bus.ts +380 -0
  560. package/src/jsonrpc/message-framing.ts +142 -0
  561. package/src/launch/broker.ts +1368 -0
  562. package/src/launch/client.ts +521 -0
  563. package/src/launch/ensure.ts +73 -0
  564. package/src/launch/paths.ts +14 -0
  565. package/src/launch/presence.ts +82 -0
  566. package/src/launch/protocol.ts +448 -0
  567. package/src/launch/spawn-options.ts +17 -0
  568. package/src/launch/terminal-output-worker-client.ts +53 -0
  569. package/src/launch/terminal-output-worker-protocol.ts +11 -0
  570. package/src/launch/terminal-output-worker.ts +23 -0
  571. package/src/launch/terminal-output.ts +46 -0
  572. package/src/lib/xai-http.ts +150 -0
  573. package/src/live/attestation.ts +91 -0
  574. package/src/live/controller.ts +517 -0
  575. package/src/live/prompts/agent-final-message.md +3 -0
  576. package/src/live/prompts/live-instructions.md +23 -0
  577. package/src/live/protocol.ts +233 -0
  578. package/src/live/transport.ts +422 -0
  579. package/src/live/visualizer.ts +221 -0
  580. package/src/live/voices.ts +18 -0
  581. package/src/lsp/client.ts +1465 -0
  582. package/src/lsp/clients/biome-client.ts +263 -0
  583. package/src/lsp/clients/index.ts +50 -0
  584. package/src/lsp/clients/lsp-linter-client.ts +85 -0
  585. package/src/lsp/clients/swiftlint-client.ts +120 -0
  586. package/src/lsp/config.ts +549 -0
  587. package/src/lsp/defaults.json +499 -0
  588. package/src/lsp/deferred-diagnostics.ts +66 -0
  589. package/src/lsp/diagnostics-ledger.ts +51 -0
  590. package/src/lsp/edits.ts +288 -0
  591. package/src/lsp/format-options.ts +119 -0
  592. package/src/lsp/index.ts +2821 -0
  593. package/src/lsp/lspmux.ts +233 -0
  594. package/src/lsp/mux/daemon.ts +348 -0
  595. package/src/lsp/mux/protocol.ts +96 -0
  596. package/src/lsp/mux/server.ts +797 -0
  597. package/src/lsp/render.ts +668 -0
  598. package/src/lsp/startup-events.ts +13 -0
  599. package/src/lsp/types.ts +479 -0
  600. package/src/lsp/utils.ts +747 -0
  601. package/src/main.ts +1799 -0
  602. package/src/markit/NOTICE +32 -0
  603. package/src/markit/converters/docx.ts +56 -0
  604. package/src/markit/converters/epub.ts +136 -0
  605. package/src/markit/converters/pdf/columns.ts +103 -0
  606. package/src/markit/converters/pdf/extract.ts +598 -0
  607. package/src/markit/converters/pdf/grid.ts +780 -0
  608. package/src/markit/converters/pdf/headers.ts +106 -0
  609. package/src/markit/converters/pdf/index.ts +146 -0
  610. package/src/markit/converters/pdf/render.ts +501 -0
  611. package/src/markit/converters/pdf/types.ts +84 -0
  612. package/src/markit/converters/pptx.ts +325 -0
  613. package/src/markit/converters/xlsx.ts +173 -0
  614. package/src/markit/index.ts +2 -0
  615. package/src/markit/registry.ts +59 -0
  616. package/src/markit/types.ts +35 -0
  617. package/src/mcp/client.ts +511 -0
  618. package/src/mcp/config-writer.ts +377 -0
  619. package/src/mcp/config.ts +385 -0
  620. package/src/mcp/index.ts +29 -0
  621. package/src/mcp/json-rpc.ts +122 -0
  622. package/src/mcp/loader.ts +125 -0
  623. package/src/mcp/manager.ts +1524 -0
  624. package/src/mcp/oauth-credentials.ts +104 -0
  625. package/src/mcp/oauth-discovery.ts +587 -0
  626. package/src/mcp/oauth-flow.ts +830 -0
  627. package/src/mcp/render.ts +214 -0
  628. package/src/mcp/request-id.ts +24 -0
  629. package/src/mcp/smithery-auth.ts +108 -0
  630. package/src/mcp/smithery-connect.ts +154 -0
  631. package/src/mcp/smithery-registry.ts +500 -0
  632. package/src/mcp/startup-events.ts +116 -0
  633. package/src/mcp/timeout.ts +59 -0
  634. package/src/mcp/tool-bridge.ts +691 -0
  635. package/src/mcp/tool-cache.ts +117 -0
  636. package/src/mcp/transports/header-policy.ts +95 -0
  637. package/src/mcp/transports/http.ts +508 -0
  638. package/src/mcp/transports/index.ts +7 -0
  639. package/src/mcp/transports/sse.ts +372 -0
  640. package/src/mcp/transports/stdio.ts +905 -0
  641. package/src/mcp/types.ts +467 -0
  642. package/src/memories/index.ts +1434 -0
  643. package/src/memories/storage.ts +578 -0
  644. package/src/memory-backend/index.ts +19 -0
  645. package/src/memory-backend/local-backend.ts +47 -0
  646. package/src/memory-backend/messages.ts +19 -0
  647. package/src/memory-backend/off-backend.ts +25 -0
  648. package/src/memory-backend/resolve.ts +25 -0
  649. package/src/memory-backend/runtime.ts +66 -0
  650. package/src/memory-backend/tool-names.ts +2 -0
  651. package/src/memory-backend/types.ts +166 -0
  652. package/src/mnemopi/backend.ts +629 -0
  653. package/src/mnemopi/config.ts +267 -0
  654. package/src/mnemopi/embed-client.ts +293 -0
  655. package/src/mnemopi/embed-protocol.ts +35 -0
  656. package/src/mnemopi/embed-worker.ts +114 -0
  657. package/src/mnemopi/index.ts +3 -0
  658. package/src/mnemopi/state.ts +940 -0
  659. package/src/modes/acp/acp-agent.ts +2595 -0
  660. package/src/modes/acp/acp-client-bridge.ts +154 -0
  661. package/src/modes/acp/acp-event-mapper.ts +1084 -0
  662. package/src/modes/acp/acp-mode.ts +48 -0
  663. package/src/modes/acp/index.ts +2 -0
  664. package/src/modes/acp/terminal-auth.ts +37 -0
  665. package/src/modes/components/advisor-config.ts +635 -0
  666. package/src/modes/components/advisor-message.ts +109 -0
  667. package/src/modes/components/agent-dashboard.ts +1252 -0
  668. package/src/modes/components/agent-hub-projection.ts +248 -0
  669. package/src/modes/components/agent-hub-renderer.ts +194 -0
  670. package/src/modes/components/agent-hub.ts +1126 -0
  671. package/src/modes/components/agent-transcript-viewer.ts +647 -0
  672. package/src/modes/components/ask-dialog.ts +1017 -0
  673. package/src/modes/components/assistant-message.ts +978 -0
  674. package/src/modes/components/background-tan-message.ts +36 -0
  675. package/src/modes/components/bash-execution.ts +233 -0
  676. package/src/modes/components/bordered-loader.ts +41 -0
  677. package/src/modes/components/btw-panel.ts +161 -0
  678. package/src/modes/components/cache-invalidation-marker.ts +110 -0
  679. package/src/modes/components/chat-block.ts +111 -0
  680. package/src/modes/components/chat-transcript-builder.ts +518 -0
  681. package/src/modes/components/codex-reset-fireworks.ts +369 -0
  682. package/src/modes/components/collab-prompt-message.ts +32 -0
  683. package/src/modes/components/compaction-summary-message.ts +221 -0
  684. package/src/modes/components/copy-selector.ts +218 -0
  685. package/src/modes/components/countdown-timer.ts +75 -0
  686. package/src/modes/components/custom-editor.ts +1036 -0
  687. package/src/modes/components/custom-message.ts +70 -0
  688. package/src/modes/components/diff.ts +254 -0
  689. package/src/modes/components/dynamic-border.ts +37 -0
  690. package/src/modes/components/error-banner.ts +33 -0
  691. package/src/modes/components/eval-execution.ts +169 -0
  692. package/src/modes/components/execution-shared.ts +101 -0
  693. package/src/modes/components/extensions/extension-dashboard.ts +492 -0
  694. package/src/modes/components/extensions/extension-list.ts +507 -0
  695. package/src/modes/components/extensions/index.ts +9 -0
  696. package/src/modes/components/extensions/inspector-panel.ts +326 -0
  697. package/src/modes/components/extensions/state-manager.ts +648 -0
  698. package/src/modes/components/extensions/types.ts +186 -0
  699. package/src/modes/components/footer.ts +274 -0
  700. package/src/modes/components/history-search.ts +268 -0
  701. package/src/modes/components/hook-editor.ts +213 -0
  702. package/src/modes/components/hook-input.ts +87 -0
  703. package/src/modes/components/hook-message.ts +67 -0
  704. package/src/modes/components/hook-selector.ts +691 -0
  705. package/src/modes/components/index.ts +42 -0
  706. package/src/modes/components/keybinding-hints.ts +56 -0
  707. package/src/modes/components/late-diagnostics-message.ts +60 -0
  708. package/src/modes/components/login-dialog.ts +197 -0
  709. package/src/modes/components/logout-account-selector.ts +130 -0
  710. package/src/modes/components/mcp-add-wizard.ts +1413 -0
  711. package/src/modes/components/message-frame.ts +98 -0
  712. package/src/modes/components/model-browser.ts +888 -0
  713. package/src/modes/components/model-hub.ts +2014 -0
  714. package/src/modes/components/model-picker.ts +237 -0
  715. package/src/modes/components/move-overlay.ts +293 -0
  716. package/src/modes/components/oauth-selector.ts +474 -0
  717. package/src/modes/components/omfg-panel.ts +141 -0
  718. package/src/modes/components/overlay-box.ts +109 -0
  719. package/src/modes/components/pause-screen.ts +208 -0
  720. package/src/modes/components/plan-review-overlay.ts +1226 -0
  721. package/src/modes/components/plan-toc.ts +138 -0
  722. package/src/modes/components/plugin-selector.ts +100 -0
  723. package/src/modes/components/plugin-settings.ts +745 -0
  724. package/src/modes/components/queue-mode-selector.ts +61 -0
  725. package/src/modes/components/read-tool-group.ts +856 -0
  726. package/src/modes/components/reset-usage-selector.ts +161 -0
  727. package/src/modes/components/segment-track.ts +89 -0
  728. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  729. package/src/modes/components/selector-helpers.ts +129 -0
  730. package/src/modes/components/session-account-selector.ts +62 -0
  731. package/src/modes/components/session-selector.ts +1035 -0
  732. package/src/modes/components/settings-defs.ts +267 -0
  733. package/src/modes/components/settings-selector.ts +1445 -0
  734. package/src/modes/components/show-images-selector.ts +50 -0
  735. package/src/modes/components/skill-message.ts +110 -0
  736. package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
  737. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  738. package/src/modes/components/status-line/component.ts +1843 -0
  739. package/src/modes/components/status-line/context-thresholds.ts +86 -0
  740. package/src/modes/components/status-line/git-utils.ts +42 -0
  741. package/src/modes/components/status-line/index.ts +5 -0
  742. package/src/modes/components/status-line/presets.ts +106 -0
  743. package/src/modes/components/status-line/segments.ts +710 -0
  744. package/src/modes/components/status-line/separators.ts +55 -0
  745. package/src/modes/components/status-line/types.ts +159 -0
  746. package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
  747. package/src/modes/components/theme-selector.ts +68 -0
  748. package/src/modes/components/thinking-selector.ts +57 -0
  749. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  750. package/src/modes/components/tips.txt +26 -0
  751. package/src/modes/components/todo-reminder.ts +43 -0
  752. package/src/modes/components/tool-execution.ts +1467 -0
  753. package/src/modes/components/transcript-container.ts +527 -0
  754. package/src/modes/components/tree-selector.ts +1006 -0
  755. package/src/modes/components/ttsr-notification.ts +123 -0
  756. package/src/modes/components/usage-row.ts +52 -0
  757. package/src/modes/components/user-message-selector.ts +227 -0
  758. package/src/modes/components/user-message.ts +167 -0
  759. package/src/modes/components/visual-truncate.ts +63 -0
  760. package/src/modes/components/welcome.ts +578 -0
  761. package/src/modes/controllers/btw-controller.ts +246 -0
  762. package/src/modes/controllers/command-controller-shared.ts +109 -0
  763. package/src/modes/controllers/command-controller.ts +2024 -0
  764. package/src/modes/controllers/event-controller.ts +2173 -0
  765. package/src/modes/controllers/extension-ui-controller.ts +1243 -0
  766. package/src/modes/controllers/input-controller.ts +2037 -0
  767. package/src/modes/controllers/live-command-controller.ts +261 -0
  768. package/src/modes/controllers/mcp-command-controller.ts +2552 -0
  769. package/src/modes/controllers/omfg-controller.ts +287 -0
  770. package/src/modes/controllers/omfg-rule.ts +647 -0
  771. package/src/modes/controllers/selector-controller.ts +2058 -0
  772. package/src/modes/controllers/session-focus-controller.ts +117 -0
  773. package/src/modes/controllers/ssh-command-controller.ts +385 -0
  774. package/src/modes/controllers/streaming-reveal.ts +399 -0
  775. package/src/modes/controllers/tan-command-controller.ts +243 -0
  776. package/src/modes/controllers/todo-command-controller.ts +487 -0
  777. package/src/modes/controllers/tool-args-reveal.ts +591 -0
  778. package/src/modes/data/emojis.json +1 -0
  779. package/src/modes/emoji-autocomplete.ts +285 -0
  780. package/src/modes/github-ref-autocomplete.ts +75 -0
  781. package/src/modes/gradient-highlight.ts +99 -0
  782. package/src/modes/image-references.ts +137 -0
  783. package/src/modes/index.ts +10 -0
  784. package/src/modes/interactive-mode.ts +5055 -0
  785. package/src/modes/internal-url-autocomplete.ts +158 -0
  786. package/src/modes/loop-limit.ts +192 -0
  787. package/src/modes/magic-keyword-boundary.ts +23 -0
  788. package/src/modes/magic-keywords.ts +42 -0
  789. package/src/modes/markdown-prose.ts +247 -0
  790. package/src/modes/oauth-manual-input.ts +69 -0
  791. package/src/modes/orchestrate.ts +43 -0
  792. package/src/modes/print-mode.ts +282 -0
  793. package/src/modes/prompt-action-autocomplete.ts +322 -0
  794. package/src/modes/queue-input.ts +132 -0
  795. package/src/modes/rpc/host-tools.ts +204 -0
  796. package/src/modes/rpc/host-uris.ts +241 -0
  797. package/src/modes/rpc/rpc-client.ts +1213 -0
  798. package/src/modes/rpc/rpc-frame.ts +316 -0
  799. package/src/modes/rpc/rpc-input.ts +38 -0
  800. package/src/modes/rpc/rpc-messages.ts +127 -0
  801. package/src/modes/rpc/rpc-mode.ts +1519 -0
  802. package/src/modes/rpc/rpc-subagents.ts +265 -0
  803. package/src/modes/rpc/rpc-types.ts +544 -0
  804. package/src/modes/running-subagent-badge.ts +13 -0
  805. package/src/modes/runtime-init.ts +144 -0
  806. package/src/modes/session-observer-registry.ts +223 -0
  807. package/src/modes/session-teardown.ts +82 -0
  808. package/src/modes/setup-version.ts +11 -0
  809. package/src/modes/setup-wizard/index.ts +103 -0
  810. package/src/modes/setup-wizard/lazy.ts +16 -0
  811. package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
  812. package/src/modes/setup-wizard/scenes/model.ts +132 -0
  813. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  814. package/src/modes/setup-wizard/scenes/providers.ts +105 -0
  815. package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
  816. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  817. package/src/modes/setup-wizard/scenes/theme.ts +330 -0
  818. package/src/modes/setup-wizard/scenes/types.ts +65 -0
  819. package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
  820. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  821. package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
  822. package/src/modes/shared.ts +49 -0
  823. package/src/modes/skill-command.ts +91 -0
  824. package/src/modes/theme/dark.json +95 -0
  825. package/src/modes/theme/defaults/alabaster.json +93 -0
  826. package/src/modes/theme/defaults/amethyst.json +96 -0
  827. package/src/modes/theme/defaults/anthracite.json +93 -0
  828. package/src/modes/theme/defaults/basalt.json +91 -0
  829. package/src/modes/theme/defaults/birch.json +95 -0
  830. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  831. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  832. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  833. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  834. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  835. package/src/modes/theme/defaults/dark-copper.json +95 -0
  836. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  837. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  838. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  839. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  840. package/src/modes/theme/defaults/dark-ember.json +95 -0
  841. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  842. package/src/modes/theme/defaults/dark-forest.json +96 -0
  843. package/src/modes/theme/defaults/dark-github.json +105 -0
  844. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  845. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  846. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  847. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  848. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  849. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  850. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  851. package/src/modes/theme/defaults/dark-nord.json +97 -0
  852. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  853. package/src/modes/theme/defaults/dark-one.json +100 -0
  854. package/src/modes/theme/defaults/dark-poimandres.json +143 -0
  855. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  856. package/src/modes/theme/defaults/dark-reef.json +91 -0
  857. package/src/modes/theme/defaults/dark-retro.json +92 -0
  858. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  859. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  860. package/src/modes/theme/defaults/dark-slate.json +95 -0
  861. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  862. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  863. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  864. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  865. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  866. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  867. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  868. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  869. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  870. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  871. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  872. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  873. package/src/modes/theme/defaults/graphite.json +92 -0
  874. package/src/modes/theme/defaults/index.ts +199 -0
  875. package/src/modes/theme/defaults/light-arctic.json +107 -0
  876. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  877. package/src/modes/theme/defaults/light-canyon.json +91 -0
  878. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  879. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  880. package/src/modes/theme/defaults/light-coral.json +95 -0
  881. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  882. package/src/modes/theme/defaults/light-dawn.json +90 -0
  883. package/src/modes/theme/defaults/light-dunes.json +91 -0
  884. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  885. package/src/modes/theme/defaults/light-forest.json +100 -0
  886. package/src/modes/theme/defaults/light-frost.json +95 -0
  887. package/src/modes/theme/defaults/light-github.json +115 -0
  888. package/src/modes/theme/defaults/light-glacier.json +91 -0
  889. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  890. package/src/modes/theme/defaults/light-haze.json +90 -0
  891. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  892. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  893. package/src/modes/theme/defaults/light-lavender.json +95 -0
  894. package/src/modes/theme/defaults/light-meadow.json +91 -0
  895. package/src/modes/theme/defaults/light-mint.json +95 -0
  896. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  897. package/src/modes/theme/defaults/light-ocean.json +99 -0
  898. package/src/modes/theme/defaults/light-one.json +99 -0
  899. package/src/modes/theme/defaults/light-opal.json +91 -0
  900. package/src/modes/theme/defaults/light-orchard.json +91 -0
  901. package/src/modes/theme/defaults/light-paper.json +95 -0
  902. package/src/modes/theme/defaults/light-poimandres.json +143 -0
  903. package/src/modes/theme/defaults/light-prism.json +90 -0
  904. package/src/modes/theme/defaults/light-retro.json +98 -0
  905. package/src/modes/theme/defaults/light-sand.json +95 -0
  906. package/src/modes/theme/defaults/light-savanna.json +91 -0
  907. package/src/modes/theme/defaults/light-solarized.json +102 -0
  908. package/src/modes/theme/defaults/light-soleil.json +90 -0
  909. package/src/modes/theme/defaults/light-sunset.json +99 -0
  910. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  911. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  912. package/src/modes/theme/defaults/light-wetland.json +91 -0
  913. package/src/modes/theme/defaults/light-zenith.json +89 -0
  914. package/src/modes/theme/defaults/limestone.json +94 -0
  915. package/src/modes/theme/defaults/mahogany.json +97 -0
  916. package/src/modes/theme/defaults/marble.json +93 -0
  917. package/src/modes/theme/defaults/obsidian.json +91 -0
  918. package/src/modes/theme/defaults/onyx.json +91 -0
  919. package/src/modes/theme/defaults/pearl.json +93 -0
  920. package/src/modes/theme/defaults/porcelain.json +91 -0
  921. package/src/modes/theme/defaults/quartz.json +96 -0
  922. package/src/modes/theme/defaults/sandstone.json +95 -0
  923. package/src/modes/theme/defaults/titanium.json +90 -0
  924. package/src/modes/theme/light.json +93 -0
  925. package/src/modes/theme/mermaid-cache.ts +92 -0
  926. package/src/modes/theme/shimmer.ts +305 -0
  927. package/src/modes/theme/theme-schema.json +463 -0
  928. package/src/modes/theme/theme.ts +3171 -0
  929. package/src/modes/turn-budget.ts +31 -0
  930. package/src/modes/types.ts +494 -0
  931. package/src/modes/ultrathink.ts +42 -0
  932. package/src/modes/utils/context-usage.ts +518 -0
  933. package/src/modes/utils/copy-targets.ts +378 -0
  934. package/src/modes/utils/hotkeys-markdown.ts +65 -0
  935. package/src/modes/utils/interactive-context-helpers.ts +31 -0
  936. package/src/modes/utils/keybinding-matchers.ts +86 -0
  937. package/src/modes/utils/tools-markdown.ts +31 -0
  938. package/src/modes/utils/transcript-render-helpers.ts +253 -0
  939. package/src/modes/utils/ui-helpers.ts +976 -0
  940. package/src/modes/warp-events.ts +232 -0
  941. package/src/modes/workflow.ts +55 -0
  942. package/src/plan-mode/approved-plan.ts +194 -0
  943. package/src/plan-mode/model-transition.ts +51 -0
  944. package/src/plan-mode/plan-files.ts +40 -0
  945. package/src/plan-mode/plan-handoff.ts +37 -0
  946. package/src/plan-mode/plan-protection.ts +31 -0
  947. package/src/plan-mode/state.ts +6 -0
  948. package/src/priority.json +60 -0
  949. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  950. package/src/prompts/advisor/advise-tool.md +3 -0
  951. package/src/prompts/advisor/context-files.md +8 -0
  952. package/src/prompts/advisor/system.md +98 -0
  953. package/src/prompts/agents/designer.md +74 -0
  954. package/src/prompts/agents/frontmatter.md +12 -0
  955. package/src/prompts/agents/init.md +33 -0
  956. package/src/prompts/agents/librarian.md +119 -0
  957. package/src/prompts/agents/reviewer.md +139 -0
  958. package/src/prompts/agents/scout.md +58 -0
  959. package/src/prompts/agents/security-reviewer.md +75 -0
  960. package/src/prompts/agents/task.md +17 -0
  961. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  962. package/src/prompts/bench/cache-prefix.md +3 -0
  963. package/src/prompts/bench/cache-suffix.md +1 -0
  964. package/src/prompts/bench.md +6 -0
  965. package/src/prompts/ci-green-request.md +36 -0
  966. package/src/prompts/dry-balance-bench.md +8 -0
  967. package/src/prompts/goals/goal-budget-limit.md +16 -0
  968. package/src/prompts/goals/goal-continuation.md +28 -0
  969. package/src/prompts/goals/goal-mode-active.md +23 -0
  970. package/src/prompts/goals/goal-mode-context.md +4 -0
  971. package/src/prompts/goals/goal-todo-context.md +12 -0
  972. package/src/prompts/goals/guided-goal-interview.md +43 -0
  973. package/src/prompts/memories/consolidation.md +30 -0
  974. package/src/prompts/memories/consolidation_system.md +4 -0
  975. package/src/prompts/memories/read-path.md +17 -0
  976. package/src/prompts/memories/stage_one_input.md +6 -0
  977. package/src/prompts/memories/stage_one_system.md +21 -0
  978. package/src/prompts/review-custom-request.md +21 -0
  979. package/src/prompts/review-headless-request.md +16 -0
  980. package/src/prompts/review-request.md +68 -0
  981. package/src/prompts/security/scan-coordinator.md +7 -0
  982. package/src/prompts/security/scan-request.md +21 -0
  983. package/src/prompts/security/validate-request.md +8 -0
  984. package/src/prompts/session/launch-completion.md +1 -0
  985. package/src/prompts/skills/autoload.md +8 -0
  986. package/src/prompts/skills/user-invocation.md +11 -0
  987. package/src/prompts/steering/parent-irc.md +5 -0
  988. package/src/prompts/steering/user-interjection.md +6 -0
  989. package/src/prompts/system/active-repo-context.md +4 -0
  990. package/src/prompts/system/agent-creation-architect.md +50 -0
  991. package/src/prompts/system/agent-creation-user.md +6 -0
  992. package/src/prompts/system/auto-continue.md +1 -0
  993. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  994. package/src/prompts/system/auto-thinking-difficulty.md +14 -0
  995. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  996. package/src/prompts/system/autolearn-guidance.md +7 -0
  997. package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
  998. package/src/prompts/system/background-tan-dispatch.md +8 -0
  999. package/src/prompts/system/btw-user.md +8 -0
  1000. package/src/prompts/system/commit-message-system.md +14 -0
  1001. package/src/prompts/system/computer-safety.md +14 -0
  1002. package/src/prompts/system/custom-system-prompt.md +64 -0
  1003. package/src/prompts/system/eager-task.md +7 -0
  1004. package/src/prompts/system/eager-todo.md +18 -0
  1005. package/src/prompts/system/empty-stop-retry.md +4 -0
  1006. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  1007. package/src/prompts/system/interrupted-thinking.md +7 -0
  1008. package/src/prompts/system/irc-autoreply.md +6 -0
  1009. package/src/prompts/system/irc-incoming.md +9 -0
  1010. package/src/prompts/system/manual-continue.md +7 -0
  1011. package/src/prompts/system/mcp-xdev-guidance.md +11 -0
  1012. package/src/prompts/system/memory-consolidation-system.md +8 -0
  1013. package/src/prompts/system/memory-extraction-system.md +26 -0
  1014. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  1015. package/src/prompts/system/omfg-user.md +50 -0
  1016. package/src/prompts/system/orchestrate-notice.md +40 -0
  1017. package/src/prompts/system/personalities/default.md +18 -0
  1018. package/src/prompts/system/personalities/friendly.md +17 -0
  1019. package/src/prompts/system/personalities/pragmatic.md +15 -0
  1020. package/src/prompts/system/plan-mode-active.md +125 -0
  1021. package/src/prompts/system/plan-mode-approved.md +22 -0
  1022. package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
  1023. package/src/prompts/system/plan-mode-reference.md +10 -0
  1024. package/src/prompts/system/plan-mode-subagent.md +33 -0
  1025. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  1026. package/src/prompts/system/plan-yolo-handoff.md +5 -0
  1027. package/src/prompts/system/prewalk-checklist.md +7 -0
  1028. package/src/prompts/system/prewalk-continue.md +1 -0
  1029. package/src/prompts/system/prewalk-plan.md +13 -0
  1030. package/src/prompts/system/project-prompt.md +61 -0
  1031. package/src/prompts/system/recap-user.md +9 -0
  1032. package/src/prompts/system/resolve-device-reminder.md +3 -0
  1033. package/src/prompts/system/rewind-report.md +6 -0
  1034. package/src/prompts/system/side-channel-no-tools.md +3 -0
  1035. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  1036. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  1037. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  1038. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  1039. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  1040. package/src/prompts/system/speech-rewrite.md +15 -0
  1041. package/src/prompts/system/subagent-async-pending.md +6 -0
  1042. package/src/prompts/system/subagent-system-prompt.md +73 -0
  1043. package/src/prompts/system/subagent-user-prompt.md +3 -0
  1044. package/src/prompts/system/subagent-yield-reminder.md +23 -0
  1045. package/src/prompts/system/system-prompt.md +263 -0
  1046. package/src/prompts/system/tan-context-switch.md +17 -0
  1047. package/src/prompts/system/task-label.md +23 -0
  1048. package/src/prompts/system/thinking-loop-redirect.md +10 -0
  1049. package/src/prompts/system/title-marker-instruction.md +1 -0
  1050. package/src/prompts/system/title-system.md +16 -0
  1051. package/src/prompts/system/tool-call-loop-redirect.md +8 -0
  1052. package/src/prompts/system/ttsr-interrupt.md +7 -0
  1053. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  1054. package/src/prompts/system/ultrathink-notice.md +3 -0
  1055. package/src/prompts/system/unexpected-stop-classifier.md +17 -0
  1056. package/src/prompts/system/unexpected-stop-retry.md +4 -0
  1057. package/src/prompts/system/vibe-mode-active.md +26 -0
  1058. package/src/prompts/system/web-search.md +25 -0
  1059. package/src/prompts/system/workflow-notice.md +112 -0
  1060. package/src/prompts/system/xdev-mount-notice.md +20 -0
  1061. package/src/prompts/tools/apply-patch.md +65 -0
  1062. package/src/prompts/tools/ask.md +22 -0
  1063. package/src/prompts/tools/ast-edit.md +11 -0
  1064. package/src/prompts/tools/ast-grep.md +19 -0
  1065. package/src/prompts/tools/async-result.md +8 -0
  1066. package/src/prompts/tools/bash.md +23 -0
  1067. package/src/prompts/tools/browser.md +29 -0
  1068. package/src/prompts/tools/checkpoint.md +15 -0
  1069. package/src/prompts/tools/computer.md +26 -0
  1070. package/src/prompts/tools/debug.md +3 -0
  1071. package/src/prompts/tools/eval.md +45 -0
  1072. package/src/prompts/tools/github.md +22 -0
  1073. package/src/prompts/tools/glob.md +16 -0
  1074. package/src/prompts/tools/goal.md +11 -0
  1075. package/src/prompts/tools/grep.md +13 -0
  1076. package/src/prompts/tools/hub.md +34 -0
  1077. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  1078. package/src/prompts/tools/image-attachment-describe.md +10 -0
  1079. package/src/prompts/tools/image-gen.md +7 -0
  1080. package/src/prompts/tools/inspect-image-system.md +20 -0
  1081. package/src/prompts/tools/inspect-image.md +22 -0
  1082. package/src/prompts/tools/learn.md +7 -0
  1083. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  1084. package/src/prompts/tools/lsp.md +19 -0
  1085. package/src/prompts/tools/manage-skill.md +9 -0
  1086. package/src/prompts/tools/memory-edit.md +12 -0
  1087. package/src/prompts/tools/patch.md +57 -0
  1088. package/src/prompts/tools/read.md +27 -0
  1089. package/src/prompts/tools/recall.md +7 -0
  1090. package/src/prompts/tools/reflect.md +5 -0
  1091. package/src/prompts/tools/replace.md +30 -0
  1092. package/src/prompts/tools/retain.md +6 -0
  1093. package/src/prompts/tools/rewind.md +14 -0
  1094. package/src/prompts/tools/security-publish.md +1 -0
  1095. package/src/prompts/tools/security-scan.md +1 -0
  1096. package/src/prompts/tools/task-async-contract.md +1 -0
  1097. package/src/prompts/tools/task-summary.md +20 -0
  1098. package/src/prompts/tools/task.md +83 -0
  1099. package/src/prompts/tools/todo.md +42 -0
  1100. package/src/prompts/tools/vibe-kill.md +3 -0
  1101. package/src/prompts/tools/vibe-list.md +3 -0
  1102. package/src/prompts/tools/vibe-send.md +9 -0
  1103. package/src/prompts/tools/vibe-spawn.md +10 -0
  1104. package/src/prompts/tools/vibe-turn-result.md +19 -0
  1105. package/src/prompts/tools/vibe-wait.md +8 -0
  1106. package/src/prompts/tools/web-search.md +8 -0
  1107. package/src/prompts/tools/write.md +14 -0
  1108. package/src/registry/agent-lifecycle.ts +505 -0
  1109. package/src/registry/agent-registry.ts +287 -0
  1110. package/src/registry/persisted-agents.ts +423 -0
  1111. package/src/sdk.ts +3820 -0
  1112. package/src/secrets/index.ts +375 -0
  1113. package/src/secrets/obfuscator.ts +2629 -0
  1114. package/src/secrets/regex.ts +21 -0
  1115. package/src/security/auth.ts +98 -0
  1116. package/src/security/cloud.ts +686 -0
  1117. package/src/security/comparison.ts +255 -0
  1118. package/src/security/contracts/ids.ts +111 -0
  1119. package/src/security/contracts/index.ts +4 -0
  1120. package/src/security/contracts/schemas.ts +219 -0
  1121. package/src/security/contracts/types.ts +254 -0
  1122. package/src/security/contracts/validation.ts +69 -0
  1123. package/src/security/coordinator.ts +708 -0
  1124. package/src/security/importers/codex-security.ts +387 -0
  1125. package/src/security/importers/index.ts +2 -0
  1126. package/src/security/importers/sarif.ts +357 -0
  1127. package/src/security/index.ts +13 -0
  1128. package/src/security/preflight.ts +405 -0
  1129. package/src/security/provenance.ts +106 -0
  1130. package/src/security/publication.ts +326 -0
  1131. package/src/security/remediation.ts +93 -0
  1132. package/src/security/resource-output.ts +50 -0
  1133. package/src/security/sarif.ts +78 -0
  1134. package/src/security/store.ts +430 -0
  1135. package/src/session/acp-permission-gate.ts +165 -0
  1136. package/src/session/agent-session-events.ts +67 -0
  1137. package/src/session/agent-session-types.ts +407 -0
  1138. package/src/session/agent-session.ts +9000 -0
  1139. package/src/session/agent-storage.ts +807 -0
  1140. package/src/session/artifacts.ts +154 -0
  1141. package/src/session/async-job-delivery.ts +82 -0
  1142. package/src/session/auth-broker-config.ts +92 -0
  1143. package/src/session/auth-storage.ts +25 -0
  1144. package/src/session/bash-runner.ts +326 -0
  1145. package/src/session/blob-store.ts +295 -0
  1146. package/src/session/checkpoint-entries.ts +81 -0
  1147. package/src/session/claude-session-store.ts +426 -0
  1148. package/src/session/client-bridge.ts +85 -0
  1149. package/src/session/codex-auto-reset.ts +673 -0
  1150. package/src/session/codex-session-store.ts +673 -0
  1151. package/src/session/compact-modes.ts +105 -0
  1152. package/src/session/credential-pin.ts +93 -0
  1153. package/src/session/eval-runner.ts +219 -0
  1154. package/src/session/exit-diagnostics.ts +310 -0
  1155. package/src/session/foreign-session-import.ts +52 -0
  1156. package/src/session/foreign-session-jsonl.ts +29 -0
  1157. package/src/session/foreign-session-store.ts +26 -0
  1158. package/src/session/history-storage.ts +329 -0
  1159. package/src/session/indexed-session-storage.ts +553 -0
  1160. package/src/session/irc-bridge.ts +203 -0
  1161. package/src/session/launch-completion.ts +37 -0
  1162. package/src/session/messages.ts +1304 -0
  1163. package/src/session/model-controls.ts +757 -0
  1164. package/src/session/prewalk.ts +279 -0
  1165. package/src/session/provider-image-budget.ts +86 -0
  1166. package/src/session/queued-messages.ts +99 -0
  1167. package/src/session/redis-session-storage.ts +257 -0
  1168. package/src/session/retry-fallback-chains.ts +455 -0
  1169. package/src/session/role-models.ts +85 -0
  1170. package/src/session/session-advisors.ts +1896 -0
  1171. package/src/session/session-context.ts +584 -0
  1172. package/src/session/session-dump-format.ts +216 -0
  1173. package/src/session/session-entries.ts +306 -0
  1174. package/src/session/session-handoff.ts +325 -0
  1175. package/src/session/session-history-format.ts +459 -0
  1176. package/src/session/session-listing.ts +715 -0
  1177. package/src/session/session-loader.ts +362 -0
  1178. package/src/session/session-maintenance.ts +3115 -0
  1179. package/src/session/session-manager.ts +2747 -0
  1180. package/src/session/session-memory.ts +222 -0
  1181. package/src/session/session-metadata.ts +53 -0
  1182. package/src/session/session-migrations.ts +78 -0
  1183. package/src/session/session-paths.ts +280 -0
  1184. package/src/session/session-persistence.ts +293 -0
  1185. package/src/session/session-provider-boundary.ts +306 -0
  1186. package/src/session/session-stats.ts +349 -0
  1187. package/src/session/session-storage.ts +774 -0
  1188. package/src/session/session-title-slot.ts +141 -0
  1189. package/src/session/session-tools.ts +1385 -0
  1190. package/src/session/session-workspace.ts +53 -0
  1191. package/src/session/settings-stream-fn.ts +79 -0
  1192. package/src/session/shake-types.ts +43 -0
  1193. package/src/session/snapcompact-inline.ts +545 -0
  1194. package/src/session/snapcompact-savings-journal.ts +113 -0
  1195. package/src/session/sql-session-storage.ts +374 -0
  1196. package/src/session/stream-guards.ts +417 -0
  1197. package/src/session/streaming-output.ts +1459 -0
  1198. package/src/session/todo-tracker.ts +380 -0
  1199. package/src/session/tool-choice-queue.ts +305 -0
  1200. package/src/session/ttsr-coordinator.ts +496 -0
  1201. package/src/session/turn-persistence.ts +142 -0
  1202. package/src/session/turn-recovery.ts +2005 -0
  1203. package/src/session/unexpected-stop-classifier.ts +141 -0
  1204. package/src/session/yield-queue.ts +288 -0
  1205. package/src/slash-commands/acp-builtins.ts +70 -0
  1206. package/src/slash-commands/available-commands.ts +105 -0
  1207. package/src/slash-commands/builtin-registry.ts +3149 -0
  1208. package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
  1209. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  1210. package/src/slash-commands/helpers/context-report.ts +66 -0
  1211. package/src/slash-commands/helpers/format.ts +46 -0
  1212. package/src/slash-commands/helpers/logout.ts +108 -0
  1213. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  1214. package/src/slash-commands/helpers/mcp.ts +533 -0
  1215. package/src/slash-commands/helpers/parse.ts +85 -0
  1216. package/src/slash-commands/helpers/reset-usage.ts +68 -0
  1217. package/src/slash-commands/helpers/security.ts +451 -0
  1218. package/src/slash-commands/helpers/session-pin.ts +44 -0
  1219. package/src/slash-commands/helpers/ssh.ts +196 -0
  1220. package/src/slash-commands/helpers/stats-dashboard.ts +86 -0
  1221. package/src/slash-commands/helpers/todo.ts +285 -0
  1222. package/src/slash-commands/helpers/usage-report.ts +198 -0
  1223. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  1224. package/src/slash-commands/types.ts +139 -0
  1225. package/src/ssh/config-writer.ts +183 -0
  1226. package/src/ssh/connection-manager.ts +667 -0
  1227. package/src/ssh/file-transfer.ts +214 -0
  1228. package/src/ssh/sshfs-mount.ts +163 -0
  1229. package/src/ssh/utils.ts +51 -0
  1230. package/src/startup-splash.ts +19 -0
  1231. package/src/stt/asr-client.ts +401 -0
  1232. package/src/stt/asr-protocol.ts +65 -0
  1233. package/src/stt/asr-worker.ts +603 -0
  1234. package/src/stt/downloader.ts +142 -0
  1235. package/src/stt/endpointer.ts +259 -0
  1236. package/src/stt/index.ts +6 -0
  1237. package/src/stt/models.ts +150 -0
  1238. package/src/stt/sherpa-runtime.ts +71 -0
  1239. package/src/stt/stt-controller.ts +320 -0
  1240. package/src/stt/submit-trigger.ts +74 -0
  1241. package/src/subprocess/worker-client.ts +463 -0
  1242. package/src/subprocess/worker-runtime.ts +494 -0
  1243. package/src/system-prompt.ts +921 -0
  1244. package/src/task/agents.ts +170 -0
  1245. package/src/task/commands.ts +132 -0
  1246. package/src/task/discovery.ts +145 -0
  1247. package/src/task/executor.ts +3431 -0
  1248. package/src/task/index.ts +1515 -0
  1249. package/src/task/isolation-ownership.ts +106 -0
  1250. package/src/task/isolation-runner.ts +444 -0
  1251. package/src/task/label.ts +40 -0
  1252. package/src/task/name-generator.ts +1577 -0
  1253. package/src/task/omp-command.ts +26 -0
  1254. package/src/task/output-manager.ts +115 -0
  1255. package/src/task/parallel.ts +221 -0
  1256. package/src/task/persisted-revive.ts +173 -0
  1257. package/src/task/prewalk.ts +6 -0
  1258. package/src/task/prompt-policy.ts +8 -0
  1259. package/src/task/provider-concurrency.ts +100 -0
  1260. package/src/task/read-only-policy.ts +27 -0
  1261. package/src/task/render.ts +1830 -0
  1262. package/src/task/renderer.ts +14 -0
  1263. package/src/task/repair-args.ts +118 -0
  1264. package/src/task/spawn-policy.ts +72 -0
  1265. package/src/task/structured-subagent.ts +676 -0
  1266. package/src/task/subprocess-tool-registry.ts +88 -0
  1267. package/src/task/types.ts +555 -0
  1268. package/src/task/worktree.ts +967 -0
  1269. package/src/task/yield-assembly.ts +198 -0
  1270. package/src/telemetry-export.ts +504 -0
  1271. package/src/thinking.ts +377 -0
  1272. package/src/tiny/device.ts +111 -0
  1273. package/src/tiny/dtype.ts +101 -0
  1274. package/src/tiny/message-preproc.ts +155 -0
  1275. package/src/tiny/models.ts +268 -0
  1276. package/src/tiny/text.ts +290 -0
  1277. package/src/tiny/title-client.ts +460 -0
  1278. package/src/tiny/title-protocol.ts +56 -0
  1279. package/src/tiny/worker.ts +354 -0
  1280. package/src/tools/acp-bridge.ts +125 -0
  1281. package/src/tools/approval.ts +245 -0
  1282. package/src/tools/ask.ts +1459 -0
  1283. package/src/tools/ast-edit.ts +718 -0
  1284. package/src/tools/ast-grep.ts +526 -0
  1285. package/src/tools/auto-generated-guard.ts +335 -0
  1286. package/src/tools/bash-interactive.ts +435 -0
  1287. package/src/tools/bash-interceptor.ts +148 -0
  1288. package/src/tools/bash-pty-selection.ts +14 -0
  1289. package/src/tools/bash-skill-urls.ts +350 -0
  1290. package/src/tools/bash.ts +1826 -0
  1291. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  1292. package/src/tools/browser/aria/aria-snapshot.ts +131 -0
  1293. package/src/tools/browser/attach.ts +219 -0
  1294. package/src/tools/browser/cmux/cmux-tab.ts +1531 -0
  1295. package/src/tools/browser/cmux/rpc.ts +200 -0
  1296. package/src/tools/browser/cmux/socket-client.ts +445 -0
  1297. package/src/tools/browser/launch.ts +939 -0
  1298. package/src/tools/browser/readable.ts +112 -0
  1299. package/src/tools/browser/registry.ts +447 -0
  1300. package/src/tools/browser/relay/bridge.ts +945 -0
  1301. package/src/tools/browser/relay/daemon.ts +117 -0
  1302. package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
  1303. package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
  1304. package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
  1305. package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
  1306. package/src/tools/browser/relay/kind.ts +41 -0
  1307. package/src/tools/browser/relay/protocol.ts +54 -0
  1308. package/src/tools/browser/relay/server.ts +141 -0
  1309. package/src/tools/browser/render.ts +229 -0
  1310. package/src/tools/browser/run-output.ts +76 -0
  1311. package/src/tools/browser/shared-daemon.ts +139 -0
  1312. package/src/tools/browser/tab-protocol.ts +123 -0
  1313. package/src/tools/browser/tab-supervisor.ts +1077 -0
  1314. package/src/tools/browser/tab-worker-entry.ts +29 -0
  1315. package/src/tools/browser/tab-worker.ts +1982 -0
  1316. package/src/tools/browser.ts +487 -0
  1317. package/src/tools/builtin-names.ts +67 -0
  1318. package/src/tools/checkpoint.ts +137 -0
  1319. package/src/tools/computer/exposure.ts +14 -0
  1320. package/src/tools/computer/protocol.ts +69 -0
  1321. package/src/tools/computer/supervisor.ts +409 -0
  1322. package/src/tools/computer/worker-entry.ts +39 -0
  1323. package/src/tools/computer/worker.ts +745 -0
  1324. package/src/tools/computer-renderer.ts +147 -0
  1325. package/src/tools/computer.ts +222 -0
  1326. package/src/tools/conflict-detect.ts +815 -0
  1327. package/src/tools/context.ts +49 -0
  1328. package/src/tools/debug.ts +1121 -0
  1329. package/src/tools/default-renderer.ts +154 -0
  1330. package/src/tools/essential-tools.ts +46 -0
  1331. package/src/tools/eval-backends.ts +34 -0
  1332. package/src/tools/eval-format/index.ts +24 -0
  1333. package/src/tools/eval-format/javascript.ts +952 -0
  1334. package/src/tools/eval-format/julia.ts +446 -0
  1335. package/src/tools/eval-format/python.ts +544 -0
  1336. package/src/tools/eval-format/ruby.ts +380 -0
  1337. package/src/tools/eval-render.ts +784 -0
  1338. package/src/tools/eval.ts +774 -0
  1339. package/src/tools/fetch.ts +1889 -0
  1340. package/src/tools/file-recorder.ts +35 -0
  1341. package/src/tools/fs-cache-invalidation.ts +28 -0
  1342. package/src/tools/gh-cache-invalidation.ts +175 -0
  1343. package/src/tools/gh-format.ts +12 -0
  1344. package/src/tools/gh-renderer.ts +484 -0
  1345. package/src/tools/gh.ts +3958 -0
  1346. package/src/tools/github-cache.ts +663 -0
  1347. package/src/tools/glob.ts +691 -0
  1348. package/src/tools/grep.ts +1920 -0
  1349. package/src/tools/grouped-file-output.ts +210 -0
  1350. package/src/tools/hub/index.ts +579 -0
  1351. package/src/tools/hub/jobs.ts +714 -0
  1352. package/src/tools/hub/launch.ts +690 -0
  1353. package/src/tools/hub/messaging.ts +735 -0
  1354. package/src/tools/hub/types.ts +117 -0
  1355. package/src/tools/image-gen.ts +1689 -0
  1356. package/src/tools/image-providers.ts +50 -0
  1357. package/src/tools/index.ts +736 -0
  1358. package/src/tools/inspect-image-renderer.ts +133 -0
  1359. package/src/tools/inspect-image.ts +316 -0
  1360. package/src/tools/json-tree.ts +260 -0
  1361. package/src/tools/jtd-to-json-schema.ts +219 -0
  1362. package/src/tools/jtd-to-typescript.ts +136 -0
  1363. package/src/tools/jtd-utils.ts +102 -0
  1364. package/src/tools/learn.ts +141 -0
  1365. package/src/tools/list-limit.ts +40 -0
  1366. package/src/tools/manage-skill.ts +102 -0
  1367. package/src/tools/match-line-format.ts +20 -0
  1368. package/src/tools/memory-edit.ts +61 -0
  1369. package/src/tools/memory-recall.ts +102 -0
  1370. package/src/tools/memory-reflect.ts +88 -0
  1371. package/src/tools/memory-render.ts +211 -0
  1372. package/src/tools/memory-retain.ts +89 -0
  1373. package/src/tools/output-meta.ts +860 -0
  1374. package/src/tools/output-schema-validator.ts +307 -0
  1375. package/src/tools/path-utils.ts +1489 -0
  1376. package/src/tools/plan-mode-guard.ts +155 -0
  1377. package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
  1378. package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
  1379. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
  1380. package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
  1381. package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
  1382. package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
  1383. package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
  1384. package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
  1385. package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
  1386. package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
  1387. package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
  1388. package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
  1389. package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
  1390. package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
  1391. package/src/tools/read.ts +3762 -0
  1392. package/src/tools/render-utils.ts +926 -0
  1393. package/src/tools/renderers.ts +133 -0
  1394. package/src/tools/report-tool-issue.ts +568 -0
  1395. package/src/tools/resolve.ts +423 -0
  1396. package/src/tools/review.ts +103 -0
  1397. package/src/tools/run-scope.ts +417 -0
  1398. package/src/tools/security-scan.ts +287 -0
  1399. package/src/tools/shell-tokenize.ts +213 -0
  1400. package/src/tools/sqlite-reader.ts +884 -0
  1401. package/src/tools/terminal-output.ts +141 -0
  1402. package/src/tools/todo.ts +1226 -0
  1403. package/src/tools/tool-errors.ts +62 -0
  1404. package/src/tools/tool-result.ts +102 -0
  1405. package/src/tools/tool-timeouts.ts +38 -0
  1406. package/src/tools/tts.ts +266 -0
  1407. package/src/tools/vibe.ts +608 -0
  1408. package/src/tools/write.ts +1646 -0
  1409. package/src/tools/xdev.ts +560 -0
  1410. package/src/tools/yield.ts +486 -0
  1411. package/src/tts/downloader.ts +64 -0
  1412. package/src/tts/index.ts +10 -0
  1413. package/src/tts/models.ts +137 -0
  1414. package/src/tts/runtime.ts +21 -0
  1415. package/src/tts/speakable.ts +392 -0
  1416. package/src/tts/speech-enhancer.ts +206 -0
  1417. package/src/tts/streaming-player.ts +120 -0
  1418. package/src/tts/tts-client.ts +475 -0
  1419. package/src/tts/tts-protocol.ts +69 -0
  1420. package/src/tts/tts-worker.ts +434 -0
  1421. package/src/tts/vocalizer.ts +419 -0
  1422. package/src/tts/wav.ts +58 -0
  1423. package/src/tui/code-cell.ts +268 -0
  1424. package/src/tui/file-list.ts +55 -0
  1425. package/src/tui/hyperlink.ts +178 -0
  1426. package/src/tui/index.ts +13 -0
  1427. package/src/tui/output-block.ts +268 -0
  1428. package/src/tui/status-line.ts +54 -0
  1429. package/src/tui/tree-list.ts +172 -0
  1430. package/src/tui/types.ts +15 -0
  1431. package/src/tui/utils.ts +103 -0
  1432. package/src/tui/width-aware-text.ts +58 -0
  1433. package/src/utils/active-repo-context.ts +143 -0
  1434. package/src/utils/block-context.ts +312 -0
  1435. package/src/utils/changelog.ts +373 -0
  1436. package/src/utils/clipboard.ts +361 -0
  1437. package/src/utils/command-args.ts +74 -0
  1438. package/src/utils/commit-message-generator.ts +148 -0
  1439. package/src/utils/cpuprofile.ts +235 -0
  1440. package/src/utils/edit-mode.ts +61 -0
  1441. package/src/utils/enhanced-paste.ts +230 -0
  1442. package/src/utils/event-bus.ts +33 -0
  1443. package/src/utils/external-editor.ts +80 -0
  1444. package/src/utils/fetch-timeout.ts +10 -0
  1445. package/src/utils/file-display-mode.ts +44 -0
  1446. package/src/utils/file-mentions.ts +293 -0
  1447. package/src/utils/git.ts +2472 -0
  1448. package/src/utils/image-loading.ts +236 -0
  1449. package/src/utils/image-resize.ts +420 -0
  1450. package/src/utils/image-vision-fallback.ts +196 -0
  1451. package/src/utils/inspect-image-mode.ts +39 -0
  1452. package/src/utils/ipc.ts +38 -0
  1453. package/src/utils/jj.ts +422 -0
  1454. package/src/utils/lang-from-path.ts +251 -0
  1455. package/src/utils/late-cleanup.ts +17 -0
  1456. package/src/utils/local-date.ts +7 -0
  1457. package/src/utils/mac-file-urls.applescript +37 -0
  1458. package/src/utils/markit-cache.ts +166 -0
  1459. package/src/utils/markit.ts +223 -0
  1460. package/src/utils/mupdf-wasm-embed.ts +12 -0
  1461. package/src/utils/open.ts +126 -0
  1462. package/src/utils/profile-tree.ts +111 -0
  1463. package/src/utils/prompt-path.ts +3 -0
  1464. package/src/utils/qrcode.ts +535 -0
  1465. package/src/utils/sample-profile.ts +437 -0
  1466. package/src/utils/session-color.ts +142 -0
  1467. package/src/utils/shell-snapshot-fn-env.sh +63 -0
  1468. package/src/utils/shell-snapshot.ts +326 -0
  1469. package/src/utils/sixel.ts +69 -0
  1470. package/src/utils/thinking-display.ts +163 -0
  1471. package/src/utils/title-generator.ts +597 -0
  1472. package/src/utils/token-rate.ts +72 -0
  1473. package/src/utils/tool-choice.ts +50 -0
  1474. package/src/utils/tools-manager.ts +411 -0
  1475. package/src/utils/turndown.ts +82 -0
  1476. package/src/utils/zip.ts +1106 -0
  1477. package/src/vibe/runtime.ts +1684 -0
  1478. package/src/vibe/state.ts +4 -0
  1479. package/src/web/kagi.ts +305 -0
  1480. package/src/web/parallel.ts +354 -0
  1481. package/src/web/scrapers/artifacthub.ts +207 -0
  1482. package/src/web/scrapers/arxiv.ts +83 -0
  1483. package/src/web/scrapers/aur.ts +162 -0
  1484. package/src/web/scrapers/biorxiv.ts +133 -0
  1485. package/src/web/scrapers/bluesky.ts +262 -0
  1486. package/src/web/scrapers/brew.ts +172 -0
  1487. package/src/web/scrapers/cheatsh.ts +68 -0
  1488. package/src/web/scrapers/chocolatey.ts +196 -0
  1489. package/src/web/scrapers/choosealicense.ts +95 -0
  1490. package/src/web/scrapers/cisa-kev.ts +87 -0
  1491. package/src/web/scrapers/clojars.ts +154 -0
  1492. package/src/web/scrapers/coingecko.ts +177 -0
  1493. package/src/web/scrapers/crates-io.ts +97 -0
  1494. package/src/web/scrapers/crossref.ts +136 -0
  1495. package/src/web/scrapers/devto.ts +147 -0
  1496. package/src/web/scrapers/discogs.ts +306 -0
  1497. package/src/web/scrapers/discourse.ts +197 -0
  1498. package/src/web/scrapers/dockerhub.ts +138 -0
  1499. package/src/web/scrapers/docs-rs.ts +663 -0
  1500. package/src/web/scrapers/fdroid.ts +134 -0
  1501. package/src/web/scrapers/firefox-addons.ts +191 -0
  1502. package/src/web/scrapers/flathub.ts +223 -0
  1503. package/src/web/scrapers/github-gist.ts +58 -0
  1504. package/src/web/scrapers/github.ts +800 -0
  1505. package/src/web/scrapers/gitlab.ts +401 -0
  1506. package/src/web/scrapers/go-pkg.ts +266 -0
  1507. package/src/web/scrapers/hackage.ts +129 -0
  1508. package/src/web/scrapers/hackernews.ts +189 -0
  1509. package/src/web/scrapers/hex.ts +105 -0
  1510. package/src/web/scrapers/huggingface.ts +321 -0
  1511. package/src/web/scrapers/iacr.ts +89 -0
  1512. package/src/web/scrapers/index.ts +252 -0
  1513. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1514. package/src/web/scrapers/lemmy.ts +203 -0
  1515. package/src/web/scrapers/lobsters.ts +175 -0
  1516. package/src/web/scrapers/mastodon.ts +292 -0
  1517. package/src/web/scrapers/maven.ts +138 -0
  1518. package/src/web/scrapers/mdn.ts +173 -0
  1519. package/src/web/scrapers/metacpan.ts +222 -0
  1520. package/src/web/scrapers/musicbrainz.ts +250 -0
  1521. package/src/web/scrapers/npm.ts +98 -0
  1522. package/src/web/scrapers/nuget.ts +183 -0
  1523. package/src/web/scrapers/nvd.ts +222 -0
  1524. package/src/web/scrapers/ollama.ts +239 -0
  1525. package/src/web/scrapers/open-vsx.ts +106 -0
  1526. package/src/web/scrapers/opencorporates.ts +292 -0
  1527. package/src/web/scrapers/openlibrary.ts +336 -0
  1528. package/src/web/scrapers/orcid.ts +286 -0
  1529. package/src/web/scrapers/osv.ts +176 -0
  1530. package/src/web/scrapers/packagist.ts +160 -0
  1531. package/src/web/scrapers/pub-dev.ts +143 -0
  1532. package/src/web/scrapers/pubmed.ts +211 -0
  1533. package/src/web/scrapers/pypi.ts +112 -0
  1534. package/src/web/scrapers/rawg.ts +110 -0
  1535. package/src/web/scrapers/readthedocs.ts +121 -0
  1536. package/src/web/scrapers/reddit.ts +95 -0
  1537. package/src/web/scrapers/repology.ts +251 -0
  1538. package/src/web/scrapers/rfc.ts +201 -0
  1539. package/src/web/scrapers/rubygems.ts +103 -0
  1540. package/src/web/scrapers/searchcode.ts +189 -0
  1541. package/src/web/scrapers/sec-edgar.ts +261 -0
  1542. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1543. package/src/web/scrapers/snapcraft.ts +187 -0
  1544. package/src/web/scrapers/sourcegraph.ts +336 -0
  1545. package/src/web/scrapers/spdx.ts +108 -0
  1546. package/src/web/scrapers/spotify.ts +198 -0
  1547. package/src/web/scrapers/stackoverflow.ts +120 -0
  1548. package/src/web/scrapers/terraform.ts +277 -0
  1549. package/src/web/scrapers/tldr.ts +47 -0
  1550. package/src/web/scrapers/twitter.ts +94 -0
  1551. package/src/web/scrapers/types.ts +354 -0
  1552. package/src/web/scrapers/utils.ts +109 -0
  1553. package/src/web/scrapers/vimeo.ts +133 -0
  1554. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1555. package/src/web/scrapers/w3c.ts +156 -0
  1556. package/src/web/scrapers/wikidata.ts +344 -0
  1557. package/src/web/scrapers/wikipedia.ts +84 -0
  1558. package/src/web/scrapers/youtube.ts +325 -0
  1559. package/src/web/search/index.ts +393 -0
  1560. package/src/web/search/provider.ts +272 -0
  1561. package/src/web/search/providers/anthropic.ts +406 -0
  1562. package/src/web/search/providers/base.ts +112 -0
  1563. package/src/web/search/providers/brave.ts +181 -0
  1564. package/src/web/search/providers/browser-headers.ts +82 -0
  1565. package/src/web/search/providers/browser-page.ts +125 -0
  1566. package/src/web/search/providers/codex.ts +788 -0
  1567. package/src/web/search/providers/duckduckgo.ts +382 -0
  1568. package/src/web/search/providers/ecosia.ts +183 -0
  1569. package/src/web/search/providers/exa.ts +513 -0
  1570. package/src/web/search/providers/firecrawl.ts +213 -0
  1571. package/src/web/search/providers/gemini.ts +634 -0
  1572. package/src/web/search/providers/google.ts +195 -0
  1573. package/src/web/search/providers/jina.ts +144 -0
  1574. package/src/web/search/providers/kagi.ts +98 -0
  1575. package/src/web/search/providers/kimi.ts +221 -0
  1576. package/src/web/search/providers/mojeek.ts +220 -0
  1577. package/src/web/search/providers/parallel.ts +186 -0
  1578. package/src/web/search/providers/perplexity-auth.ts +142 -0
  1579. package/src/web/search/providers/perplexity.ts +997 -0
  1580. package/src/web/search/providers/public.ts +199 -0
  1581. package/src/web/search/providers/searxng.ts +467 -0
  1582. package/src/web/search/providers/startpage.ts +225 -0
  1583. package/src/web/search/providers/synthetic.ts +126 -0
  1584. package/src/web/search/providers/tavily.ts +244 -0
  1585. package/src/web/search/providers/tinyfish.ts +166 -0
  1586. package/src/web/search/providers/utils.ts +130 -0
  1587. package/src/web/search/providers/xai.ts +372 -0
  1588. package/src/web/search/providers/zai.ts +452 -0
  1589. package/src/web/search/query.ts +850 -0
  1590. package/src/web/search/render.ts +262 -0
  1591. package/src/web/search/types.ts +518 -0
  1592. package/src/web/search/utils.ts +17 -0
  1593. package/src/workspace-tree.ts +326 -0
@@ -0,0 +1,1920 @@
1
+ import { mkdtemp, rm, stat, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import * as path from "node:path";
4
+ import { formatHashlineHeader } from "@oh-my-pi/hashline";
5
+ import { type } from "@oh-my-pi/omptype";
6
+ import type {
7
+ AgentTool,
8
+ AgentToolContext,
9
+ AgentToolResult,
10
+ AgentToolUpdateCallback,
11
+ ToolTier,
12
+ } from "@oh-my-pi/pi-agent-core";
13
+ import { type GrepMatch, GrepOutputMode, type GrepResult, grep } from "@oh-my-pi/pi-natives";
14
+ import type { Component } from "@oh-my-pi/pi-tui";
15
+ import { Text } from "@oh-my-pi/pi-tui";
16
+ import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
17
+ import { recordFileSnapshot, recordSeenLinesFromBody } from "../edit/file-snapshot-store";
18
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
19
+ import type { LocalProtocolOptions } from "../internal-urls/local-protocol";
20
+ import { InternalUrlRouter } from "../internal-urls/router";
21
+ import type { InternalResource, ResolveContext } from "../internal-urls/types";
22
+ import type { Theme } from "../modes/theme/theme";
23
+ import grepDescription from "../prompts/tools/grep.md" with { type: "text" };
24
+ import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead, truncateLine } from "../session/streaming-output";
25
+ import { isScoutSpawnable } from "../task/spawn-policy";
26
+ import {
27
+ Ellipsis,
28
+ fileHyperlink,
29
+ getTreeBranch,
30
+ getTreeContinuePrefix,
31
+ renderStatusLine,
32
+ renderTreeList,
33
+ truncateToWidth,
34
+ tryResolveInternalUrlSync,
35
+ uriHyperlink,
36
+ } from "../tui";
37
+ import { resolveFileDisplayMode } from "../utils/file-display-mode";
38
+ import { type ArchiveReader, type ExtractedArchiveFile, openArchive, parseArchivePathCandidates } from "../utils/zip";
39
+ import type { ToolSession } from ".";
40
+ import { materializeReadUrlToFile, parseReadUrlTarget } from "./fetch";
41
+ import { createFileRecorder, formatResultPath } from "./file-recorder";
42
+ import { classifyGroupedLines, formatGroupedFiles, groupLineIndicesByBlank } from "./grouped-file-output";
43
+ import { formatMatchLine } from "./match-line-format";
44
+ import type { OutputMeta } from "./output-meta";
45
+ import {
46
+ expandDelimitedPathEntries,
47
+ hasGlobPathChars,
48
+ isLineInRanges,
49
+ type LineRange,
50
+ parseLineRanges,
51
+ pathTargetsSsh,
52
+ type ResolvedSearchTarget,
53
+ resolveReadPath,
54
+ resolveToolSearchScope,
55
+ selectorLineRanges,
56
+ splitInternalUrlSel,
57
+ splitPathAndSel,
58
+ splitPathAndSelPreferringLiteral,
59
+ toPathList,
60
+ } from "./path-utils";
61
+ import {
62
+ createCachedComponent,
63
+ formatCodeFrameLine,
64
+ formatCount,
65
+ formatEmptyMessage,
66
+ formatErrorMessage,
67
+ formatMoreItems,
68
+ PREVIEW_LIMITS,
69
+ replaceTabs,
70
+ } from "./render-utils";
71
+ import { ToolError } from "./tool-errors";
72
+ import { toolResult } from "./tool-result";
73
+
74
+ const searchPathEntry = type("string").describe(
75
+ 'file, directory, glob, internal URL, or "<file>:<lines>" selector to search (e.g. "src/foo.ts:50-100", "src/foo.ts:50+10", "src/foo.ts:50-100,200-300")',
76
+ );
77
+ const searchSchema = type({
78
+ pattern: type("string").describe("regex pattern"),
79
+ "path?": searchPathEntry.describe(
80
+ 'file, directory, glob, internal URL, or "<file>:<lines>" selector to search; pass several as a semicolon-delimited list ("src; tests"). Omitted -> searches the workspace root (".")',
81
+ ),
82
+ "case?": type("boolean").describe("case-sensitive search"),
83
+ "gitignore?": type("boolean").describe("respect gitignore"),
84
+ "skip?": type("number")
85
+ .or("null")
86
+ .describe("files to skip before collecting results — use to paginate when the prior call hit the file limit"),
87
+ });
88
+
89
+ export type GrepToolInput = typeof searchSchema.infer;
90
+
91
+ /** Maximum number of distinct files surfaced in a single response. The
92
+ * agent paginates further pages via `skip`. */
93
+ export const DEFAULT_FILE_LIMIT = 20;
94
+ /** Per-file match cap for multi-file searches — keeps a single hot file
95
+ * from crowding out diverse hits. Applied in JS after grep returns. */
96
+ export const MULTI_FILE_PER_FILE_MATCHES = 20;
97
+ /** Per-file match cap for single-file searches — there's no diversity
98
+ * concern when the scope is one file. */
99
+ export const SINGLE_FILE_MATCHES = 200;
100
+ /** Hard safety ceiling on how many matches we fetch from native grep
101
+ * before JS-side grouping. Sized to comfortably cover the file window
102
+ * (DEFAULT_FILE_LIMIT files × MULTI_FILE_PER_FILE_MATCHES matches) plus
103
+ * pagination headroom so the caller can see total file count. */
104
+ const INTERNAL_TOTAL_CAP = 2000;
105
+ /** Mirrors `MAX_FILE_BYTES` in `crates/pi-natives/src/grep.rs`. Native grep
106
+ * searches only the first `MAX_FILE_BYTES` of a larger file (a leading mmap
107
+ * window) and drops the rest; matches beyond the window are not returned. We
108
+ * surface a partial-coverage note when the caller explicitly targeted such a
109
+ * file so they know matches past the window are not shown. */
110
+ const NATIVE_GREP_MAX_FILE_BYTES = 4 * 1024 * 1024;
111
+ /** Wall-clock budget for a single native grep invocation. Without it, an
112
+ * aborted or runaway search (huge tree, network mount) keeps burning CPU on
113
+ * the native thread pool after the JS promise is abandoned. */
114
+ const SEARCH_GREP_TIMEOUT_MS = 30_000;
115
+
116
+ /**
117
+ * Parsed `paths` entry — a path (possibly archive-shaped) plus an optional
118
+ * line-range selector peeled off the trailing `:N-M` (or `:N+K`, `:N,M`, …)
119
+ * chunk via {@link splitPathAndSel}.
120
+ */
121
+ interface GrepPathSpec {
122
+ original: string;
123
+ clean: string;
124
+ literalFilesystemMatch?: boolean;
125
+ ranges?: [LineRange, ...LineRange[]];
126
+ }
127
+
128
+ /**
129
+ * Mirror of read's `parseSel` selector grammar (`read.ts`) so `grep` accepts
130
+ * exactly the internal-URL selectors `read` accepts: a single chunk that is a
131
+ * line range, `raw`, or `conflicts`; or a two-chunk compound of exactly one `raw`
132
+ * plus one line range. Everything else (`:-10`, `:1-1:1-2`, `:conflicts:1-1`,
133
+ * `:raw:conflicts`) is rejected.
134
+ *
135
+ * This mirrors the *accepted set* of `parseSel`; `read` rejects the same shapes
136
+ * caller-side when a peeled internal-URL selector parses as `none`, so neither
137
+ * tool silently widens on a malformed compound. Keep in sync with `read.parseSel`.
138
+ */
139
+ function isReadSelectorGrammar(sel: string): boolean {
140
+ if (sel.includes(":")) {
141
+ const chunks = sel.split(":");
142
+ if (chunks.length !== 2) return false;
143
+ const [a, b] = chunks as [string, string];
144
+ const aIsRaw = a.toLowerCase() === "raw";
145
+ const bIsRaw = b.toLowerCase() === "raw";
146
+ const rangeChunk = aIsRaw ? b : bIsRaw ? a : null;
147
+ return rangeChunk !== null && parseLineRanges(rangeChunk) !== null;
148
+ }
149
+ const lower = sel.toLowerCase();
150
+ return lower === "raw" || lower === "conflicts" || parseLineRanges(sel) !== null;
151
+ }
152
+
153
+ async function parsePathSpecs(rawEntries: readonly string[], cwd: string): Promise<GrepPathSpec[]> {
154
+ const specs: GrepPathSpec[] = [];
155
+ for (const entry of rawEntries) {
156
+ // Internal URLs (`artifact://`, `skill://`, …) use the URL-aware splitter,
157
+ // which peels selector-shaped tails only for selector-capable schemes and
158
+ // leaves opaque ones (`mcp://`) intact. Unlike filesystem paths, their
159
+ // verbatim/index display modes (`raw`, `conflicts`) carry no meaning for
160
+ // content search, so we accept them — searching the whole resource — and
161
+ // still honor any embedded line range as a match filter.
162
+ const internalSplit = splitInternalUrlSel(entry);
163
+ if (internalSplit.sel !== undefined) {
164
+ // Reject selectors read's parseSel would reject (`:-10`, `:1-1:1-2`,
165
+ // `:conflicts:1-1`) instead of silently widening the search or dropping a chunk.
166
+ if (!isReadSelectorGrammar(internalSplit.sel)) {
167
+ throw new ToolError(
168
+ `path entry "${entry}" has an invalid selector ":${internalSplit.sel}" — use ":N-M" line ranges, ":raw"/":conflicts", a range plus ":raw", or percent-encode a literal ":" as %3A`,
169
+ );
170
+ }
171
+ specs.push({ original: entry, clean: internalSplit.path, ranges: selectorLineRanges(internalSplit.sel) });
172
+ continue;
173
+ }
174
+ // Prefer a literal filesystem match when one exists — a real file named
175
+ // `test:1-2` outranks the `:1-2` selector interpretation (issue #4618).
176
+ const strictSplit = splitPathAndSel(entry);
177
+ const split = await splitPathAndSelPreferringLiteral(entry, cwd);
178
+ const literalFilesystemMatch = strictSplit.sel !== undefined && split.sel === undefined;
179
+ let clean = literalFilesystemMatch ? resolveReadPath(entry, cwd) : entry;
180
+ let ranges: [LineRange, ...LineRange[]] | undefined;
181
+ if (!literalFilesystemMatch && split.sel) {
182
+ const parsed = parseLineRanges(split.sel);
183
+ if (!parsed) {
184
+ throw new ToolError(
185
+ `path entry "${entry}" — only line-range selectors like ":50-100" are supported (no ":raw"/":conflicts")`,
186
+ );
187
+ }
188
+ if (hasGlobPathChars(split.path)) {
189
+ throw new ToolError(`Line-range selector requires a single file, not a glob: ${entry}`);
190
+ }
191
+ clean = split.path;
192
+ ranges = parsed;
193
+ }
194
+ specs.push({
195
+ original: entry,
196
+ clean,
197
+ literalFilesystemMatch,
198
+ ranges,
199
+ });
200
+ }
201
+ return specs;
202
+ }
203
+
204
+ function mergeRangesInto(map: Map<string, LineRange[]>, absKey: string, ranges: readonly LineRange[]): void {
205
+ // Concat-without-merge is correct: `isLineInRanges` scans linearly, so
206
+ // duplicates/overlaps only cost a few extra comparisons per match.
207
+ const existing = map.get(absKey);
208
+ if (existing) {
209
+ existing.push(...ranges);
210
+ } else {
211
+ map.set(absKey, [...ranges]);
212
+ }
213
+ }
214
+
215
+ function matchAbsolutePath(matchPath: string, searchPath: string): string {
216
+ if (matchPath === "") return searchPath;
217
+ if (path.isAbsolute(matchPath)) return matchPath;
218
+ return path.resolve(searchPath, matchPath);
219
+ }
220
+
221
+ /**
222
+ * Pre-resolve any `paths` entries that point at a member inside an archive
223
+ * (e.g. `bundle.zip:src/foo.ts`, `release.tar.gz:notes.md`). Native grep
224
+ * cannot read archive members, so we materialize each text member to a
225
+ * temp scratch file and substitute that path into the search inputs. After
226
+ * grep returns, callers remap `match.path` back to the original
227
+ * `archive:member` selector so it round-trips through the `read` tool.
228
+ *
229
+ * Returns the rewritten paths array (same length/order as input), a map
230
+ * from absolute scratch path → original selector, a list of entries we
231
+ * could not materialize (binary member, missing archive, etc.), and a
232
+ * cleanup hook the caller MUST invoke in a `finally`.
233
+ */
234
+ async function resolveArchiveSearchPaths(
235
+ pathSpecs: readonly GrepPathSpec[],
236
+ cwd: string,
237
+ ): Promise<{
238
+ resolvedPaths: string[];
239
+ displayMap: Map<string, string>;
240
+ displaySet: Set<string>;
241
+ unreadable: string[];
242
+ cleanup: () => Promise<void>;
243
+ }> {
244
+ const resolvedPaths = pathSpecs.map(spec => spec.clean);
245
+ const displayMap = new Map<string, string>();
246
+ const displaySet = new Set<string>();
247
+ const unreadable: string[] = [];
248
+ let tempDir: string | undefined;
249
+ const archiveCache = new Map<string, ArchiveReader>();
250
+
251
+ for (let idx = 0; idx < pathSpecs.length; idx++) {
252
+ const spec = pathSpecs[idx];
253
+ if (!spec || spec.literalFilesystemMatch) continue;
254
+ const entry = spec.clean;
255
+ const candidates = parseArchivePathCandidates(entry);
256
+ const member = candidates.find(c => c.subPath !== "" && c.archivePath !== entry);
257
+ if (!member) continue;
258
+
259
+ const archiveAbs = resolveReadPath(member.archivePath, cwd);
260
+ let archive = archiveCache.get(archiveAbs);
261
+ if (!archive) {
262
+ try {
263
+ archive = await openArchive(archiveAbs);
264
+ } catch (err) {
265
+ unreadable.push(`${entry} (cannot open archive: ${(err as Error).message})`);
266
+ continue;
267
+ }
268
+ archiveCache.set(archiveAbs, archive);
269
+ }
270
+
271
+ let extracted: ExtractedArchiveFile;
272
+ try {
273
+ extracted = await archive.readFile(member.subPath);
274
+ } catch (err) {
275
+ unreadable.push(`${entry} (${(err as Error).message})`);
276
+ continue;
277
+ }
278
+ // UTF-8 only — binary members would just produce noise through ripgrep.
279
+ if (extracted.bytes.some(byte => byte === 0)) {
280
+ unreadable.push(`${entry} (binary archive entry)`);
281
+ continue;
282
+ }
283
+ let text: string;
284
+ try {
285
+ text = new TextDecoder("utf-8", { fatal: true }).decode(extracted.bytes);
286
+ } catch {
287
+ unreadable.push(`${entry} (non-UTF-8 archive entry)`);
288
+ continue;
289
+ }
290
+
291
+ if (!tempDir) {
292
+ tempDir = await mkdtemp(path.join(tmpdir(), "omp-search-archive-"));
293
+ }
294
+ // Per-entry filename keeps the scratch path unique even when two selectors
295
+ // resolve to members with the same basename.
296
+ const safeBase = path.basename(member.subPath).replace(/[^\w.-]+/g, "_") || "entry";
297
+ const tempPath = path.join(tempDir, `${idx}-${safeBase}`);
298
+ await writeFile(tempPath, text);
299
+ resolvedPaths[idx] = tempPath;
300
+ displayMap.set(tempPath, entry);
301
+ displaySet.add(entry);
302
+ }
303
+
304
+ const cleanup = async () => {
305
+ if (tempDir) {
306
+ await rm(tempDir, { recursive: true, force: true }).catch(() => {});
307
+ }
308
+ };
309
+ return { resolvedPaths, displayMap, displaySet, unreadable, cleanup };
310
+ }
311
+
312
+ interface VirtualSearchResource {
313
+ path: string;
314
+ content: string;
315
+ ranges?: readonly LineRange[];
316
+ }
317
+
318
+ interface InternalSearchInputResolution {
319
+ paths: string[];
320
+ resolvedPathsByInput: string[];
321
+ virtualResources: VirtualSearchResource[];
322
+ virtualPathSet: Set<string>;
323
+ virtualInputIndexes: Set<number>;
324
+ immutableSourcePaths: Set<string>;
325
+ virtualScopePath?: string;
326
+ }
327
+
328
+ function isImmutableSourcePath(filePath: string, immutableSourcePaths: ReadonlySet<string>): boolean {
329
+ for (const immutablePath of immutableSourcePaths) {
330
+ if (filePath === immutablePath || filePath.startsWith(`${immutablePath}${path.sep}`)) {
331
+ return true;
332
+ }
333
+ }
334
+ return false;
335
+ }
336
+
337
+ interface IndexedContentLines {
338
+ lines: string[];
339
+ starts: number[];
340
+ }
341
+
342
+ const OMP_ROOT_URL_RE = /^omp:\/\/(?:\/?|docs\/?)$/i;
343
+
344
+ function normalizeSearchLine(line: string): string {
345
+ return line.endsWith("\r") ? line.slice(0, -1) : line;
346
+ }
347
+
348
+ function splitSearchLines(content: string): string[] {
349
+ const lines = content.split("\n");
350
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
351
+ lines.pop();
352
+ }
353
+ return lines.map(normalizeSearchLine);
354
+ }
355
+
356
+ function indexSearchLines(content: string): IndexedContentLines {
357
+ const rawLines = content.split("\n");
358
+ if (rawLines.length > 0 && rawLines[rawLines.length - 1] === "") {
359
+ rawLines.pop();
360
+ }
361
+ const lines: string[] = [];
362
+ const starts: number[] = [];
363
+ let offset = 0;
364
+ for (const rawLine of rawLines) {
365
+ starts.push(offset);
366
+ lines.push(normalizeSearchLine(rawLine));
367
+ offset += rawLine.length + 1;
368
+ }
369
+ return { lines, starts };
370
+ }
371
+
372
+ function lineAllowed(lineNumber: number, ranges: readonly LineRange[] | undefined): boolean {
373
+ return !ranges || isLineInRanges(lineNumber, ranges);
374
+ }
375
+
376
+ /**
377
+ * Per-file native fetch budget that guarantees the JS range filter can still
378
+ * surface `perFileKeep` in-range hits. Matches arrive one entry per matched
379
+ * line in line order, so a bounded range's hits all sit within the first
380
+ * `endLine` entries, and an open-ended range starting at S is preceded by at
381
+ * most S-1 out-of-range entries — S-1+perFileKeep entries cover the kept
382
+ * window or exhaust the file. Clamped to the native file-size ceiling (a
383
+ * ≤4 MiB file cannot have more matched lines than bytes), which also keeps
384
+ * the scaled global budget inside the native layer's u32 bounds.
385
+ */
386
+ function lineRangeFetchCap(pathSpecs: readonly GrepPathSpec[], perFileKeep: number): number {
387
+ let cap = 0;
388
+ for (const spec of pathSpecs) {
389
+ if (!spec.ranges) continue;
390
+ for (const range of spec.ranges) {
391
+ cap = Math.max(cap, range.endLine ?? range.startLine - 1 + perFileKeep);
392
+ }
393
+ }
394
+ return Math.min(cap, NATIVE_GREP_MAX_FILE_BYTES);
395
+ }
396
+
397
+ /** Binary search for the index of the line containing byte `offset`. */
398
+ function findLineIndex(starts: readonly number[], offset: number): number {
399
+ if (starts.length === 0) return -1;
400
+ let low = 0;
401
+ let high = starts.length - 1;
402
+ while (low <= high) {
403
+ const mid = Math.floor((low + high) / 2);
404
+ if (starts[mid] <= offset) {
405
+ low = mid + 1;
406
+ } else {
407
+ high = mid - 1;
408
+ }
409
+ }
410
+ return Math.max(0, high);
411
+ }
412
+
413
+ /**
414
+ * JS-`RegExp` fallback returning matched line indexes for a virtual resource too
415
+ * large for native grep (>`NATIVE_GREP_MAX_FILE_BYTES`, which native grep silently
416
+ * skips). Mirrors the native probe's output (sorted, deduped indexes) so
417
+ * `buildVirtualMatches` rebuilds context/ranges identically; only the regex dialect
418
+ * differs for these oversized inputs (the pre-RE2-parity behavior).
419
+ */
420
+ function jsMatchedLineIndexes(
421
+ content: string,
422
+ lines: readonly string[],
423
+ pattern: string,
424
+ ignoreCase: boolean,
425
+ multiline: boolean,
426
+ ): number[] {
427
+ const flags = `${ignoreCase ? "i" : ""}${multiline ? "gm" : ""}`;
428
+ let regex: RegExp;
429
+ try {
430
+ regex = new RegExp(pattern, flags);
431
+ } catch (err) {
432
+ const message = err instanceof Error ? err.message : String(err);
433
+ throw new ToolError(`Invalid regex: ${message.replace(/^Invalid regular expression:\s*/i, "")}`);
434
+ }
435
+ if (!multiline) {
436
+ const out: number[] = [];
437
+ for (let i = 0; i < lines.length; i++) {
438
+ regex.lastIndex = 0;
439
+ if (regex.test(lines[i] ?? "")) out.push(i);
440
+ }
441
+ return out;
442
+ }
443
+ const { starts } = indexSearchLines(content);
444
+ const seen = new Set<number>();
445
+ const out: number[] = [];
446
+ let match = regex.exec(content);
447
+ while (match !== null) {
448
+ const lineIndex = findLineIndex(starts, match.index);
449
+ if (lineIndex >= 0 && !seen.has(lineIndex)) {
450
+ seen.add(lineIndex);
451
+ out.push(lineIndex);
452
+ }
453
+ if (match[0].length === 0) regex.lastIndex++;
454
+ match = regex.exec(content);
455
+ }
456
+ out.sort((a, b) => a - b);
457
+ return out;
458
+ }
459
+
460
+ /**
461
+ * Native-grep an oversized (>NATIVE_GREP_MAX_FILE_BYTES) line-mode virtual resource
462
+ * in line-boundary chunks (each <= the cap) so it keeps RE2 dialect parity instead of
463
+ * the JS fallback. Each chunk's matched line numbers are offset by its starting line
464
+ * index. A single line larger than the cap can't be native-grepped, so that one line
465
+ * is JS-tested. Returns sorted 0-based line indexes.
466
+ */
467
+ async function nativeChunkedLineIndexes(
468
+ dir: string,
469
+ resourceIdx: number,
470
+ content: string,
471
+ pattern: string,
472
+ ignoreCase: boolean,
473
+ signal: AbortSignal | undefined,
474
+ ): Promise<number[]> {
475
+ const rawLines = content.split("\n");
476
+ if (rawLines.length > 0 && rawLines[rawLines.length - 1] === "") rawLines.pop();
477
+ const indexes: number[] = [];
478
+ let chunkStart = 0;
479
+ let chunkBytes = 0;
480
+ let chunkLines: string[] = [];
481
+ let chunkSeq = 0;
482
+ const flush = async (): Promise<void> => {
483
+ if (chunkLines.length === 0) return;
484
+ const scratch = path.resolve(dir, `${resourceIdx}-chunk-${chunkSeq++}`);
485
+ await writeFile(scratch, chunkLines.join("\n"));
486
+ const probe = await grep(
487
+ {
488
+ pattern,
489
+ path: scratch,
490
+ ignoreCase,
491
+ multiline: false,
492
+ hidden: true,
493
+ gitignore: false,
494
+ maxCount: chunkLines.length,
495
+ contextBefore: 0,
496
+ contextAfter: 0,
497
+ maxColumns: DEFAULT_MAX_COLUMN,
498
+ mode: GrepOutputMode.Content,
499
+ signal,
500
+ timeoutMs: SEARCH_GREP_TIMEOUT_MS,
501
+ },
502
+ undefined,
503
+ );
504
+ for (const match of probe.matches) indexes.push(chunkStart + match.lineNumber - 1);
505
+ chunkLines = [];
506
+ chunkBytes = 0;
507
+ };
508
+ let lineRegex: RegExp | undefined;
509
+ for (let i = 0; i < rawLines.length; i++) {
510
+ const line = rawLines[i];
511
+ const lineBytes = Buffer.byteLength(line, "utf8") + 1;
512
+ if (lineBytes > NATIVE_GREP_MAX_FILE_BYTES) {
513
+ await flush();
514
+ if (!lineRegex) {
515
+ try {
516
+ lineRegex = new RegExp(pattern, ignoreCase ? "i" : "");
517
+ } catch (err) {
518
+ const message = err instanceof Error ? err.message : String(err);
519
+ throw new ToolError(`Invalid regex: ${message.replace(/^Invalid regular expression:\s*/i, "")}`);
520
+ }
521
+ }
522
+ lineRegex.lastIndex = 0;
523
+ if (lineRegex.test(line)) indexes.push(i);
524
+ chunkStart = i + 1;
525
+ continue;
526
+ }
527
+ if (chunkLines.length > 0 && chunkBytes + lineBytes > NATIVE_GREP_MAX_FILE_BYTES) {
528
+ await flush();
529
+ chunkStart = i;
530
+ }
531
+ if (chunkLines.length === 0) chunkStart = i;
532
+ chunkLines.push(line);
533
+ chunkBytes += lineBytes;
534
+ }
535
+ await flush();
536
+ indexes.sort((a, b) => a - b);
537
+ return indexes;
538
+ }
539
+
540
+ function makeContextLine(lines: readonly string[], lineIndex: number): { lineNumber: number; line: string } {
541
+ const { text } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
542
+ return { lineNumber: lineIndex + 1, line: text };
543
+ }
544
+
545
+ function makeVirtualMatch(
546
+ resource: VirtualSearchResource,
547
+ lines: readonly string[],
548
+ lineIndex: number,
549
+ contextBefore: number,
550
+ contextAfter: number,
551
+ lastEmittedLine: number,
552
+ nextMatchLine: number,
553
+ ): GrepMatch {
554
+ const lineNumber = lineIndex + 1;
555
+ const { text, wasTruncated } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
556
+ const match: GrepMatch = {
557
+ path: resource.path,
558
+ lineNumber,
559
+ line: text,
560
+ };
561
+ if (wasTruncated) match.truncated = true;
562
+
563
+ if (contextBefore > 0) {
564
+ const before: NonNullable<GrepMatch["contextBefore"]> = [];
565
+ // Start after the previous match's last emitted line so adjacent matches
566
+ // never repeat or rewind context lines (mirrors native grep's sink).
567
+ const start = Math.max(0, lineIndex - contextBefore, lastEmittedLine);
568
+ for (let idx = start; idx < lineIndex; idx++) {
569
+ const contextLineNumber = idx + 1;
570
+ if (lineAllowed(contextLineNumber, resource.ranges)) {
571
+ before.push(makeContextLine(lines, idx));
572
+ }
573
+ }
574
+ if (before.length > 0) match.contextBefore = before;
575
+ }
576
+
577
+ if (contextAfter > 0) {
578
+ const after: NonNullable<GrepMatch["contextAfter"]> = [];
579
+ // Stop before the next match line; it is emitted as a match itself.
580
+ const end = Math.min(lines.length - 1, lineIndex + contextAfter, nextMatchLine - 2);
581
+ for (let idx = lineIndex + 1; idx <= end; idx++) {
582
+ const contextLineNumber = idx + 1;
583
+ if (lineAllowed(contextLineNumber, resource.ranges)) {
584
+ after.push(makeContextLine(lines, idx));
585
+ }
586
+ }
587
+ if (after.length > 0) match.contextAfter = after;
588
+ }
589
+
590
+ return match;
591
+ }
592
+
593
+ /** Build matches for ascending matched line indexes with forward-only,
594
+ * deduplicated context windows (line numbers never repeat or go backwards
595
+ * within one resource). */
596
+ function buildVirtualMatches(
597
+ resource: VirtualSearchResource,
598
+ lines: readonly string[],
599
+ matchedIndexes: readonly number[],
600
+ contextBefore: number,
601
+ contextAfter: number,
602
+ maxCount: number,
603
+ ): GrepMatch[] {
604
+ const matches: GrepMatch[] = [];
605
+ let lastEmittedLine = 0;
606
+ for (let i = 0; i < matchedIndexes.length && matches.length < maxCount; i++) {
607
+ const lineIndex = matchedIndexes[i];
608
+ const nextMatchLine = i + 1 < matchedIndexes.length ? matchedIndexes[i + 1] + 1 : Number.POSITIVE_INFINITY;
609
+ const match = makeVirtualMatch(
610
+ resource,
611
+ lines,
612
+ lineIndex,
613
+ contextBefore,
614
+ contextAfter,
615
+ lastEmittedLine,
616
+ nextMatchLine,
617
+ );
618
+ const after = match.contextAfter;
619
+ lastEmittedLine = after && after.length > 0 ? after[after.length - 1].lineNumber : match.lineNumber;
620
+ matches.push(match);
621
+ }
622
+ return matches;
623
+ }
624
+
625
+ async function searchVirtualResources(
626
+ resources: readonly VirtualSearchResource[],
627
+ pattern: string,
628
+ ignoreCase: boolean,
629
+ multiline: boolean,
630
+ contextBefore: number,
631
+ contextAfter: number,
632
+ maxCount: number,
633
+ signal?: AbortSignal,
634
+ ): Promise<GrepResult> {
635
+ if (resources.length === 0) {
636
+ return { matches: [], totalMatches: 0, filesWithMatches: 0, filesSearched: 0, limitReached: false };
637
+ }
638
+ const matches: GrepMatch[] = [];
639
+ const filesWithMatches = new Set<string>();
640
+ let totalMatches = 0;
641
+ let limitReached = false;
642
+ // Detect matched line numbers with native grep (RE2) — the SAME matcher local
643
+ // search uses — so a pattern valid for local grep but not JS `RegExp` (`(?i)x`,
644
+ // `[[:digit:]]`) behaves identically on virtual/remote resources. The JS helpers
645
+ // below then rebuild the exact forward-only, range-trimmed context windows the
646
+ // virtual-search contract requires.
647
+ const dir = await mkdtemp(path.join(tmpdir(), "omp-search-virtual-"));
648
+ try {
649
+ for (let idx = 0; idx < resources.length; idx++) {
650
+ const resource = resources[idx];
651
+ const remaining = Math.max(maxCount - matches.length, 0);
652
+ if (remaining === 0) {
653
+ limitReached = true;
654
+ break;
655
+ }
656
+ const lines = multiline ? indexSearchLines(resource.content).lines : splitSearchLines(resource.content);
657
+ let matchedIndexes: number[];
658
+ if (Buffer.byteLength(resource.content, "utf8") > NATIVE_GREP_MAX_FILE_BYTES) {
659
+ // Native grep skips files above its 4 MiB cap. Search oversized content in
660
+ // line-boundary chunks so line-mode keeps RE2 parity; multiline can't be chunked
661
+ // without missing matches that span a chunk boundary, so it falls back to JS
662
+ // (dialect-as-JS only for these oversized multiline inputs).
663
+ matchedIndexes = (
664
+ multiline
665
+ ? jsMatchedLineIndexes(resource.content, lines, pattern, ignoreCase, true)
666
+ : await nativeChunkedLineIndexes(dir, idx, resource.content, pattern, ignoreCase, signal)
667
+ ).filter(lineIndex => lineAllowed(lineIndex + 1, resource.ranges));
668
+ } else {
669
+ const scratch = path.resolve(dir, `${idx}`);
670
+ await writeFile(scratch, resource.content);
671
+ const probe = await grep(
672
+ {
673
+ pattern,
674
+ path: scratch,
675
+ ignoreCase,
676
+ multiline,
677
+ hidden: true,
678
+ gitignore: false,
679
+ // A ranged selector must see every match so the range filter below never
680
+ // drops in-range hits that fall after the cap; matches can't exceed the
681
+ // line count. Unranged search keeps the overall result cap.
682
+ maxCount: resource.ranges ? Math.max(lines.length, 1) : INTERNAL_TOTAL_CAP,
683
+ contextBefore: 0,
684
+ contextAfter: 0,
685
+ maxColumns: DEFAULT_MAX_COLUMN,
686
+ mode: GrepOutputMode.Content,
687
+ signal,
688
+ timeoutMs: SEARCH_GREP_TIMEOUT_MS,
689
+ },
690
+ undefined,
691
+ );
692
+ matchedIndexes = [...new Set(probe.matches.map(match => match.lineNumber - 1))]
693
+ .filter(lineIndex => lineAllowed(lineIndex + 1, resource.ranges))
694
+ .sort((a, b) => a - b);
695
+ }
696
+ const resourceMatches = buildVirtualMatches(
697
+ resource,
698
+ lines,
699
+ matchedIndexes,
700
+ contextBefore,
701
+ contextAfter,
702
+ remaining,
703
+ );
704
+ if (matchedIndexes.length > 0) filesWithMatches.add(resource.path);
705
+ totalMatches += matchedIndexes.length;
706
+ limitReached = limitReached || matchedIndexes.length > resourceMatches.length;
707
+ matches.push(...resourceMatches);
708
+ }
709
+ } finally {
710
+ await rm(dir, { recursive: true, force: true }).catch(() => {});
711
+ }
712
+ return {
713
+ matches,
714
+ totalMatches,
715
+ filesWithMatches: filesWithMatches.size,
716
+ filesSearched: resources.length,
717
+ limitReached,
718
+ };
719
+ }
720
+
721
+ function mergeGrepResults(left: GrepResult, right: GrepResult, maxCount: number): GrepResult {
722
+ if (left.matches.length === 0) return right;
723
+ if (right.matches.length === 0) return left;
724
+ const combinedMatches = [...left.matches, ...right.matches];
725
+ const matches = combinedMatches.length > maxCount ? combinedMatches.slice(0, maxCount) : combinedMatches;
726
+ return {
727
+ matches,
728
+ totalMatches: left.totalMatches + right.totalMatches,
729
+ filesWithMatches: new Set(matches.map(match => match.path)).size,
730
+ filesSearched: left.filesSearched + right.filesSearched,
731
+ limitReached: left.limitReached || right.limitReached || matches.length < combinedMatches.length,
732
+ };
733
+ }
734
+
735
+ async function expandVirtualInternalResource(
736
+ rawPath: string,
737
+ resource: InternalResource,
738
+ internalRouter: InternalUrlRouter,
739
+ context: ResolveContext,
740
+ ranges: readonly LineRange[] | undefined,
741
+ ): Promise<VirtualSearchResource[]> {
742
+ if (OMP_ROOT_URL_RE.test(rawPath)) {
743
+ const completions = await internalRouter.complete("omp", "");
744
+ if (completions && completions.length > 0) {
745
+ const resources: VirtualSearchResource[] = [];
746
+ const seen = new Set<string>();
747
+ for (const completion of completions) {
748
+ if (seen.has(completion.value)) continue;
749
+ seen.add(completion.value);
750
+ const docUrl = `omp://${completion.value}`;
751
+ const doc = await internalRouter.resolve(docUrl, context);
752
+ if (!doc.sourcePath) {
753
+ resources.push({ path: docUrl, content: doc.content, ranges });
754
+ }
755
+ }
756
+ if (resources.length > 0) return resources;
757
+ }
758
+ }
759
+
760
+ return [{ path: rawPath, content: resource.content, ranges }];
761
+ }
762
+
763
+ async function resolveInternalSearchInputs(opts: {
764
+ pathSpecs: readonly GrepPathSpec[];
765
+ resolvedPaths: string[];
766
+ cwd: string;
767
+ settings: unknown;
768
+ signal?: AbortSignal;
769
+ archiveDisplayMap: ReadonlyMap<string, string>;
770
+ localProtocolOptions?: LocalProtocolOptions;
771
+ skills?: ResolveContext["skills"];
772
+ }): Promise<InternalSearchInputResolution> {
773
+ const internalRouter = InternalUrlRouter.instance();
774
+ const paths = opts.resolvedPaths.slice();
775
+ const virtualResources: VirtualSearchResource[] = [];
776
+ const virtualPathSet = new Set<string>();
777
+ const virtualInputIndexes = new Set<number>();
778
+ const immutableSourcePaths = new Set<string>();
779
+ let virtualScopePath: string | undefined;
780
+ const context: ResolveContext = {
781
+ cwd: opts.cwd,
782
+ settings: opts.settings,
783
+ signal: opts.signal,
784
+ localProtocolOptions: opts.localProtocolOptions,
785
+ skills: opts.skills,
786
+ skipDirectoryListing: true,
787
+ // Try path-only first so large artifacts (and any other handler that
788
+ // separates path from content) resolve without materializing bytes.
789
+ // Handlers that ignore the flag still return content, and virtual
790
+ // resources without a sourcePath fall through to a second resolve.
791
+ pathOnly: true,
792
+ };
793
+
794
+ for (let idx = 0; idx < paths.length; idx++) {
795
+ const rawPath = paths[idx];
796
+ if (!rawPath || opts.archiveDisplayMap.has(rawPath) || !internalRouter.canHandle(rawPath)) {
797
+ continue;
798
+ }
799
+ // `ssh://[::1]/path` carries `[`/`]` in the IPv6 authority — glob metacharacters
800
+ // — so check only the path portion for ssh:// (the SSH handler reads a single
801
+ // remote file; there is no glob expansion). A glob in the remote path still trips.
802
+ const globTarget = /^ssh:\/\//i.test(rawPath) ? rawPath.replace(/^ssh:\/\/[^/]*/i, "") : rawPath;
803
+ if (hasGlobPathChars(globTarget)) {
804
+ throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
805
+ }
806
+ let resource = await internalRouter.resolve(rawPath, context);
807
+ // A directory listing with no backing local path (e.g. a remote ssh:// dir)
808
+ // has no real contents to grep — searching its listing text would be
809
+ // misleading. Local/skill/vault dir resources set `sourcePath` and skip this.
810
+ if (resource.isDirectory && !resource.sourcePath) {
811
+ throw new ToolError(
812
+ `search cannot recurse the directory listing at ${rawPath}; search a specific file under it (e.g. ${rawPath.replace(/\/+$/, "")}/<file>) or read ${rawPath} to list its entries`,
813
+ );
814
+ }
815
+ if (resource.sourcePath) {
816
+ paths[idx] = resource.sourcePath;
817
+ if (resource.immutable) {
818
+ immutableSourcePaths.add(path.resolve(resource.sourcePath));
819
+ }
820
+ continue;
821
+ }
822
+
823
+ // No sourcePath: this handler needs its content materialized so the
824
+ // virtual expansion can search it. Re-resolve without pathOnly.
825
+ if (context.pathOnly) {
826
+ resource = await internalRouter.resolve(rawPath, { ...context, pathOnly: false });
827
+ }
828
+
829
+ const ranges = opts.pathSpecs[idx]?.ranges;
830
+ const expanded = await expandVirtualInternalResource(
831
+ rawPath,
832
+ resource,
833
+ internalRouter,
834
+ { ...context, pathOnly: false },
835
+ ranges,
836
+ );
837
+ virtualInputIndexes.add(idx);
838
+ for (const virtual of expanded) {
839
+ virtualResources.push(virtual);
840
+ virtualPathSet.add(virtual.path);
841
+ }
842
+ virtualScopePath = virtualScopePath ? `${virtualScopePath}, ${rawPath}` : rawPath;
843
+ }
844
+
845
+ return {
846
+ resolvedPathsByInput: paths,
847
+ paths: paths.filter((_, idx) => !virtualInputIndexes.has(idx)),
848
+ virtualResources,
849
+ virtualPathSet,
850
+ virtualInputIndexes,
851
+ immutableSourcePaths,
852
+ virtualScopePath,
853
+ };
854
+ }
855
+
856
+ export interface GrepToolDetails {
857
+ truncation?: TruncationResult;
858
+ fileLimitReached?: number;
859
+ perFileLimitReached?: number;
860
+ linesTruncated?: boolean;
861
+ meta?: OutputMeta;
862
+ scopePath?: string;
863
+ matchCount?: number;
864
+ fileCount?: number;
865
+ files?: string[];
866
+ fileMatches?: Array<{ path: string; count: number }>;
867
+ truncated?: boolean;
868
+ error?: string;
869
+ /** Pre-formatted text for the user-visible TUI render. Mirrors the model-facing
870
+ * `result.text` lines but uses a `│` gutter and `*` to mark match lines (vs space for
871
+ * context). The TUI uses this directly so it never parses model-facing hashline anchors. */
872
+ displayContent?: string;
873
+ /** Absolute base directory used during search. Used by the renderer to resolve
874
+ * display-relative paths to absolute paths for OSC 8 hyperlinks. */
875
+ searchPath?: string;
876
+ /** Session cwd at search time. The renderer resolves the display-relative
877
+ * (cwd-relative) header/match paths against this for OSC 8 hyperlinks;
878
+ * `searchPath` is the scope label target, not the display-path base. */
879
+ cwd?: string;
880
+ /** User-supplied paths whose base directory was missing on disk. The tool
881
+ * skipped these and continued with the surviving entries; surfaced as a
882
+ * non-fatal warning in the renderer and in the model-facing text. */
883
+ missingPaths?: string[];
884
+ }
885
+
886
+ type SearchParams = typeof searchSchema.infer;
887
+
888
+ /**
889
+ * Construction-time overrides for callers that are not the model.
890
+ *
891
+ * The model-facing schema deliberately does not grow these: they exist for
892
+ * wire bridges (the Cursor `pi_grep` frame) whose protocol carries an explicit
893
+ * context width and total match cap, and which would otherwise have to drop
894
+ * them. Unset means "use the session settings / built-in caps" — the behavior
895
+ * every model-issued call keeps.
896
+ */
897
+ export interface GrepToolOptions {
898
+ /** Overrides `grep.contextBefore`/`grep.contextAfter` for every call on this instance. */
899
+ context?: number;
900
+ /** Caps total surfaced matches. Applied on top of the built-in per-file and file-window caps, never above them. */
901
+ totalMatchLimit?: number;
902
+ }
903
+
904
+ export class GrepTool implements AgentTool<typeof searchSchema, GrepToolDetails> {
905
+ readonly name = "grep";
906
+ readonly approval = (args: unknown): ToolTier => {
907
+ const a = args as { path?: string | string[]; paths?: string | string[] };
908
+ return toPathList(a.path ?? a.paths).some(pathTargetsSsh) ? "exec" : "read";
909
+ };
910
+ readonly label = "Grep";
911
+ readonly loadMode = "discoverable";
912
+ readonly summary = "Grep file contents using ripgrep (fast regex search)";
913
+ get description(): string {
914
+ const displayMode = resolveFileDisplayMode(this.session);
915
+ return prompt.render(grepDescription, {
916
+ IS_HL_MODE: displayMode.hashLines,
917
+ IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
918
+ scoutAvailable: isScoutSpawnable(
919
+ this.session.settings.get("task.disabledAgents") as string[] | undefined,
920
+ this.session.getSessionSpawns?.() ?? "*",
921
+ ),
922
+ });
923
+ }
924
+ readonly parameters = searchSchema;
925
+ readonly strict = true;
926
+
927
+ readonly #contextOverride?: number;
928
+ readonly #totalMatchLimit?: number;
929
+
930
+ constructor(
931
+ private readonly session: ToolSession,
932
+ options?: GrepToolOptions,
933
+ ) {
934
+ const context = options?.context;
935
+ this.#contextOverride = context !== undefined ? Math.max(0, Math.floor(context)) : undefined;
936
+ const total = options?.totalMatchLimit;
937
+ this.#totalMatchLimit = total !== undefined ? Math.max(1, Math.floor(total)) : undefined;
938
+ }
939
+
940
+ async execute(
941
+ _toolCallId: string,
942
+ params: SearchParams,
943
+ signal?: AbortSignal,
944
+ _onUpdate?: AgentToolUpdateCallback<GrepToolDetails>,
945
+ _toolContext?: AgentToolContext,
946
+ ): Promise<AgentToolResult<GrepToolDetails>> {
947
+ const { pattern, path: rawPath, case: caseSensitive, gitignore, skip } = params;
948
+
949
+ return untilAborted(signal, async () => {
950
+ // Preserve the pattern verbatim — leading/trailing whitespace is
951
+ // meaningful in regexes (indentation anchors, trailing-space matches).
952
+ if (!pattern.trim()) {
953
+ throw new ToolError("Pattern must not be empty");
954
+ }
955
+ const normalizedPattern = pattern;
956
+
957
+ const normalizedSkip =
958
+ skip === undefined || skip === null ? 0 : Number.isFinite(skip) ? Math.floor(skip) : Number.NaN;
959
+ if (normalizedSkip < 0 || !Number.isFinite(normalizedSkip)) {
960
+ throw new ToolError("Skip must be a non-negative number");
961
+ }
962
+ const scopedPaths = toPathList(rawPath);
963
+ const effectivePaths = scopedPaths.length > 0 ? scopedPaths : ["."];
964
+ const rawEntries = await expandDelimitedPathEntries(effectivePaths, this.session.cwd);
965
+ const pathSpecs = await parsePathSpecs(rawEntries, this.session.cwd);
966
+ const materializedExternalPaths = new Map<string, string>();
967
+ const materializeExternalUrlForSearch = async (rawPath: string) => {
968
+ const target = parseReadUrlTarget(rawPath);
969
+ if (!target) return undefined;
970
+ const materialized = await materializeReadUrlToFile(
971
+ this.session,
972
+ { path: target.path, raw: target.raw },
973
+ signal,
974
+ );
975
+ materializedExternalPaths.set(rawPath, materialized.path);
976
+ return { sourcePath: materialized.path, immutable: true };
977
+ };
978
+ const {
979
+ resolvedPaths,
980
+ displayMap: archiveDisplayMap,
981
+ displaySet: archiveDisplaySet,
982
+ unreadable: archiveUnreadable,
983
+ cleanup: cleanupArchiveScratch,
984
+ } = await resolveArchiveSearchPaths(pathSpecs, this.session.cwd);
985
+ try {
986
+ const internalResolution = await resolveInternalSearchInputs({
987
+ pathSpecs,
988
+ resolvedPaths,
989
+ cwd: this.session.cwd,
990
+ settings: this.session.settings,
991
+ signal,
992
+ archiveDisplayMap,
993
+ localProtocolOptions: this.session.localProtocolOptions,
994
+ skills: this.session.skills,
995
+ });
996
+ const searchablePaths = internalResolution.paths;
997
+ const { virtualResources, virtualPathSet, virtualInputIndexes } = internalResolution;
998
+ const rangesByAbsPath = new Map<string, LineRange[]>();
999
+
1000
+ if (
1001
+ archiveUnreadable.length > 0 &&
1002
+ searchablePaths.length === archiveUnreadable.length &&
1003
+ virtualResources.length === 0
1004
+ ) {
1005
+ // All inputs were archive selectors we couldn't materialize; surface the
1006
+ // reason instead of a downstream "path not found" from the scope resolver.
1007
+ throw new ToolError(
1008
+ `Cannot search archive member(s): ${archiveUnreadable.join(", ")}. ` +
1009
+ `Read the member with \`read <archive>:<member>\` and inspect the returned text, ` +
1010
+ `or pass a UTF-8 text member.`,
1011
+ );
1012
+ }
1013
+ const normalizedContextBefore = this.#contextOverride ?? this.session.settings.get("grep.contextBefore");
1014
+ const normalizedContextAfter = this.#contextOverride ?? this.session.settings.get("grep.contextAfter");
1015
+ const ignoreCase = !(caseSensitive ?? true);
1016
+ const useGitignore = gitignore ?? true;
1017
+ const patternHasNewline = normalizedPattern.includes("\n") || normalizedPattern.includes("\\n");
1018
+ const effectiveMultiline = patternHasNewline;
1019
+
1020
+ let searchPath: string;
1021
+ let scopePath: string;
1022
+ let globFilter: string | undefined;
1023
+ let isDirectory: boolean;
1024
+ let multiTargets: ResolvedSearchTarget[] | undefined;
1025
+ let exactFilePaths: string[] | undefined;
1026
+ let missingPaths: string[];
1027
+ const immutableSourcePaths = new Set(internalResolution.immutableSourcePaths);
1028
+ if (searchablePaths.length > 0) {
1029
+ const scope = await resolveToolSearchScope({
1030
+ rawPaths: searchablePaths,
1031
+ cwd: this.session.cwd,
1032
+ internalUrlAction: "search",
1033
+ trackImmutableSources: true,
1034
+ surfaceExactFilePaths: true,
1035
+ fanOutFileTargets: true,
1036
+ multipathStatHint: " (`path` list entries must each exist relative to cwd)",
1037
+ settings: this.session.settings,
1038
+ signal,
1039
+ localProtocolOptions: this.session.localProtocolOptions,
1040
+ skills: this.session.skills,
1041
+ resolveExternalUrl: materializeExternalUrlForSearch,
1042
+ });
1043
+ searchPath = scope.searchPath;
1044
+ isDirectory = scope.isDirectory;
1045
+ multiTargets = scope.multiTargets;
1046
+ exactFilePaths = scope.exactFilePaths;
1047
+ missingPaths = scope.missingPaths;
1048
+ globFilter = scope.globFilter;
1049
+ for (const immutablePath of scope.immutableSourcePaths) {
1050
+ immutableSourcePaths.add(immutablePath);
1051
+ }
1052
+ // Build the per-file line-range filter after URL materialization has run:
1053
+ // archive entries are keyed by scratch path, URL entries by read-cache
1054
+ // content path, and ordinary files by their resolved filesystem path.
1055
+ for (let idx = 0; idx < pathSpecs.length; idx++) {
1056
+ const spec = pathSpecs[idx];
1057
+ if (!spec.ranges) continue;
1058
+ if (virtualInputIndexes.has(idx)) continue;
1059
+ const resolved = internalResolution.resolvedPathsByInput[idx];
1060
+ if (!resolved) continue;
1061
+ const materializedExternalPath = materializedExternalPaths.get(spec.clean);
1062
+ if (materializedExternalPath) {
1063
+ mergeRangesInto(rangesByAbsPath, path.resolve(materializedExternalPath), spec.ranges);
1064
+ continue;
1065
+ }
1066
+ if (resolved === spec.clean && !archiveDisplayMap.has(resolved)) {
1067
+ // Non-archive entry; ensure the cleaned path resolves to a regular file.
1068
+ const absKey = path.resolve(resolveReadPath(resolved, this.session.cwd));
1069
+ const stats = await stat(absKey).catch(() => null);
1070
+ if (!stats) {
1071
+ throw new ToolError(`Path not found for line-range selector: ${spec.original}`);
1072
+ }
1073
+ if (!stats.isFile()) {
1074
+ throw new ToolError(
1075
+ `Line-range selector requires a single file: ${spec.original} is a directory`,
1076
+ );
1077
+ }
1078
+ mergeRangesInto(rangesByAbsPath, absKey, spec.ranges);
1079
+ } else {
1080
+ mergeRangesInto(rangesByAbsPath, path.resolve(resolved), spec.ranges);
1081
+ }
1082
+ }
1083
+ // When the only input was an archive selector, surface that selector instead
1084
+ // of the temp scratch path the resolver substituted in.
1085
+ const physicalScopePath =
1086
+ searchablePaths.length === 1 && archiveDisplayMap.get(searchPath)
1087
+ ? (archiveDisplayMap.get(searchPath) as string)
1088
+ : scope.scopePath;
1089
+ scopePath = internalResolution.virtualScopePath
1090
+ ? `${physicalScopePath}, ${internalResolution.virtualScopePath}`
1091
+ : physicalScopePath;
1092
+ } else {
1093
+ searchPath = this.session.cwd;
1094
+ scopePath = internalResolution.virtualScopePath ?? ".";
1095
+ globFilter = undefined;
1096
+ isDirectory = false;
1097
+ multiTargets = undefined;
1098
+ exactFilePaths = undefined;
1099
+ missingPaths = [];
1100
+ }
1101
+ if (
1102
+ missingPaths.length > 0 &&
1103
+ missingPaths.length === searchablePaths.length &&
1104
+ virtualResources.length === 0
1105
+ ) {
1106
+ const archiveHint =
1107
+ archiveUnreadable.length > 0
1108
+ ? ` (archive members were not searchable: ${archiveUnreadable.join(", ")})`
1109
+ : "";
1110
+ throw new ToolError(
1111
+ `Path not found: ${missingPaths.join(", ")}; list each target in the semicolon-delimited \`path\`${archiveHint}`,
1112
+ );
1113
+ }
1114
+ const baseDisplayMode = resolveFileDisplayMode(this.session);
1115
+
1116
+ const effectiveOutputMode = GrepOutputMode.Content;
1117
+ const isMultiScope =
1118
+ isDirectory ||
1119
+ Boolean(exactFilePaths) ||
1120
+ Boolean(multiTargets) ||
1121
+ (virtualResources.length > 0 && (virtualResources.length > 1 || searchablePaths.length > 0));
1122
+ const perFileMatchCap = isMultiScope ? MULTI_FILE_PER_FILE_MATCHES : SINGLE_FILE_MATCHES;
1123
+ // Range filtering happens in JS after the native fetch, so out-of-range
1124
+ // matches consume fetch budget. Widen the per-file budget just enough
1125
+ // that filtering can still yield `perFileMatchCap` in-range hits, and
1126
+ // scale the global safety ceiling by the same amplification so ranged
1127
+ // searches keep the baseline file coverage while staying finite.
1128
+ const hasLineRangeFilters = pathSpecs.some(spec => spec.ranges);
1129
+ const nativeMaxCountPerFile = hasLineRangeFilters
1130
+ ? Math.max(perFileMatchCap + 1, lineRangeFetchCap(pathSpecs, perFileMatchCap + 1))
1131
+ : perFileMatchCap + 1;
1132
+ const nativeMaxCount = hasLineRangeFilters
1133
+ ? Math.ceil(INTERNAL_TOTAL_CAP / (perFileMatchCap + 1)) * nativeMaxCountPerFile
1134
+ : INTERNAL_TOTAL_CAP;
1135
+
1136
+ // Run grep
1137
+ let result: GrepResult = {
1138
+ matches: [],
1139
+ totalMatches: 0,
1140
+ filesWithMatches: 0,
1141
+ filesSearched: 0,
1142
+ limitReached: false,
1143
+ };
1144
+ let skippedOversizedCount = 0;
1145
+ try {
1146
+ if (searchablePaths.length > 0) {
1147
+ if (exactFilePaths || multiTargets) {
1148
+ const matches: GrepMatch[] = [];
1149
+ const seenMatchKeys = new Set<string>();
1150
+ let limitReached = false;
1151
+ let totalMatches = 0;
1152
+ let filesSearched = 0;
1153
+ const targets = exactFilePaths
1154
+ ? exactFilePaths.map(filePath => ({
1155
+ basePath: filePath,
1156
+ glob: undefined as string | undefined,
1157
+ }))
1158
+ : (multiTargets ?? []);
1159
+ for (const target of targets) {
1160
+ const targetResult = await grep(
1161
+ {
1162
+ pattern: normalizedPattern,
1163
+ path: target.basePath,
1164
+ glob: target.glob,
1165
+ ignoreCase,
1166
+ multiline: effectiveMultiline,
1167
+ hidden: true,
1168
+ gitignore: useGitignore,
1169
+ maxCount: nativeMaxCount,
1170
+ contextBefore: normalizedContextBefore,
1171
+ contextAfter: normalizedContextAfter,
1172
+ maxColumns: DEFAULT_MAX_COLUMN,
1173
+ mode: effectiveOutputMode,
1174
+ maxCountPerFile: nativeMaxCountPerFile,
1175
+ signal,
1176
+ timeoutMs: SEARCH_GREP_TIMEOUT_MS,
1177
+ },
1178
+ undefined,
1179
+ );
1180
+ skippedOversizedCount += targetResult.skippedOversized ?? 0;
1181
+ limitReached = limitReached || Boolean(targetResult.limitReached);
1182
+ totalMatches += targetResult.totalMatches;
1183
+ filesSearched += targetResult.filesSearched;
1184
+ for (const match of targetResult.matches) {
1185
+ const absolute = path.resolve(target.basePath, match.path);
1186
+ // Overlapping targets (a directory plus a file nested
1187
+ // inside it) surface the same physical line twice;
1188
+ // keep the first occurrence.
1189
+ const matchKey = `${absolute}\0${match.lineNumber}`;
1190
+ if (seenMatchKeys.has(matchKey)) {
1191
+ totalMatches = Math.max(0, totalMatches - 1);
1192
+ continue;
1193
+ }
1194
+ seenMatchKeys.add(matchKey);
1195
+ const rebased = path.relative(searchPath, absolute).replace(/\\/g, "/");
1196
+ matches.push({ ...match, path: rebased });
1197
+ }
1198
+ }
1199
+ result = {
1200
+ matches,
1201
+ totalMatches: exactFilePaths ? matches.length : totalMatches,
1202
+ filesWithMatches: new Set(matches.map(match => match.path)).size,
1203
+ filesSearched: exactFilePaths ? exactFilePaths.length : filesSearched,
1204
+ limitReached,
1205
+ };
1206
+ } else {
1207
+ result = await grep(
1208
+ {
1209
+ pattern: normalizedPattern,
1210
+ path: searchPath,
1211
+ glob: globFilter,
1212
+ ignoreCase,
1213
+ multiline: effectiveMultiline,
1214
+ hidden: true,
1215
+ gitignore: useGitignore,
1216
+ maxCount: nativeMaxCount,
1217
+ contextBefore: normalizedContextBefore,
1218
+ contextAfter: normalizedContextAfter,
1219
+ maxColumns: DEFAULT_MAX_COLUMN,
1220
+ mode: effectiveOutputMode,
1221
+ maxCountPerFile: nativeMaxCountPerFile,
1222
+ signal,
1223
+ timeoutMs: SEARCH_GREP_TIMEOUT_MS,
1224
+ },
1225
+ undefined,
1226
+ );
1227
+ skippedOversizedCount = result.skippedOversized ?? 0;
1228
+ }
1229
+ }
1230
+ } catch (err) {
1231
+ if (err instanceof Error && /^regex(?: parse)? error/i.test(err.message)) {
1232
+ throw new ToolError(err.message.replace(/^regex(?: parse)? error:?\s*/i, "Invalid regex: "));
1233
+ }
1234
+ if (err instanceof Error && err.message.includes("Aborted: Timeout")) {
1235
+ throw new ToolError(
1236
+ `Grep timed out after ${SEARCH_GREP_TIMEOUT_MS / 1000}s; narrow paths or pattern, or scope with \`glob\` first`,
1237
+ );
1238
+ }
1239
+ throw err;
1240
+ }
1241
+ let virtualResult: GrepResult;
1242
+ try {
1243
+ virtualResult = await searchVirtualResources(
1244
+ virtualResources,
1245
+ normalizedPattern,
1246
+ ignoreCase,
1247
+ effectiveMultiline,
1248
+ normalizedContextBefore,
1249
+ normalizedContextAfter,
1250
+ INTERNAL_TOTAL_CAP,
1251
+ signal,
1252
+ );
1253
+ } catch (err) {
1254
+ if (err instanceof Error && /^regex(?: parse)? error/i.test(err.message)) {
1255
+ throw new ToolError(err.message.replace(/^regex(?: parse)? error:?\s*/i, "Invalid regex: "));
1256
+ }
1257
+ if (err instanceof SyntaxError) {
1258
+ throw new ToolError(`Invalid regex: ${err.message}`);
1259
+ }
1260
+ throw err;
1261
+ }
1262
+ result = mergeGrepResults(result, virtualResult, nativeMaxCount);
1263
+ if (rangesByAbsPath.size > 0) {
1264
+ const filteredMatches: GrepMatch[] = [];
1265
+ for (const match of result.matches) {
1266
+ const abs = matchAbsolutePath(match.path, searchPath);
1267
+ const ranges = rangesByAbsPath.get(abs);
1268
+ if (!ranges) {
1269
+ // Path has no line-range constraint (e.g. a peer entry without `:N-M`).
1270
+ filteredMatches.push(match);
1271
+ continue;
1272
+ }
1273
+ if (!isLineInRanges(match.lineNumber, ranges)) continue;
1274
+ // Drop context lines that fall outside the allowed ranges; they would
1275
+ // otherwise leak content the caller explicitly excluded.
1276
+ const trimBefore = match.contextBefore?.filter(c => isLineInRanges(c.lineNumber, ranges));
1277
+ const trimAfter = match.contextAfter?.filter(c => isLineInRanges(c.lineNumber, ranges));
1278
+ filteredMatches.push({
1279
+ ...match,
1280
+ contextBefore: trimBefore && trimBefore.length > 0 ? trimBefore : undefined,
1281
+ contextAfter: trimAfter && trimAfter.length > 0 ? trimAfter : undefined,
1282
+ });
1283
+ }
1284
+ result = {
1285
+ matches: filteredMatches,
1286
+ totalMatches: filteredMatches.length,
1287
+ filesWithMatches: new Set(filteredMatches.map(match => match.path)).size,
1288
+ filesSearched: result.filesSearched,
1289
+ limitReached: result.limitReached,
1290
+ };
1291
+ }
1292
+ if (archiveDisplayMap.size > 0) {
1293
+ for (const match of result.matches) {
1294
+ const abs = matchAbsolutePath(match.path, searchPath);
1295
+ const display = archiveDisplayMap.get(abs);
1296
+ if (display) match.path = display;
1297
+ }
1298
+ }
1299
+
1300
+ const formatPath = (filePath: string): string =>
1301
+ archiveDisplaySet.has(filePath) || virtualPathSet.has(filePath)
1302
+ ? filePath
1303
+ : formatResultPath(filePath, isDirectory, searchPath, this.session.cwd);
1304
+
1305
+ // Group matches by file in encounter order. Detect per-file overflow
1306
+ // BEFORE truncation so the renderer can surface that a hot file was
1307
+ // trimmed for diversity.
1308
+ const fileOrder: string[] = [];
1309
+ const matchesByPath = new Map<string, GrepMatch[]>();
1310
+ for (const match of result.matches) {
1311
+ if (!matchesByPath.has(match.path)) {
1312
+ fileOrder.push(match.path);
1313
+ matchesByPath.set(match.path, []);
1314
+ }
1315
+ matchesByPath.get(match.path)!.push(match);
1316
+ }
1317
+ let perFileLimitReached = false;
1318
+ for (const file of fileOrder) {
1319
+ const list = matchesByPath.get(file)!;
1320
+ if (list.length > perFileMatchCap) {
1321
+ perFileLimitReached = true;
1322
+ list.length = perFileMatchCap;
1323
+ }
1324
+ }
1325
+ const totalFiles = fileOrder.length;
1326
+ // When native grep stopped at its internal cap, files past the cap were
1327
+ // never surfaced — the file total is only a lower bound.
1328
+ const totalFilesLabel = result.limitReached ? `${totalFiles}+` : `${totalFiles}`;
1329
+ // Single-file scopes can't paginate — there is one file by definition.
1330
+ const canPaginate = isMultiScope;
1331
+ const skipFiles = canPaginate ? Math.min(normalizedSkip, totalFiles) : 0;
1332
+ // A caller with a total match cap is not paginating: the cap bounds the
1333
+ // output, and the only consumer that sets one (`pi_grep`) has no `skip`
1334
+ // field to follow a "use skip=N" suggestion with. Windowing it to the
1335
+ // first 20 files would silently return fewer matches than it asked for
1336
+ // while reporting the cap as unreached.
1337
+ //
1338
+ // The window is cap+1 files, not cap: with one match per file, a cap
1339
+ // of N over exactly N files is complete, while over N+1 files it is
1340
+ // clipped — and only reading that extra file distinguishes the two.
1341
+ // The cap below then does the trimming and records that it bit, so
1342
+ // `match_limit_reached` reaches the frame set.
1343
+ const fileWindow = this.#totalMatchLimit !== undefined ? this.#totalMatchLimit + 1 : DEFAULT_FILE_LIMIT;
1344
+ const windowFiles = canPaginate ? fileOrder.slice(skipFiles, skipFiles + fileWindow) : fileOrder;
1345
+ const fileLimitReached = canPaginate && totalFiles > skipFiles + fileWindow;
1346
+ const selectedMatches: GrepMatch[] = [];
1347
+ let totalMatchLimitReached = false;
1348
+ if (windowFiles.length > 0) {
1349
+ const lists = windowFiles.map(file => matchesByPath.get(file) ?? []);
1350
+ const cursors = new Array<number>(lists.length).fill(0);
1351
+ let anyAdded = true;
1352
+ while (anyAdded) {
1353
+ anyAdded = false;
1354
+ for (let i = 0; i < lists.length; i++) {
1355
+ if (cursors[i] < lists[i].length) {
1356
+ selectedMatches.push(lists[i][cursors[i]++]);
1357
+ anyAdded = true;
1358
+ }
1359
+ }
1360
+ }
1361
+ // Round-robin above interleaves files for diversity, so the cap is
1362
+ // applied after selection rather than as a per-list bound: trimming
1363
+ // mid-rotation would silently favour whichever files sort first.
1364
+ const cap = this.#totalMatchLimit;
1365
+ if (cap !== undefined && selectedMatches.length > cap) {
1366
+ selectedMatches.length = cap;
1367
+ totalMatchLimitReached = true;
1368
+ }
1369
+ }
1370
+ const nextSkip = skipFiles + windowFiles.length;
1371
+ const limitMessage = fileLimitReached
1372
+ ? `Showing files ${skipFiles + 1}-${nextSkip} of ${totalFilesLabel}. Use skip=${nextSkip} for the next page, or narrow paths/pattern.`
1373
+ : "";
1374
+ const { record: recordFile, list: fileList } = createFileRecorder();
1375
+ const fileMatchCounts = new Map<string, number>();
1376
+ // Detect explicit file targets that exceed the native grep size cap.
1377
+ // Native searches only their first NATIVE_GREP_MAX_FILE_BYTES; without
1378
+ // this note the caller might miss that matches beyond the window
1379
+ // (or "no matches") reflect partial coverage, not the whole file.
1380
+ const oversizedNote = await (async (): Promise<string | undefined> => {
1381
+ const explicitFileTargets: string[] = [];
1382
+ if (exactFilePaths) {
1383
+ explicitFileTargets.push(...exactFilePaths);
1384
+ } else if (searchablePaths.length > 0 && !isDirectory && !multiTargets) {
1385
+ explicitFileTargets.push(searchPath);
1386
+ }
1387
+ if (explicitFileTargets.length === 0) return undefined;
1388
+ const oversized: string[] = [];
1389
+ await Promise.all(
1390
+ explicitFileTargets.map(async target => {
1391
+ try {
1392
+ const st = await stat(target);
1393
+ if (st.isFile() && st.size > NATIVE_GREP_MAX_FILE_BYTES) {
1394
+ oversized.push(path.relative(this.session.cwd, target) || target);
1395
+ }
1396
+ } catch {
1397
+ // Stat failures here are surfaced by other code paths.
1398
+ }
1399
+ }),
1400
+ );
1401
+ if (oversized.length === 0) return undefined;
1402
+ const limitMb = Math.floor(NATIVE_GREP_MAX_FILE_BYTES / (1024 * 1024));
1403
+ return `Searched only the first ${limitMb}MB of large files (matches past the ${limitMb}MB window are not shown; use \`read\` for the rest): ${oversized.join(", ")}`;
1404
+ })();
1405
+ // Directory/multi-target scopes: native counts files it could not map
1406
+ // even a prefix of (rare mmap failures), but cannot name them.
1407
+ const oversizedScanNote =
1408
+ !oversizedNote && skippedOversizedCount > 0
1409
+ ? `Skipped ${skippedOversizedCount} unreadable large file(s); target them directly with \`read\``
1410
+ : undefined;
1411
+ const archiveNote =
1412
+ archiveUnreadable.length > 0
1413
+ ? `Skipped archive entries (search supports text members only): ${archiveUnreadable.join(", ")}`
1414
+ : undefined;
1415
+ // Suppress entries we already explained via archiveNote — they would otherwise
1416
+ // double up (the unreadable selector also failed the scope's existence check).
1417
+ const archiveUnreadablePaths = new Set(archiveUnreadable.map(s => s.replace(/ \(.*\)$/, "")));
1418
+ const missingPathsForNote = missingPaths.filter(p => !archiveUnreadablePaths.has(p));
1419
+ const missingPathsNote =
1420
+ missingPathsForNote.length > 0 ? `Skipped missing paths: ${missingPathsForNote.join(", ")}` : undefined;
1421
+ const warningNote =
1422
+ [missingPathsNote, archiveNote, oversizedNote, oversizedScanNote]
1423
+ .filter((s): s is string => Boolean(s))
1424
+ .join("\n") || undefined;
1425
+ if (selectedMatches.length === 0) {
1426
+ const details: GrepToolDetails = {
1427
+ scopePath,
1428
+ searchPath,
1429
+ cwd: this.session.cwd,
1430
+ matchCount: 0,
1431
+ fileCount: 0,
1432
+ files: [],
1433
+ truncated: false,
1434
+ missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
1435
+ };
1436
+ const skipPastEnd = canPaginate && normalizedSkip > 0 && totalFiles > 0 && skipFiles >= totalFiles;
1437
+ const noMatchText = skipPastEnd
1438
+ ? `No more results (${totalFilesLabel} files total; skip=${normalizedSkip} is past the end)`
1439
+ : "No matches found";
1440
+ const text = warningNote ? `${noMatchText}\n${warningNote}` : noMatchText;
1441
+ // Zero matches is useless regardless of warnings: by the time
1442
+ // compaction runs, the follow-up call has already corrected course.
1443
+ return toolResult(details).text(text).useless().done();
1444
+ }
1445
+ const outputLines: string[] = [];
1446
+ let linesTruncated = false;
1447
+ const matchesByFile = new Map<string, GrepMatch[]>();
1448
+ for (const match of selectedMatches) {
1449
+ const relativePath = formatPath(match.path);
1450
+ recordFile(relativePath);
1451
+ if (!matchesByFile.has(relativePath)) {
1452
+ matchesByFile.set(relativePath, []);
1453
+ }
1454
+ matchesByFile.get(relativePath)!.push(match);
1455
+ }
1456
+ const displayLines: string[] = [];
1457
+ const hashContexts = new Map<string, { tag: string }>();
1458
+ if (baseDisplayMode.hashLines) {
1459
+ for (const relativePath of fileList) {
1460
+ if (archiveDisplaySet.has(relativePath) || virtualPathSet.has(relativePath)) continue;
1461
+ const absoluteFilePath = path.resolve(this.session.cwd, relativePath);
1462
+ if (isImmutableSourcePath(absoluteFilePath, immutableSourcePaths)) continue;
1463
+ // Mint a whole-file content tag so any anchor validates while the
1464
+ // file is unchanged; over-cap / unreadable files get no tag (and
1465
+ // therefore plain, non-editable line output).
1466
+ const tag = await recordFileSnapshot(this.session, absoluteFilePath);
1467
+ if (tag) hashContexts.set(relativePath, { tag });
1468
+ }
1469
+ }
1470
+ const renderMatchesForFile = (relativePath: string): { model: string[]; display: string[] } => {
1471
+ const modelOut: string[] = [];
1472
+ const displayOut: string[] = [];
1473
+ const fileMatches = matchesByFile.get(relativePath) ?? [];
1474
+ const hashContext = hashContexts.get(relativePath);
1475
+ const useHashLines = hashContext !== undefined;
1476
+ const lineNumberWidth = fileMatches.reduce((width, match) => {
1477
+ let nextWidth = Math.max(width, String(match.lineNumber).length);
1478
+ for (const ctx of match.contextBefore ?? []) {
1479
+ nextWidth = Math.max(nextWidth, String(ctx.lineNumber).length);
1480
+ }
1481
+ for (const ctx of match.contextAfter ?? []) {
1482
+ nextWidth = Math.max(nextWidth, String(ctx.lineNumber).length);
1483
+ }
1484
+ return nextWidth;
1485
+ }, 0);
1486
+ let lastEmittedLine: number | undefined;
1487
+ const gutterPad = " ".repeat(lineNumberWidth + 1);
1488
+ for (const match of fileMatches) {
1489
+ const pushLine = (lineNumber: number, line: string, isMatch: boolean) => {
1490
+ if (lastEmittedLine !== undefined && lineNumber > lastEmittedLine + 1) {
1491
+ modelOut.push("...");
1492
+ displayOut.push(`${gutterPad}│...`);
1493
+ }
1494
+ modelOut.push(formatMatchLine(lineNumber, line, isMatch, { useHashLines }));
1495
+ displayOut.push(formatCodeFrameLine(isMatch ? "*" : " ", lineNumber, line, lineNumberWidth));
1496
+ lastEmittedLine = lineNumber;
1497
+ };
1498
+ if (match.contextBefore) {
1499
+ for (const ctx of match.contextBefore) {
1500
+ pushLine(ctx.lineNumber, ctx.line, false);
1501
+ }
1502
+ }
1503
+ pushLine(match.lineNumber, match.line, true);
1504
+ if (match.truncated) linesTruncated = true;
1505
+ if (match.contextAfter) {
1506
+ for (const ctx of match.contextAfter) {
1507
+ pushLine(ctx.lineNumber, ctx.line, false);
1508
+ }
1509
+ }
1510
+ fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
1511
+ }
1512
+ if (hashContext?.tag) {
1513
+ const absoluteFilePath = path.resolve(this.session.cwd, relativePath);
1514
+ recordSeenLinesFromBody(this.session, absoluteFilePath, hashContext.tag, modelOut.join("\n"));
1515
+ }
1516
+ return { model: modelOut, display: displayOut };
1517
+ };
1518
+ const useGroupedOutput = isDirectory || isMultiScope;
1519
+ if (useGroupedOutput) {
1520
+ const grouped = formatGroupedFiles(fileList, relativePath => {
1521
+ const rendered = renderMatchesForFile(relativePath);
1522
+ const hashContext = hashContexts.get(relativePath);
1523
+ return {
1524
+ modelLines: rendered.model,
1525
+ displayLines: rendered.display,
1526
+ headerSuffix: hashContext?.tag ? `#${hashContext.tag}` : "",
1527
+ skip: rendered.model.length === 0,
1528
+ };
1529
+ });
1530
+ outputLines.push(...grouped.model);
1531
+ displayLines.push(...grouped.display);
1532
+ } else {
1533
+ for (const relativePath of fileList) {
1534
+ const rendered = renderMatchesForFile(relativePath);
1535
+ if (rendered.model.length === 0) continue;
1536
+ if (outputLines.length > 0) {
1537
+ outputLines.push("");
1538
+ displayLines.push("");
1539
+ }
1540
+ const hashContext = hashContexts.get(relativePath);
1541
+ if (hashContext?.tag) {
1542
+ outputLines.push(formatHashlineHeader(relativePath, hashContext.tag));
1543
+ }
1544
+ outputLines.push(...rendered.model);
1545
+ displayLines.push(...rendered.display);
1546
+ }
1547
+ }
1548
+ if (limitMessage) {
1549
+ outputLines.push("", limitMessage);
1550
+ }
1551
+ if (warningNote) {
1552
+ outputLines.push("", warningNote);
1553
+ }
1554
+ const rawOutput = outputLines.join("\n");
1555
+ const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
1556
+ const output = truncation.content;
1557
+ const displayText = displayLines.join("\n");
1558
+ const truncated = Boolean(
1559
+ fileLimitReached ||
1560
+ perFileLimitReached ||
1561
+ totalMatchLimitReached ||
1562
+ result.limitReached ||
1563
+ truncation.truncated ||
1564
+ linesTruncated,
1565
+ );
1566
+ const details: GrepToolDetails = {
1567
+ scopePath,
1568
+ searchPath,
1569
+ cwd: this.session.cwd,
1570
+ matchCount: selectedMatches.length,
1571
+ fileCount: fileList.length,
1572
+ files: fileList,
1573
+ fileMatches: fileList.map(path => ({
1574
+ path,
1575
+ count: fileMatchCounts.get(path) ?? 0,
1576
+ })),
1577
+ truncated,
1578
+ fileLimitReached: fileLimitReached ? fileWindow : undefined,
1579
+ perFileLimitReached: totalMatchLimitReached
1580
+ ? this.#totalMatchLimit
1581
+ : perFileLimitReached
1582
+ ? perFileMatchCap
1583
+ : undefined,
1584
+ displayContent: displayText,
1585
+ missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
1586
+ };
1587
+ if (truncation.truncated) details.truncation = truncation;
1588
+ if (linesTruncated) details.linesTruncated = true;
1589
+ const resultBuilder = toolResult(details)
1590
+ .text(output)
1591
+ .limits({ columnMax: linesTruncated ? DEFAULT_MAX_COLUMN : undefined });
1592
+ if (truncation.truncated) {
1593
+ resultBuilder.truncation(truncation, { direction: "head" });
1594
+ }
1595
+ return resultBuilder.done();
1596
+ } finally {
1597
+ await cleanupArchiveScratch();
1598
+ }
1599
+ });
1600
+ }
1601
+ }
1602
+
1603
+ // =============================================================================
1604
+ // TUI Renderer
1605
+ // =============================================================================
1606
+
1607
+ interface GrepRenderArgs {
1608
+ pattern: string;
1609
+ path?: string | string[];
1610
+ /** Legacy pre-`path` argument name; kept so historical transcripts still render a scope. */
1611
+ paths?: string | string[];
1612
+ case?: boolean;
1613
+ gitignore?: boolean;
1614
+ skip?: number;
1615
+ }
1616
+
1617
+ const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
1618
+ /** Line budget for the expanded view. Larger than collapsed so expanding
1619
+ * reveals more matches with context, but still bounded so a single hot file
1620
+ * whose matches span the whole file can't dump its entire length. */
1621
+ const EXPANDED_TEXT_LIMIT = PREVIEW_LIMITS.EXPANDED_LINES * 2;
1622
+
1623
+ const SEARCH_CODE_FRAME_LINE_RE = /^\s*\*?(\d+)│/;
1624
+
1625
+ function searchScopeMeta(details: GrepToolDetails | undefined): string | undefined {
1626
+ if (!details?.scopePath) return undefined;
1627
+ const label = details.searchPath ? fileHyperlink(details.searchPath, details.scopePath) : details.scopePath;
1628
+ return `in ${label}`;
1629
+ }
1630
+
1631
+ function linkUrlLikeSearchHeader(raw: string, styled: string): { line: string; absPath?: string } {
1632
+ const resolvedPath = tryResolveInternalUrlSync(raw);
1633
+ if (resolvedPath) return { line: fileHyperlink(resolvedPath, styled), absPath: resolvedPath };
1634
+ return { line: uriHyperlink(raw, styled) };
1635
+ }
1636
+
1637
+ function parseSearchDisplayLineNumber(line: string): number | undefined {
1638
+ const match = SEARCH_CODE_FRAME_LINE_RE.exec(line);
1639
+ if (!match) return undefined;
1640
+ return Number.parseInt(match[1]!, 10);
1641
+ }
1642
+
1643
+ const SEARCH_MATCH_LINE_RE = /^\s*\*\d+(?:│|[:|])/;
1644
+
1645
+ interface RenderedSearchLine {
1646
+ raw: string;
1647
+ styled: string;
1648
+ }
1649
+
1650
+ function isSearchMatchLine(line: string): boolean {
1651
+ return SEARCH_MATCH_LINE_RE.test(line);
1652
+ }
1653
+
1654
+ function isSearchHeaderLine(line: string): boolean {
1655
+ return /^#+ /.test(line);
1656
+ }
1657
+
1658
+ const URL_HEADER_PREFIX_RE = /^#+\s+/;
1659
+
1660
+ function renderSearchDisplayLines(
1661
+ lines: readonly string[],
1662
+ headerBase: string | undefined,
1663
+ fileScope: string | undefined,
1664
+ uiTheme: Theme,
1665
+ ): RenderedSearchLine[] {
1666
+ const contexts = classifyGroupedLines(lines, headerBase, fileScope);
1667
+ // `classifyGroupedLines` can't resolve internal URLs (TUI-only), so track the
1668
+ // resolved URL target here and use it for the body lines that follow.
1669
+ let urlFile: string | undefined;
1670
+ return lines.map((line, index) => {
1671
+ const ctx = contexts[index]!;
1672
+ if (ctx.kind === "dir") {
1673
+ urlFile = undefined;
1674
+ const styled = uiTheme.fg("accent", line);
1675
+ return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
1676
+ }
1677
+ if (ctx.kind === "file") {
1678
+ if (ctx.isUrl) {
1679
+ const raw = line
1680
+ .replace(URL_HEADER_PREFIX_RE, "")
1681
+ .trimEnd()
1682
+ .replace(/\s+\([^)]*\)\s*$/, "");
1683
+ const linked = linkUrlLikeSearchHeader(raw, uiTheme.fg("accent", line));
1684
+ urlFile = linked.absPath;
1685
+ return { raw: line, styled: linked.line };
1686
+ }
1687
+ urlFile = undefined;
1688
+ // Root-level files keep the bright accent; nested file headers are dimmed.
1689
+ const styled = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
1690
+ return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
1691
+ }
1692
+ const styled = uiTheme.fg("toolOutput", line);
1693
+ const lineNumber = parseSearchDisplayLineNumber(line);
1694
+ const filePath = ctx.filePath ?? urlFile;
1695
+ return {
1696
+ raw: line,
1697
+ styled: filePath && lineNumber !== undefined ? fileHyperlink(filePath, styled, { line: lineNumber }) : styled,
1698
+ };
1699
+ });
1700
+ }
1701
+
1702
+ function compactSearchPreviewGroup(group: RenderedSearchLine[]): RenderedSearchLine[] {
1703
+ const compact = group.filter(line => isSearchHeaderLine(line.raw) || isSearchMatchLine(line.raw));
1704
+ return compact.length > 0 ? compact : group;
1705
+ }
1706
+
1707
+ function countPreviewMatches(lines: readonly RenderedSearchLine[], hasMarkedMatches: boolean): number {
1708
+ if (hasMarkedMatches) return lines.reduce((count, line) => count + (isSearchMatchLine(line.raw) ? 1 : 0), 0);
1709
+ return lines.reduce((count, line) => count + (!isSearchHeaderLine(line.raw) && line.raw.length > 0 ? 1 : 0), 0);
1710
+ }
1711
+
1712
+ function renderBudgetedSearchGroups(
1713
+ groups: RenderedSearchLine[][],
1714
+ maxLines: number,
1715
+ matchCount: number,
1716
+ uiTheme: Theme,
1717
+ compact: boolean,
1718
+ ): string[] {
1719
+ if (maxLines <= 0) return [];
1720
+ const renderedGroups = groups
1721
+ .map(group => (compact ? compactSearchPreviewGroup(group) : group))
1722
+ .filter(group => group.length > 0);
1723
+ if (renderedGroups.length === 0) return [];
1724
+
1725
+ let totalLines = 0;
1726
+ let totalMarkedMatches = 0;
1727
+ let totalFallbackMatches = 0;
1728
+ for (const group of renderedGroups) {
1729
+ totalLines += group.length;
1730
+ totalMarkedMatches += countPreviewMatches(group, true);
1731
+ totalFallbackMatches += countPreviewMatches(group, false);
1732
+ }
1733
+ const hasMarkedMatches = totalMarkedMatches > 0;
1734
+ const needsSummary = totalLines > maxLines;
1735
+ const contentBudget = needsSummary ? Math.max(maxLines - 1, 0) : maxLines;
1736
+ const visibleGroups: RenderedSearchLine[][] = [];
1737
+ let visibleLineCount = 0;
1738
+ let visibleMatches = 0;
1739
+ for (const group of renderedGroups) {
1740
+ if (visibleLineCount >= contentBudget) break;
1741
+ const available = contentBudget - visibleLineCount;
1742
+ const take = Math.min(group.length, available);
1743
+ if (take <= 0) break;
1744
+ const visibleGroup = group.slice(0, take);
1745
+ visibleGroups.push(visibleGroup);
1746
+ visibleLineCount += visibleGroup.length;
1747
+ visibleMatches += countPreviewMatches(visibleGroup, hasMarkedMatches);
1748
+ }
1749
+
1750
+ const totalMatches = hasMarkedMatches ? totalMarkedMatches : Math.max(matchCount, totalFallbackMatches);
1751
+ const hiddenMatches = Math.max(totalMatches - visibleMatches, 0);
1752
+ const hiddenLines = Math.max(totalLines - visibleLineCount, 0);
1753
+ const hasSummary = needsSummary && (hiddenMatches > 0 || hiddenLines > 0);
1754
+ const lines: string[] = [];
1755
+ for (let i = 0; i < visibleGroups.length; i++) {
1756
+ const group = visibleGroups[i]!;
1757
+ const isLast = !hasSummary && i === visibleGroups.length - 1;
1758
+ const prefix = `${uiTheme.fg("dim", getTreeBranch(isLast, uiTheme))} `;
1759
+ const continuePrefix = uiTheme.fg("dim", getTreeContinuePrefix(isLast, uiTheme));
1760
+ lines.push(`${prefix}${replaceTabs(group[0]!.styled)}`);
1761
+ for (let j = 1; j < group.length; j++) {
1762
+ lines.push(`${continuePrefix}${replaceTabs(group[j]!.styled)}`);
1763
+ }
1764
+ }
1765
+ if (hasSummary) {
1766
+ const hiddenLabel =
1767
+ hiddenMatches > 0 ? formatMoreItems(hiddenMatches, "match") : formatMoreItems(hiddenLines, "line");
1768
+ lines.push(`${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", hiddenLabel)}`);
1769
+ }
1770
+ return lines;
1771
+ }
1772
+
1773
+ function grepStatusIcon(uiTheme: Theme): string {
1774
+ return uiTheme.fg("toolTitle", uiTheme.symbol("icon.search"));
1775
+ }
1776
+
1777
+ export const grepToolRenderer = {
1778
+ inline: true,
1779
+ renderCall(args: GrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
1780
+ const paths = toPathList(args.path ?? args.paths);
1781
+ const meta: string[] = [];
1782
+ if (paths.length) meta.push(`in ${paths.join(", ")}`);
1783
+ if (args.case === false) meta.push("case:insensitive");
1784
+ if (args.gitignore === false) meta.push("gitignore:false");
1785
+ if (args.skip !== undefined && args.skip > 0) meta.push(`skip:${args.skip}`);
1786
+
1787
+ const text = renderStatusLine(
1788
+ { icon: "pending", title: "Grep", titleColor: "toolTitle", description: args.pattern || "?", meta },
1789
+ uiTheme,
1790
+ );
1791
+ return new Text(text, 1, 0);
1792
+ },
1793
+
1794
+ renderResult(
1795
+ result: { content: Array<{ type: string; text?: string }>; details?: GrepToolDetails; isError?: boolean },
1796
+ options: RenderResultOptions,
1797
+ uiTheme: Theme,
1798
+ args?: GrepRenderArgs,
1799
+ ): Component {
1800
+ const details = result.details;
1801
+
1802
+ if (result.isError || details?.error) {
1803
+ const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error";
1804
+ return new Text(formatErrorMessage(errorText, uiTheme), 1, 0);
1805
+ }
1806
+
1807
+ const hasDetailedData = details?.matchCount !== undefined || details?.fileCount !== undefined;
1808
+
1809
+ if (!hasDetailedData) {
1810
+ const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text;
1811
+ if (!textContent || textContent === "No matches found") {
1812
+ return new Text(formatEmptyMessage("No matches found", uiTheme), 1, 0);
1813
+ }
1814
+ const lines = textContent.split("\n").filter(line => line.trim() !== "");
1815
+ const description = args?.pattern ?? undefined;
1816
+ const header = renderStatusLine(
1817
+ {
1818
+ iconOverride: grepStatusIcon(uiTheme),
1819
+ title: "Grep",
1820
+ titleColor: "toolTitle",
1821
+ description,
1822
+ meta: [formatCount("item", lines.length)],
1823
+ },
1824
+ uiTheme,
1825
+ );
1826
+ return createCachedComponent(
1827
+ () => options.expanded,
1828
+ width => {
1829
+ const listLines = renderTreeList(
1830
+ {
1831
+ items: lines,
1832
+ expanded: options.expanded,
1833
+ maxCollapsed: COLLAPSED_TEXT_LIMIT,
1834
+ maxCollapsedLines: COLLAPSED_TEXT_LIMIT,
1835
+ itemType: "item",
1836
+ renderItem: line => uiTheme.fg("toolOutput", line),
1837
+ },
1838
+ uiTheme,
1839
+ );
1840
+ return [header, ...listLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
1841
+ },
1842
+ { paddingX: 1 },
1843
+ );
1844
+ }
1845
+
1846
+ const matchCount = details?.matchCount ?? 0;
1847
+ const fileCount = details?.fileCount ?? 0;
1848
+ const truncation = details?.meta?.truncation;
1849
+ const limits = details?.meta?.limits;
1850
+ const truncated = Boolean(details?.truncated || truncation || limits?.columnTruncated);
1851
+
1852
+ const missingPathsList = details?.missingPaths ?? [];
1853
+ const missingNote =
1854
+ missingPathsList.length > 0
1855
+ ? uiTheme.fg("warning", `skipped missing: ${missingPathsList.join(", ")}`)
1856
+ : undefined;
1857
+
1858
+ if (matchCount === 0) {
1859
+ const meta = ["0 matches"];
1860
+ const scopeMeta = searchScopeMeta(details);
1861
+ if (scopeMeta) meta.push(scopeMeta);
1862
+ const header = renderStatusLine(
1863
+ { icon: "warning", title: "Grep", titleColor: "toolTitle", description: args?.pattern, meta },
1864
+ uiTheme,
1865
+ );
1866
+ const lines = [header, formatEmptyMessage("No matches found", uiTheme)];
1867
+ if (missingNote) lines.push(missingNote);
1868
+ return new Text(lines.join("\n"), 1, 0);
1869
+ }
1870
+
1871
+ const summaryParts = [formatCount("match", matchCount), formatCount("file", fileCount)];
1872
+ const meta = [...summaryParts];
1873
+ const scopeMeta = searchScopeMeta(details);
1874
+ if (scopeMeta) meta.push(scopeMeta);
1875
+ if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
1876
+ const description = args?.pattern ?? undefined;
1877
+ const header = renderStatusLine(
1878
+ {
1879
+ ...(truncated ? { icon: "warning" as const } : { iconOverride: grepStatusIcon(uiTheme) }),
1880
+ title: "Grep",
1881
+ titleColor: "toolTitle",
1882
+ description,
1883
+ meta,
1884
+ },
1885
+ uiTheme,
1886
+ );
1887
+
1888
+ const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text ?? "";
1889
+ const allLines = textContent.split("\n");
1890
+ // Resolve hyperlinks once over the whole output so a nested directory stack
1891
+ // reconstructs correctly across blank-line group boundaries.
1892
+ // Header/match display paths are cwd-relative, so resolve them against cwd
1893
+ // (falling back to searchPath for legacy results that predate `cwd`); the
1894
+ // scoped file's absolute path seeds body lines in single-file searches.
1895
+ const renderedLines = renderSearchDisplayLines(
1896
+ allLines,
1897
+ details?.cwd ?? details?.searchPath,
1898
+ details?.searchPath,
1899
+ uiTheme,
1900
+ );
1901
+ const matchGroups = groupLineIndicesByBlank(allLines).map(indices => indices.map(i => renderedLines[i]!));
1902
+
1903
+ const extraLines: string[] = [];
1904
+ if (missingNote) extraLines.push(missingNote);
1905
+
1906
+ return createCachedComponent(
1907
+ () => options.expanded,
1908
+ width => {
1909
+ const budget = Math.max(
1910
+ (options.expanded ? EXPANDED_TEXT_LIMIT : COLLAPSED_TEXT_LIMIT) - extraLines.length,
1911
+ 0,
1912
+ );
1913
+ const matchLines = renderBudgetedSearchGroups(matchGroups, budget, matchCount, uiTheme, !options.expanded);
1914
+ return [header, ...matchLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
1915
+ },
1916
+ { paddingX: 1 },
1917
+ );
1918
+ },
1919
+ mergeCallAndResult: true,
1920
+ };