@wahack/pi-coding-agent 15.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1152) hide show
  1. package/CHANGELOG.md +10031 -0
  2. package/README.md +36 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +104 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/extensions/README.md +142 -0
  7. package/examples/extensions/api-demo.ts +79 -0
  8. package/examples/extensions/chalk-logger.ts +25 -0
  9. package/examples/extensions/hello.ts +31 -0
  10. package/examples/extensions/pirate.ts +43 -0
  11. package/examples/extensions/plan-mode.ts +549 -0
  12. package/examples/extensions/reload-runtime.ts +38 -0
  13. package/examples/extensions/thinking-note.ts +13 -0
  14. package/examples/extensions/tools.ts +145 -0
  15. package/examples/extensions/with-deps/index.ts +36 -0
  16. package/examples/extensions/with-deps/package-lock.json +31 -0
  17. package/examples/extensions/with-deps/package.json +17 -0
  18. package/examples/hooks/README.md +56 -0
  19. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  20. package/examples/hooks/confirm-destructive.ts +58 -0
  21. package/examples/hooks/custom-compaction.ts +115 -0
  22. package/examples/hooks/dirty-repo-guard.ts +51 -0
  23. package/examples/hooks/file-trigger.ts +40 -0
  24. package/examples/hooks/git-checkpoint.ts +52 -0
  25. package/examples/hooks/handoff.ts +149 -0
  26. package/examples/hooks/permission-gate.ts +33 -0
  27. package/examples/hooks/protected-paths.ts +29 -0
  28. package/examples/hooks/qna.ts +118 -0
  29. package/examples/hooks/status-line.ts +39 -0
  30. package/examples/sdk/01-minimal.ts +21 -0
  31. package/examples/sdk/02-custom-model.ts +49 -0
  32. package/examples/sdk/03-custom-prompt.ts +46 -0
  33. package/examples/sdk/04-skills.ts +43 -0
  34. package/examples/sdk/06-extensions.ts +82 -0
  35. package/examples/sdk/06-hooks.ts +61 -0
  36. package/examples/sdk/07-context-files.ts +35 -0
  37. package/examples/sdk/08-prompt-templates.ts +41 -0
  38. package/examples/sdk/08-slash-commands.ts +46 -0
  39. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  40. package/examples/sdk/11-sessions.ts +47 -0
  41. package/examples/sdk/12-redis-sessions.ts +54 -0
  42. package/examples/sdk/13-sql-sessions.ts +61 -0
  43. package/examples/sdk/README.md +172 -0
  44. package/package.json +554 -0
  45. package/scripts/build-binary.ts +100 -0
  46. package/scripts/bundle-dist.ts +90 -0
  47. package/scripts/format-prompts.ts +68 -0
  48. package/scripts/generate-docs-index.ts +40 -0
  49. package/scripts/generate-template.ts +33 -0
  50. package/scripts/omp +42 -0
  51. package/scripts/omp.ts +19 -0
  52. package/src/async/index.ts +1 -0
  53. package/src/async/job-manager.ts +625 -0
  54. package/src/auto-thinking/classifier.ts +185 -0
  55. package/src/autoresearch/command-resume.md +14 -0
  56. package/src/autoresearch/dashboard.ts +436 -0
  57. package/src/autoresearch/git.ts +319 -0
  58. package/src/autoresearch/helpers.ts +218 -0
  59. package/src/autoresearch/index.ts +536 -0
  60. package/src/autoresearch/prompt-setup.md +43 -0
  61. package/src/autoresearch/prompt.md +103 -0
  62. package/src/autoresearch/resume-message.md +10 -0
  63. package/src/autoresearch/state.ts +273 -0
  64. package/src/autoresearch/storage.ts +699 -0
  65. package/src/autoresearch/tools/init-experiment.ts +272 -0
  66. package/src/autoresearch/tools/log-experiment.ts +524 -0
  67. package/src/autoresearch/tools/run-experiment.ts +407 -0
  68. package/src/autoresearch/tools/update-notes.ts +109 -0
  69. package/src/autoresearch/types.ts +168 -0
  70. package/src/bun-imports.d.ts +28 -0
  71. package/src/capability/context-file.ts +44 -0
  72. package/src/capability/extension-module.ts +34 -0
  73. package/src/capability/extension.ts +47 -0
  74. package/src/capability/fs.ts +117 -0
  75. package/src/capability/hook.ts +40 -0
  76. package/src/capability/index.ts +436 -0
  77. package/src/capability/instruction.ts +37 -0
  78. package/src/capability/mcp.ts +74 -0
  79. package/src/capability/prompt.ts +35 -0
  80. package/src/capability/rule-buckets.ts +66 -0
  81. package/src/capability/rule.ts +261 -0
  82. package/src/capability/settings.ts +34 -0
  83. package/src/capability/skill.ts +63 -0
  84. package/src/capability/slash-command.ts +40 -0
  85. package/src/capability/ssh.ts +41 -0
  86. package/src/capability/system-prompt.ts +34 -0
  87. package/src/capability/tool.ts +38 -0
  88. package/src/capability/types.ts +168 -0
  89. package/src/cli/agents-cli.ts +138 -0
  90. package/src/cli/args.ts +340 -0
  91. package/src/cli/auth-broker-cli.ts +895 -0
  92. package/src/cli/auth-gateway-cli.ts +611 -0
  93. package/src/cli/classify-install-target.ts +76 -0
  94. package/src/cli/claude-trace-cli.ts +795 -0
  95. package/src/cli/commands/init-xdg.ts +27 -0
  96. package/src/cli/completion-gen.ts +550 -0
  97. package/src/cli/config-cli.ts +418 -0
  98. package/src/cli/dry-balance-cli.ts +856 -0
  99. package/src/cli/extension-flags.ts +48 -0
  100. package/src/cli/file-processor.ts +133 -0
  101. package/src/cli/gallery-cli.ts +230 -0
  102. package/src/cli/gallery-fixtures/agentic.ts +407 -0
  103. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  104. package/src/cli/gallery-fixtures/edit.ts +194 -0
  105. package/src/cli/gallery-fixtures/fs.ts +220 -0
  106. package/src/cli/gallery-fixtures/index.ts +40 -0
  107. package/src/cli/gallery-fixtures/interaction.ts +49 -0
  108. package/src/cli/gallery-fixtures/memory.ts +81 -0
  109. package/src/cli/gallery-fixtures/misc.ts +250 -0
  110. package/src/cli/gallery-fixtures/search.ts +213 -0
  111. package/src/cli/gallery-fixtures/shell.ts +167 -0
  112. package/src/cli/gallery-fixtures/types.ts +57 -0
  113. package/src/cli/gallery-fixtures/web.ts +158 -0
  114. package/src/cli/gallery-screenshot.ts +279 -0
  115. package/src/cli/grep-cli.ts +160 -0
  116. package/src/cli/grievances-cli.ts +256 -0
  117. package/src/cli/initial-message.ts +58 -0
  118. package/src/cli/list-models.ts +194 -0
  119. package/src/cli/plugin-cli.ts +996 -0
  120. package/src/cli/read-cli.ts +57 -0
  121. package/src/cli/session-picker.ts +79 -0
  122. package/src/cli/setup-cli.ts +231 -0
  123. package/src/cli/shell-cli.ts +176 -0
  124. package/src/cli/ssh-cli.ts +179 -0
  125. package/src/cli/startup-cwd.ts +68 -0
  126. package/src/cli/stats-cli.ts +238 -0
  127. package/src/cli/tiny-models-cli.ts +127 -0
  128. package/src/cli/update-cli.ts +611 -0
  129. package/src/cli/usage-cli.ts +603 -0
  130. package/src/cli/web-search-cli.ts +132 -0
  131. package/src/cli/worktree-cli.ts +291 -0
  132. package/src/cli-commands.ts +79 -0
  133. package/src/cli.ts +200 -0
  134. package/src/commands/acp.ts +24 -0
  135. package/src/commands/agents.ts +57 -0
  136. package/src/commands/auth-broker.ts +99 -0
  137. package/src/commands/auth-gateway.ts +69 -0
  138. package/src/commands/commit.ts +46 -0
  139. package/src/commands/complete.ts +66 -0
  140. package/src/commands/completions.ts +60 -0
  141. package/src/commands/config.ts +51 -0
  142. package/src/commands/dry-balance.ts +43 -0
  143. package/src/commands/gallery.ts +52 -0
  144. package/src/commands/grep.ts +48 -0
  145. package/src/commands/grievances.ts +51 -0
  146. package/src/commands/install.ts +107 -0
  147. package/src/commands/launch.ts +169 -0
  148. package/src/commands/plugin.ts +78 -0
  149. package/src/commands/read.ts +38 -0
  150. package/src/commands/setup.ts +67 -0
  151. package/src/commands/shell.ts +29 -0
  152. package/src/commands/ssh.ts +60 -0
  153. package/src/commands/stats.ts +29 -0
  154. package/src/commands/tiny-models.ts +36 -0
  155. package/src/commands/update.ts +21 -0
  156. package/src/commands/usage.ts +35 -0
  157. package/src/commands/web-search.ts +42 -0
  158. package/src/commands/worktree.ts +56 -0
  159. package/src/commit/agentic/agent.ts +317 -0
  160. package/src/commit/agentic/fallback.ts +96 -0
  161. package/src/commit/agentic/index.ts +355 -0
  162. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  163. package/src/commit/agentic/prompts/session-user.md +25 -0
  164. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  165. package/src/commit/agentic/prompts/system.md +38 -0
  166. package/src/commit/agentic/state.ts +60 -0
  167. package/src/commit/agentic/tools/analyze-file.ts +146 -0
  168. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  169. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  170. package/src/commit/agentic/tools/git-overview.ts +81 -0
  171. package/src/commit/agentic/tools/index.ts +54 -0
  172. package/src/commit/agentic/tools/propose-changelog.ts +144 -0
  173. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  174. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  175. package/src/commit/agentic/tools/schemas.ts +23 -0
  176. package/src/commit/agentic/tools/split-commit.ts +245 -0
  177. package/src/commit/agentic/topo-sort.ts +44 -0
  178. package/src/commit/agentic/trivial.ts +51 -0
  179. package/src/commit/agentic/validation.ts +183 -0
  180. package/src/commit/analysis/conventional.ts +64 -0
  181. package/src/commit/analysis/index.ts +4 -0
  182. package/src/commit/analysis/scope.ts +242 -0
  183. package/src/commit/analysis/summary.ts +105 -0
  184. package/src/commit/analysis/validation.ts +66 -0
  185. package/src/commit/changelog/detect.ts +40 -0
  186. package/src/commit/changelog/generate.ts +97 -0
  187. package/src/commit/changelog/index.ts +234 -0
  188. package/src/commit/changelog/parse.ts +44 -0
  189. package/src/commit/cli.ts +85 -0
  190. package/src/commit/git/diff.ts +148 -0
  191. package/src/commit/index.ts +5 -0
  192. package/src/commit/map-reduce/index.ts +69 -0
  193. package/src/commit/map-reduce/map-phase.ts +193 -0
  194. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  195. package/src/commit/map-reduce/utils.ts +9 -0
  196. package/src/commit/message.ts +11 -0
  197. package/src/commit/model-selection.ts +92 -0
  198. package/src/commit/pipeline.ts +243 -0
  199. package/src/commit/prompts/analysis-system.md +148 -0
  200. package/src/commit/prompts/analysis-user.md +38 -0
  201. package/src/commit/prompts/changelog-system.md +50 -0
  202. package/src/commit/prompts/changelog-user.md +18 -0
  203. package/src/commit/prompts/file-observer-system.md +24 -0
  204. package/src/commit/prompts/file-observer-user.md +8 -0
  205. package/src/commit/prompts/reduce-system.md +50 -0
  206. package/src/commit/prompts/reduce-user.md +17 -0
  207. package/src/commit/prompts/summary-retry.md +3 -0
  208. package/src/commit/prompts/summary-system.md +38 -0
  209. package/src/commit/prompts/summary-user.md +13 -0
  210. package/src/commit/prompts/types-description.md +2 -0
  211. package/src/commit/shared-llm.ts +77 -0
  212. package/src/commit/types.ts +118 -0
  213. package/src/commit/utils/exclusions.ts +42 -0
  214. package/src/commit/utils.ts +58 -0
  215. package/src/config/api-key-resolver.ts +60 -0
  216. package/src/config/append-only-context-mode.ts +31 -0
  217. package/src/config/config-file.ts +317 -0
  218. package/src/config/file-lock.ts +164 -0
  219. package/src/config/keybindings.ts +628 -0
  220. package/src/config/mcp-schema.json +230 -0
  221. package/src/config/model-discovery.ts +554 -0
  222. package/src/config/model-registry.ts +2090 -0
  223. package/src/config/model-resolver.ts +1502 -0
  224. package/src/config/model-roles.ts +74 -0
  225. package/src/config/models-config-schema.ts +226 -0
  226. package/src/config/models-config.ts +129 -0
  227. package/src/config/prompt-templates.ts +185 -0
  228. package/src/config/resolve-config-value.ts +94 -0
  229. package/src/config/settings-schema.ts +3530 -0
  230. package/src/config/settings.ts +1178 -0
  231. package/src/config.ts +242 -0
  232. package/src/cursor.ts +340 -0
  233. package/src/dap/client.ts +760 -0
  234. package/src/dap/config.ts +189 -0
  235. package/src/dap/defaults.json +212 -0
  236. package/src/dap/index.ts +4 -0
  237. package/src/dap/session.ts +1441 -0
  238. package/src/dap/types.ts +610 -0
  239. package/src/debug/index.ts +515 -0
  240. package/src/debug/log-formatting.ts +58 -0
  241. package/src/debug/log-viewer.ts +908 -0
  242. package/src/debug/profiler.ts +162 -0
  243. package/src/debug/protocol-probe.ts +267 -0
  244. package/src/debug/raw-sse-buffer.ts +273 -0
  245. package/src/debug/raw-sse.ts +292 -0
  246. package/src/debug/report-bundle.ts +374 -0
  247. package/src/debug/system-info.ts +111 -0
  248. package/src/debug/terminal-info.ts +124 -0
  249. package/src/discovery/agents-md.ts +67 -0
  250. package/src/discovery/agents.ts +230 -0
  251. package/src/discovery/at-imports.ts +273 -0
  252. package/src/discovery/builtin-defaults.ts +39 -0
  253. package/src/discovery/builtin-rules/index.ts +54 -0
  254. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  255. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  256. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  257. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  258. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  259. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  260. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  261. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  262. package/src/discovery/builtin-rules/ts-no-any.md +56 -0
  263. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +44 -0
  264. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  265. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  266. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  267. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  268. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  269. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  270. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  271. package/src/discovery/builtin.ts +906 -0
  272. package/src/discovery/claude-plugins.ts +386 -0
  273. package/src/discovery/claude.ts +584 -0
  274. package/src/discovery/cline.ts +83 -0
  275. package/src/discovery/codex.ts +522 -0
  276. package/src/discovery/cursor.ts +220 -0
  277. package/src/discovery/gemini.ts +383 -0
  278. package/src/discovery/github.ts +154 -0
  279. package/src/discovery/helpers.ts +1016 -0
  280. package/src/discovery/index.ts +81 -0
  281. package/src/discovery/mcp-json.ts +171 -0
  282. package/src/discovery/omp-extension-roots.ts +190 -0
  283. package/src/discovery/omp-plugins.ts +383 -0
  284. package/src/discovery/opencode.ts +398 -0
  285. package/src/discovery/plugin-dir-roots.ts +28 -0
  286. package/src/discovery/ssh.ts +153 -0
  287. package/src/discovery/substitute-plugin-root.ts +29 -0
  288. package/src/discovery/vscode.ts +105 -0
  289. package/src/discovery/windsurf.ts +147 -0
  290. package/src/edit/apply-patch/index.ts +87 -0
  291. package/src/edit/apply-patch/parser.ts +174 -0
  292. package/src/edit/diff.ts +999 -0
  293. package/src/edit/file-snapshot-store.ts +91 -0
  294. package/src/edit/hashline/block-resolver.ts +33 -0
  295. package/src/edit/hashline/diff.ts +290 -0
  296. package/src/edit/hashline/execute.ts +242 -0
  297. package/src/edit/hashline/filesystem.ts +130 -0
  298. package/src/edit/hashline/index.ts +5 -0
  299. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  300. package/src/edit/hashline/params.ts +18 -0
  301. package/src/edit/index.ts +571 -0
  302. package/src/edit/modes/apply-patch.lark +19 -0
  303. package/src/edit/modes/apply-patch.ts +53 -0
  304. package/src/edit/modes/patch.ts +1891 -0
  305. package/src/edit/modes/replace.ts +1137 -0
  306. package/src/edit/normalize.ts +345 -0
  307. package/src/edit/notebook.ts +242 -0
  308. package/src/edit/read-file.ts +25 -0
  309. package/src/edit/renderer.ts +769 -0
  310. package/src/edit/streaming.ts +517 -0
  311. package/src/eval/__tests__/agent-bridge.test.ts +708 -0
  312. package/src/eval/__tests__/bridge-timeout.test.ts +64 -0
  313. package/src/eval/__tests__/budget-bridge.test.ts +69 -0
  314. package/src/eval/__tests__/completion-bridge.test.ts +412 -0
  315. package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
  316. package/src/eval/__tests__/idle-timeout.test.ts +80 -0
  317. package/src/eval/__tests__/js-context-manager.test.ts +241 -0
  318. package/src/eval/__tests__/kernel-spawn.test.ts +103 -0
  319. package/src/eval/agent-bridge.ts +319 -0
  320. package/src/eval/backend.ts +71 -0
  321. package/src/eval/bridge-timeout.ts +44 -0
  322. package/src/eval/budget-bridge.ts +48 -0
  323. package/src/eval/completion-bridge.ts +207 -0
  324. package/src/eval/concurrency-bridge.ts +34 -0
  325. package/src/eval/idle-timeout.ts +91 -0
  326. package/src/eval/index.ts +4 -0
  327. package/src/eval/js/context-manager.ts +502 -0
  328. package/src/eval/js/executor.ts +173 -0
  329. package/src/eval/js/index.ts +51 -0
  330. package/src/eval/js/shared/helpers.ts +283 -0
  331. package/src/eval/js/shared/indirect-eval.ts +30 -0
  332. package/src/eval/js/shared/local-module-loader.ts +342 -0
  333. package/src/eval/js/shared/prelude.ts +2 -0
  334. package/src/eval/js/shared/prelude.txt +246 -0
  335. package/src/eval/js/shared/rewrite-imports.ts +532 -0
  336. package/src/eval/js/shared/runtime.ts +352 -0
  337. package/src/eval/js/shared/types.ts +18 -0
  338. package/src/eval/js/tool-bridge.ts +162 -0
  339. package/src/eval/js/worker-core.ts +132 -0
  340. package/src/eval/js/worker-entry.ts +30 -0
  341. package/src/eval/js/worker-protocol.ts +47 -0
  342. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  343. package/src/eval/py/display.ts +71 -0
  344. package/src/eval/py/executor.ts +742 -0
  345. package/src/eval/py/index.ts +68 -0
  346. package/src/eval/py/kernel.ts +748 -0
  347. package/src/eval/py/prelude.py +658 -0
  348. package/src/eval/py/prelude.ts +3 -0
  349. package/src/eval/py/runner.py +1133 -0
  350. package/src/eval/py/runtime.ts +276 -0
  351. package/src/eval/py/spawn-options.ts +126 -0
  352. package/src/eval/py/tool-bridge.ts +182 -0
  353. package/src/eval/session-id.ts +8 -0
  354. package/src/eval/types.ts +48 -0
  355. package/src/exa/index.ts +2 -0
  356. package/src/exa/mcp-client.ts +370 -0
  357. package/src/exa/types.ts +69 -0
  358. package/src/exec/bash-executor.ts +419 -0
  359. package/src/exec/exec.ts +53 -0
  360. package/src/exec/non-interactive-env.ts +48 -0
  361. package/src/export/custom-share.ts +65 -0
  362. package/src/export/html/index.ts +164 -0
  363. package/src/export/html/template.css +1051 -0
  364. package/src/export/html/template.generated.ts +2 -0
  365. package/src/export/html/template.html +46 -0
  366. package/src/export/html/template.js +2271 -0
  367. package/src/export/html/template.macro.ts +25 -0
  368. package/src/export/html/vendor/highlight.min.js +1213 -0
  369. package/src/export/html/vendor/marked.min.js +6 -0
  370. package/src/export/ttsr.ts +583 -0
  371. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  372. package/src/extensibility/custom-commands/bundled/review/index.ts +489 -0
  373. package/src/extensibility/custom-commands/index.ts +2 -0
  374. package/src/extensibility/custom-commands/loader.ts +238 -0
  375. package/src/extensibility/custom-commands/types.ts +113 -0
  376. package/src/extensibility/custom-tools/index.ts +7 -0
  377. package/src/extensibility/custom-tools/loader.ts +269 -0
  378. package/src/extensibility/custom-tools/types.ts +270 -0
  379. package/src/extensibility/custom-tools/wrapper.ts +47 -0
  380. package/src/extensibility/extensions/compact-handler.ts +40 -0
  381. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  382. package/src/extensibility/extensions/index.ts +16 -0
  383. package/src/extensibility/extensions/loader.ts +572 -0
  384. package/src/extensibility/extensions/runner.ts +922 -0
  385. package/src/extensibility/extensions/types.ts +1322 -0
  386. package/src/extensibility/extensions/wrapper.ts +223 -0
  387. package/src/extensibility/hooks/index.ts +5 -0
  388. package/src/extensibility/hooks/loader.ts +257 -0
  389. package/src/extensibility/hooks/runner.ts +425 -0
  390. package/src/extensibility/hooks/tool-wrapper.ts +107 -0
  391. package/src/extensibility/hooks/types.ts +606 -0
  392. package/src/extensibility/legacy-pi-ai-shim.ts +24 -0
  393. package/src/extensibility/legacy-pi-coding-agent-shim.ts +15 -0
  394. package/src/extensibility/plugins/doctor.ts +65 -0
  395. package/src/extensibility/plugins/git-url.ts +367 -0
  396. package/src/extensibility/plugins/index.ts +9 -0
  397. package/src/extensibility/plugins/installer.ts +192 -0
  398. package/src/extensibility/plugins/legacy-pi-compat.ts +682 -0
  399. package/src/extensibility/plugins/loader.ts +313 -0
  400. package/src/extensibility/plugins/manager.ts +827 -0
  401. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  402. package/src/extensibility/plugins/marketplace/fetcher.ts +317 -0
  403. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  404. package/src/extensibility/plugins/marketplace/manager.ts +770 -0
  405. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  406. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  407. package/src/extensibility/plugins/marketplace/types.ts +191 -0
  408. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  409. package/src/extensibility/plugins/parser.ts +105 -0
  410. package/src/extensibility/plugins/types.ts +194 -0
  411. package/src/extensibility/shared-events.ts +343 -0
  412. package/src/extensibility/skills.ts +312 -0
  413. package/src/extensibility/slash-commands.ts +227 -0
  414. package/src/extensibility/tool-proxy.ts +25 -0
  415. package/src/extensibility/typebox.ts +418 -0
  416. package/src/extensibility/utils.ts +44 -0
  417. package/src/goals/index.ts +3 -0
  418. package/src/goals/runtime.ts +528 -0
  419. package/src/goals/state.ts +37 -0
  420. package/src/goals/tools/goal-tool.ts +251 -0
  421. package/src/hindsight/backend.ts +354 -0
  422. package/src/hindsight/bank.ts +156 -0
  423. package/src/hindsight/client.ts +598 -0
  424. package/src/hindsight/config.ts +175 -0
  425. package/src/hindsight/content.ts +210 -0
  426. package/src/hindsight/index.ts +8 -0
  427. package/src/hindsight/mental-models.ts +429 -0
  428. package/src/hindsight/seeds.json +32 -0
  429. package/src/hindsight/state.ts +488 -0
  430. package/src/hindsight/transcript.ts +71 -0
  431. package/src/index.ts +59 -0
  432. package/src/internal-urls/agent-protocol.ts +146 -0
  433. package/src/internal-urls/artifact-protocol.ts +107 -0
  434. package/src/internal-urls/docs-index.generated.ts +106 -0
  435. package/src/internal-urls/history-protocol.ts +113 -0
  436. package/src/internal-urls/index.ts +25 -0
  437. package/src/internal-urls/issue-pr-protocol.ts +584 -0
  438. package/src/internal-urls/json-query.ts +126 -0
  439. package/src/internal-urls/local-protocol.ts +287 -0
  440. package/src/internal-urls/mcp-protocol.ts +151 -0
  441. package/src/internal-urls/memory-protocol.ts +169 -0
  442. package/src/internal-urls/omp-protocol.ts +93 -0
  443. package/src/internal-urls/parse.ts +72 -0
  444. package/src/internal-urls/registry-helpers.ts +25 -0
  445. package/src/internal-urls/router.ts +105 -0
  446. package/src/internal-urls/rule-protocol.ts +45 -0
  447. package/src/internal-urls/skill-protocol.ts +96 -0
  448. package/src/internal-urls/types.ts +152 -0
  449. package/src/internal-urls/vault-protocol.ts +936 -0
  450. package/src/irc/bus.ts +292 -0
  451. package/src/lib/xai-http.ts +124 -0
  452. package/src/lsp/client.ts +1193 -0
  453. package/src/lsp/clients/biome-client.ts +264 -0
  454. package/src/lsp/clients/index.ts +50 -0
  455. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  456. package/src/lsp/clients/swiftlint-client.ts +120 -0
  457. package/src/lsp/config.ts +502 -0
  458. package/src/lsp/defaults.json +493 -0
  459. package/src/lsp/diagnostics-ledger.ts +51 -0
  460. package/src/lsp/edits.ts +267 -0
  461. package/src/lsp/index.ts +2477 -0
  462. package/src/lsp/lspmux.ts +233 -0
  463. package/src/lsp/render.ts +694 -0
  464. package/src/lsp/startup-events.ts +13 -0
  465. package/src/lsp/types.ts +455 -0
  466. package/src/lsp/utils.ts +718 -0
  467. package/src/main.ts +1325 -0
  468. package/src/mcp/client.ts +484 -0
  469. package/src/mcp/config-writer.ts +225 -0
  470. package/src/mcp/config.ts +365 -0
  471. package/src/mcp/index.ts +29 -0
  472. package/src/mcp/json-rpc.ts +122 -0
  473. package/src/mcp/loader.ts +124 -0
  474. package/src/mcp/manager.ts +1275 -0
  475. package/src/mcp/oauth-discovery.ts +442 -0
  476. package/src/mcp/oauth-flow.ts +442 -0
  477. package/src/mcp/render.ts +132 -0
  478. package/src/mcp/smithery-auth.ts +104 -0
  479. package/src/mcp/smithery-connect.ts +145 -0
  480. package/src/mcp/smithery-registry.ts +477 -0
  481. package/src/mcp/timeout.ts +59 -0
  482. package/src/mcp/tool-bridge.ts +426 -0
  483. package/src/mcp/tool-cache.ts +117 -0
  484. package/src/mcp/transports/http.ts +519 -0
  485. package/src/mcp/transports/index.ts +6 -0
  486. package/src/mcp/transports/stdio.ts +528 -0
  487. package/src/mcp/types.ts +423 -0
  488. package/src/memories/index.ts +1150 -0
  489. package/src/memories/storage.ts +577 -0
  490. package/src/memory-backend/index.ts +18 -0
  491. package/src/memory-backend/local-backend.ts +39 -0
  492. package/src/memory-backend/off-backend.ts +25 -0
  493. package/src/memory-backend/resolve.ts +25 -0
  494. package/src/memory-backend/runtime.ts +66 -0
  495. package/src/memory-backend/types.ts +166 -0
  496. package/src/mnemopi/backend.ts +547 -0
  497. package/src/mnemopi/config.ts +160 -0
  498. package/src/mnemopi/index.ts +3 -0
  499. package/src/mnemopi/state.ts +584 -0
  500. package/src/modes/acp/acp-agent.ts +2407 -0
  501. package/src/modes/acp/acp-client-bridge.ts +154 -0
  502. package/src/modes/acp/acp-event-mapper.ts +929 -0
  503. package/src/modes/acp/acp-mode.ts +23 -0
  504. package/src/modes/acp/index.ts +2 -0
  505. package/src/modes/acp/terminal-auth.ts +37 -0
  506. package/src/modes/components/agent-dashboard.ts +1206 -0
  507. package/src/modes/components/agent-hub.ts +1071 -0
  508. package/src/modes/components/assistant-message.ts +307 -0
  509. package/src/modes/components/bash-execution.ts +220 -0
  510. package/src/modes/components/bordered-loader.ts +41 -0
  511. package/src/modes/components/branch-summary-message.ts +45 -0
  512. package/src/modes/components/btw-panel.ts +104 -0
  513. package/src/modes/components/chat-block.ts +111 -0
  514. package/src/modes/components/compaction-summary-message.ts +87 -0
  515. package/src/modes/components/copy-selector.ts +206 -0
  516. package/src/modes/components/countdown-timer.ts +75 -0
  517. package/src/modes/components/custom-editor.ts +398 -0
  518. package/src/modes/components/custom-message.ts +63 -0
  519. package/src/modes/components/diff.ts +277 -0
  520. package/src/modes/components/dynamic-border.ts +34 -0
  521. package/src/modes/components/error-banner.ts +33 -0
  522. package/src/modes/components/eval-execution.ts +158 -0
  523. package/src/modes/components/execution-shared.ts +101 -0
  524. package/src/modes/components/extensions/extension-dashboard.ts +399 -0
  525. package/src/modes/components/extensions/extension-list.ts +502 -0
  526. package/src/modes/components/extensions/index.ts +9 -0
  527. package/src/modes/components/extensions/inspector-panel.ts +317 -0
  528. package/src/modes/components/extensions/state-manager.ts +627 -0
  529. package/src/modes/components/extensions/types.ts +186 -0
  530. package/src/modes/components/footer.ts +274 -0
  531. package/src/modes/components/history-search.ts +280 -0
  532. package/src/modes/components/hook-editor.ts +167 -0
  533. package/src/modes/components/hook-input.ts +87 -0
  534. package/src/modes/components/hook-message.ts +66 -0
  535. package/src/modes/components/hook-selector.ts +660 -0
  536. package/src/modes/components/index.ts +38 -0
  537. package/src/modes/components/keybinding-hints.ts +65 -0
  538. package/src/modes/components/late-diagnostics-message.ts +60 -0
  539. package/src/modes/components/login-dialog.ts +164 -0
  540. package/src/modes/components/mcp-add-wizard.ts +1340 -0
  541. package/src/modes/components/message-frame.ts +88 -0
  542. package/src/modes/components/model-selector.ts +1271 -0
  543. package/src/modes/components/oauth-selector.ts +368 -0
  544. package/src/modes/components/omfg-panel.ts +141 -0
  545. package/src/modes/components/overlay-box.ts +108 -0
  546. package/src/modes/components/plan-review-overlay.ts +820 -0
  547. package/src/modes/components/plan-toc.ts +138 -0
  548. package/src/modes/components/plugin-selector.ts +95 -0
  549. package/src/modes/components/plugin-settings.ts +722 -0
  550. package/src/modes/components/queue-mode-selector.ts +56 -0
  551. package/src/modes/components/read-tool-group.ts +670 -0
  552. package/src/modes/components/segment-track.ts +52 -0
  553. package/src/modes/components/session-selector.ts +625 -0
  554. package/src/modes/components/settings-defs.ts +189 -0
  555. package/src/modes/components/settings-selector.ts +651 -0
  556. package/src/modes/components/show-images-selector.ts +45 -0
  557. package/src/modes/components/skill-message.ts +89 -0
  558. package/src/modes/components/status-line/component.ts +869 -0
  559. package/src/modes/components/status-line/context-thresholds.ts +79 -0
  560. package/src/modes/components/status-line/git-utils.ts +42 -0
  561. package/src/modes/components/status-line/index.ts +5 -0
  562. package/src/modes/components/status-line/presets.ts +106 -0
  563. package/src/modes/components/status-line/segments.ts +584 -0
  564. package/src/modes/components/status-line/separators.ts +55 -0
  565. package/src/modes/components/status-line/token-rate.ts +66 -0
  566. package/src/modes/components/status-line/types.ts +108 -0
  567. package/src/modes/components/theme-selector.ts +63 -0
  568. package/src/modes/components/thinking-selector.ts +52 -0
  569. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  570. package/src/modes/components/tips.txt +19 -0
  571. package/src/modes/components/todo-reminder.ts +38 -0
  572. package/src/modes/components/tool-execution.ts +1024 -0
  573. package/src/modes/components/transcript-container.ts +608 -0
  574. package/src/modes/components/tree-selector.ts +978 -0
  575. package/src/modes/components/ttsr-notification.ts +122 -0
  576. package/src/modes/components/user-message-selector.ts +227 -0
  577. package/src/modes/components/user-message.ts +66 -0
  578. package/src/modes/components/visual-truncate.ts +63 -0
  579. package/src/modes/components/welcome.ts +493 -0
  580. package/src/modes/controllers/btw-controller.ts +105 -0
  581. package/src/modes/controllers/command-controller-shared.ts +109 -0
  582. package/src/modes/controllers/command-controller.ts +1566 -0
  583. package/src/modes/controllers/event-controller.ts +1054 -0
  584. package/src/modes/controllers/extension-ui-controller.ts +886 -0
  585. package/src/modes/controllers/input-controller.ts +1073 -0
  586. package/src/modes/controllers/mcp-command-controller.ts +2017 -0
  587. package/src/modes/controllers/omfg-controller.ts +283 -0
  588. package/src/modes/controllers/omfg-rule.ts +647 -0
  589. package/src/modes/controllers/selector-controller.ts +1108 -0
  590. package/src/modes/controllers/ssh-command-controller.ts +384 -0
  591. package/src/modes/controllers/streaming-reveal.ts +279 -0
  592. package/src/modes/controllers/tan-command-controller.ts +173 -0
  593. package/src/modes/controllers/todo-command-controller.ts +485 -0
  594. package/src/modes/data/emojis.json +1 -0
  595. package/src/modes/emoji-autocomplete.ts +285 -0
  596. package/src/modes/gradient-highlight.ts +87 -0
  597. package/src/modes/image-references.ts +117 -0
  598. package/src/modes/index.ts +17 -0
  599. package/src/modes/interactive-mode.ts +3370 -0
  600. package/src/modes/internal-url-autocomplete.ts +143 -0
  601. package/src/modes/loop-limit.ts +140 -0
  602. package/src/modes/magic-keywords.ts +20 -0
  603. package/src/modes/markdown-prose.ts +247 -0
  604. package/src/modes/oauth-manual-input.ts +69 -0
  605. package/src/modes/orchestrate.ts +42 -0
  606. package/src/modes/print-mode.ts +126 -0
  607. package/src/modes/prompt-action-autocomplete.ts +260 -0
  608. package/src/modes/rpc/host-tools.ts +186 -0
  609. package/src/modes/rpc/host-uris.ts +235 -0
  610. package/src/modes/rpc/rpc-client.ts +963 -0
  611. package/src/modes/rpc/rpc-mode.ts +947 -0
  612. package/src/modes/rpc/rpc-subagents.ts +265 -0
  613. package/src/modes/rpc/rpc-types.ts +458 -0
  614. package/src/modes/runtime-init.ts +116 -0
  615. package/src/modes/session-observer-registry.ts +146 -0
  616. package/src/modes/setup-version.ts +11 -0
  617. package/src/modes/setup-wizard/index.ts +99 -0
  618. package/src/modes/setup-wizard/lazy.ts +16 -0
  619. package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
  620. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  621. package/src/modes/setup-wizard/scenes/providers.ts +69 -0
  622. package/src/modes/setup-wizard/scenes/sign-in.ts +205 -0
  623. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  624. package/src/modes/setup-wizard/scenes/theme.ts +299 -0
  625. package/src/modes/setup-wizard/scenes/types.ts +48 -0
  626. package/src/modes/setup-wizard/scenes/web-search.ts +129 -0
  627. package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
  628. package/src/modes/shared.ts +47 -0
  629. package/src/modes/theme/dark.json +95 -0
  630. package/src/modes/theme/defaults/alabaster.json +93 -0
  631. package/src/modes/theme/defaults/amethyst.json +96 -0
  632. package/src/modes/theme/defaults/anthracite.json +93 -0
  633. package/src/modes/theme/defaults/basalt.json +91 -0
  634. package/src/modes/theme/defaults/birch.json +95 -0
  635. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  636. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  637. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  638. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  639. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  640. package/src/modes/theme/defaults/dark-copper.json +95 -0
  641. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  642. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  643. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  644. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  645. package/src/modes/theme/defaults/dark-ember.json +95 -0
  646. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  647. package/src/modes/theme/defaults/dark-forest.json +96 -0
  648. package/src/modes/theme/defaults/dark-github.json +105 -0
  649. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  650. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  651. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  652. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  653. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  654. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  655. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  656. package/src/modes/theme/defaults/dark-nord.json +97 -0
  657. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  658. package/src/modes/theme/defaults/dark-one.json +100 -0
  659. package/src/modes/theme/defaults/dark-poimandres.json +142 -0
  660. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  661. package/src/modes/theme/defaults/dark-reef.json +91 -0
  662. package/src/modes/theme/defaults/dark-retro.json +92 -0
  663. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  664. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  665. package/src/modes/theme/defaults/dark-slate.json +95 -0
  666. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  667. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  668. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  669. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  670. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  671. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  672. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  673. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  674. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  675. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  676. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  677. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  678. package/src/modes/theme/defaults/graphite.json +92 -0
  679. package/src/modes/theme/defaults/index.ts +199 -0
  680. package/src/modes/theme/defaults/light-arctic.json +107 -0
  681. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  682. package/src/modes/theme/defaults/light-canyon.json +91 -0
  683. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  684. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  685. package/src/modes/theme/defaults/light-coral.json +95 -0
  686. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  687. package/src/modes/theme/defaults/light-dawn.json +90 -0
  688. package/src/modes/theme/defaults/light-dunes.json +91 -0
  689. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  690. package/src/modes/theme/defaults/light-forest.json +100 -0
  691. package/src/modes/theme/defaults/light-frost.json +95 -0
  692. package/src/modes/theme/defaults/light-github.json +115 -0
  693. package/src/modes/theme/defaults/light-glacier.json +91 -0
  694. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  695. package/src/modes/theme/defaults/light-haze.json +90 -0
  696. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  697. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  698. package/src/modes/theme/defaults/light-lavender.json +95 -0
  699. package/src/modes/theme/defaults/light-meadow.json +91 -0
  700. package/src/modes/theme/defaults/light-mint.json +95 -0
  701. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  702. package/src/modes/theme/defaults/light-ocean.json +99 -0
  703. package/src/modes/theme/defaults/light-one.json +99 -0
  704. package/src/modes/theme/defaults/light-opal.json +91 -0
  705. package/src/modes/theme/defaults/light-orchard.json +91 -0
  706. package/src/modes/theme/defaults/light-paper.json +95 -0
  707. package/src/modes/theme/defaults/light-poimandres.json +142 -0
  708. package/src/modes/theme/defaults/light-prism.json +90 -0
  709. package/src/modes/theme/defaults/light-retro.json +98 -0
  710. package/src/modes/theme/defaults/light-sand.json +95 -0
  711. package/src/modes/theme/defaults/light-savanna.json +91 -0
  712. package/src/modes/theme/defaults/light-solarized.json +102 -0
  713. package/src/modes/theme/defaults/light-soleil.json +90 -0
  714. package/src/modes/theme/defaults/light-sunset.json +99 -0
  715. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  716. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  717. package/src/modes/theme/defaults/light-wetland.json +91 -0
  718. package/src/modes/theme/defaults/light-zenith.json +89 -0
  719. package/src/modes/theme/defaults/limestone.json +94 -0
  720. package/src/modes/theme/defaults/mahogany.json +97 -0
  721. package/src/modes/theme/defaults/marble.json +93 -0
  722. package/src/modes/theme/defaults/obsidian.json +91 -0
  723. package/src/modes/theme/defaults/onyx.json +91 -0
  724. package/src/modes/theme/defaults/pearl.json +93 -0
  725. package/src/modes/theme/defaults/porcelain.json +91 -0
  726. package/src/modes/theme/defaults/quartz.json +96 -0
  727. package/src/modes/theme/defaults/sandstone.json +95 -0
  728. package/src/modes/theme/defaults/titanium.json +90 -0
  729. package/src/modes/theme/light.json +93 -0
  730. package/src/modes/theme/mermaid-cache.ts +29 -0
  731. package/src/modes/theme/shimmer.ts +235 -0
  732. package/src/modes/theme/theme-schema.json +459 -0
  733. package/src/modes/theme/theme.ts +2676 -0
  734. package/src/modes/turn-budget.ts +31 -0
  735. package/src/modes/types.ts +359 -0
  736. package/src/modes/ultrathink.ts +41 -0
  737. package/src/modes/utils/context-usage.ts +339 -0
  738. package/src/modes/utils/copy-targets.ts +360 -0
  739. package/src/modes/utils/hotkeys-markdown.ts +61 -0
  740. package/src/modes/utils/keybinding-matchers.ts +51 -0
  741. package/src/modes/utils/tools-markdown.ts +27 -0
  742. package/src/modes/utils/ui-helpers.ts +801 -0
  743. package/src/modes/workflow.ts +42 -0
  744. package/src/plan-mode/approved-plan.ts +186 -0
  745. package/src/plan-mode/plan-handoff.ts +37 -0
  746. package/src/plan-mode/plan-protection.ts +31 -0
  747. package/src/plan-mode/state.ts +6 -0
  748. package/src/priority.json +41 -0
  749. package/src/prompts/agents/designer.md +66 -0
  750. package/src/prompts/agents/explore.md +58 -0
  751. package/src/prompts/agents/frontmatter.md +11 -0
  752. package/src/prompts/agents/init.md +33 -0
  753. package/src/prompts/agents/librarian.md +119 -0
  754. package/src/prompts/agents/oracle.md +55 -0
  755. package/src/prompts/agents/plan.md +48 -0
  756. package/src/prompts/agents/reviewer.md +140 -0
  757. package/src/prompts/agents/task.md +16 -0
  758. package/src/prompts/ci-green-request.md +36 -0
  759. package/src/prompts/dry-balance-bench.md +8 -0
  760. package/src/prompts/goals/goal-budget-limit.md +16 -0
  761. package/src/prompts/goals/goal-continuation.md +28 -0
  762. package/src/prompts/goals/goal-mode-active.md +23 -0
  763. package/src/prompts/memories/consolidation.md +30 -0
  764. package/src/prompts/memories/read-path.md +11 -0
  765. package/src/prompts/memories/stage_one_input.md +6 -0
  766. package/src/prompts/memories/stage_one_system.md +21 -0
  767. package/src/prompts/review-custom-request.md +22 -0
  768. package/src/prompts/review-headless-request.md +16 -0
  769. package/src/prompts/review-request.md +69 -0
  770. package/src/prompts/steering/user-interjection.md +10 -0
  771. package/src/prompts/system/agent-creation-architect.md +50 -0
  772. package/src/prompts/system/agent-creation-user.md +6 -0
  773. package/src/prompts/system/auto-continue.md +1 -0
  774. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  775. package/src/prompts/system/auto-thinking-difficulty.md +12 -0
  776. package/src/prompts/system/background-tan-dispatch.md +8 -0
  777. package/src/prompts/system/btw-user.md +8 -0
  778. package/src/prompts/system/commit-message-system.md +14 -0
  779. package/src/prompts/system/custom-system-prompt.md +64 -0
  780. package/src/prompts/system/eager-todo.md +13 -0
  781. package/src/prompts/system/empty-stop-retry.md +6 -0
  782. package/src/prompts/system/irc-incoming.md +7 -0
  783. package/src/prompts/system/manual-continue.md +7 -0
  784. package/src/prompts/system/memory-consolidation-system.md +8 -0
  785. package/src/prompts/system/memory-extraction-system.md +26 -0
  786. package/src/prompts/system/omfg-user.md +50 -0
  787. package/src/prompts/system/orchestrate-notice.md +40 -0
  788. package/src/prompts/system/plan-mode-active.md +109 -0
  789. package/src/prompts/system/plan-mode-approved.md +25 -0
  790. package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
  791. package/src/prompts/system/plan-mode-reference.md +11 -0
  792. package/src/prompts/system/plan-mode-subagent.md +33 -0
  793. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  794. package/src/prompts/system/project-prompt.md +52 -0
  795. package/src/prompts/system/subagent-system-prompt.md +64 -0
  796. package/src/prompts/system/subagent-user-prompt.md +3 -0
  797. package/src/prompts/system/subagent-yield-reminder.md +12 -0
  798. package/src/prompts/system/system-prompt.md +258 -0
  799. package/src/prompts/system/tiny-title-system.md +8 -0
  800. package/src/prompts/system/title-system.md +16 -0
  801. package/src/prompts/system/ttsr-interrupt.md +7 -0
  802. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  803. package/src/prompts/system/ultrathink-notice.md +3 -0
  804. package/src/prompts/system/web-search.md +25 -0
  805. package/src/prompts/system/workflow-notice.md +70 -0
  806. package/src/prompts/tools/apply-patch.md +65 -0
  807. package/src/prompts/tools/ask.md +30 -0
  808. package/src/prompts/tools/ast-edit.md +39 -0
  809. package/src/prompts/tools/ast-grep.md +42 -0
  810. package/src/prompts/tools/async-result.md +8 -0
  811. package/src/prompts/tools/bash.md +46 -0
  812. package/src/prompts/tools/browser.md +73 -0
  813. package/src/prompts/tools/checkpoint.md +16 -0
  814. package/src/prompts/tools/debug.md +34 -0
  815. package/src/prompts/tools/eval.md +92 -0
  816. package/src/prompts/tools/find.md +36 -0
  817. package/src/prompts/tools/github.md +21 -0
  818. package/src/prompts/tools/goal.md +18 -0
  819. package/src/prompts/tools/image-gen.md +7 -0
  820. package/src/prompts/tools/inspect-image-system.md +20 -0
  821. package/src/prompts/tools/inspect-image.md +32 -0
  822. package/src/prompts/tools/irc.md +59 -0
  823. package/src/prompts/tools/job.md +19 -0
  824. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  825. package/src/prompts/tools/lsp.md +42 -0
  826. package/src/prompts/tools/memory-edit.md +8 -0
  827. package/src/prompts/tools/patch.md +70 -0
  828. package/src/prompts/tools/read.md +84 -0
  829. package/src/prompts/tools/recall.md +5 -0
  830. package/src/prompts/tools/reflect.md +5 -0
  831. package/src/prompts/tools/render-mermaid.md +9 -0
  832. package/src/prompts/tools/replace.md +30 -0
  833. package/src/prompts/tools/resolve.md +9 -0
  834. package/src/prompts/tools/retain.md +6 -0
  835. package/src/prompts/tools/rewind.md +13 -0
  836. package/src/prompts/tools/search-tool-bm25.md +32 -0
  837. package/src/prompts/tools/search.md +24 -0
  838. package/src/prompts/tools/ssh.md +31 -0
  839. package/src/prompts/tools/task-summary.md +17 -0
  840. package/src/prompts/tools/task.md +88 -0
  841. package/src/prompts/tools/todo.md +62 -0
  842. package/src/prompts/tools/web-search.md +10 -0
  843. package/src/prompts/tools/write.md +14 -0
  844. package/src/registry/agent-lifecycle.ts +218 -0
  845. package/src/registry/agent-registry.ts +151 -0
  846. package/src/sdk.ts +2558 -0
  847. package/src/secrets/index.ts +123 -0
  848. package/src/secrets/obfuscator.ts +298 -0
  849. package/src/secrets/regex.ts +21 -0
  850. package/src/session/agent-session.ts +10121 -0
  851. package/src/session/agent-storage.ts +455 -0
  852. package/src/session/artifacts.ts +135 -0
  853. package/src/session/auth-broker-config.ts +131 -0
  854. package/src/session/auth-storage.ts +29 -0
  855. package/src/session/blob-store.ts +255 -0
  856. package/src/session/client-bridge.ts +85 -0
  857. package/src/session/history-storage.ts +348 -0
  858. package/src/session/indexed-session-storage.ts +430 -0
  859. package/src/session/messages.ts +541 -0
  860. package/src/session/redis-session-storage.ts +170 -0
  861. package/src/session/session-dump-format.ts +209 -0
  862. package/src/session/session-history-format.ts +246 -0
  863. package/src/session/session-manager.ts +3676 -0
  864. package/src/session/session-storage.ts +529 -0
  865. package/src/session/shake-types.ts +43 -0
  866. package/src/session/sql-session-storage.ts +314 -0
  867. package/src/session/streaming-output.ts +1330 -0
  868. package/src/session/tool-choice-queue.ts +213 -0
  869. package/src/session/yield-queue.ts +173 -0
  870. package/src/slash-commands/acp-builtins.ts +70 -0
  871. package/src/slash-commands/builtin-registry.ts +1798 -0
  872. package/src/slash-commands/helpers/context-report.ts +39 -0
  873. package/src/slash-commands/helpers/format.ts +46 -0
  874. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  875. package/src/slash-commands/helpers/mcp.ts +532 -0
  876. package/src/slash-commands/helpers/parse.ts +85 -0
  877. package/src/slash-commands/helpers/ssh.ts +195 -0
  878. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  879. package/src/slash-commands/helpers/todo.ts +279 -0
  880. package/src/slash-commands/helpers/usage-report.ts +95 -0
  881. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  882. package/src/slash-commands/types.ts +135 -0
  883. package/src/ssh/config-writer.ts +183 -0
  884. package/src/ssh/connection-manager.ts +509 -0
  885. package/src/ssh/ssh-executor.ts +189 -0
  886. package/src/ssh/sshfs-mount.ts +140 -0
  887. package/src/ssh/utils.ts +8 -0
  888. package/src/stt/downloader.ts +71 -0
  889. package/src/stt/index.ts +3 -0
  890. package/src/stt/recorder.ts +351 -0
  891. package/src/stt/setup.ts +52 -0
  892. package/src/stt/stt-controller.ts +160 -0
  893. package/src/stt/transcribe.py +70 -0
  894. package/src/stt/transcriber.ts +91 -0
  895. package/src/stubs/natives/index.ts +814 -0
  896. package/src/stubs/natives/package.json +7 -0
  897. package/src/stubs/tui/index.ts +282 -0
  898. package/src/stubs/tui/package.json +7 -0
  899. package/src/system-prompt.ts +611 -0
  900. package/src/task/agents.ts +167 -0
  901. package/src/task/commands.ts +132 -0
  902. package/src/task/discovery.ts +122 -0
  903. package/src/task/executor.ts +2133 -0
  904. package/src/task/index.ts +1419 -0
  905. package/src/task/name-generator.ts +1577 -0
  906. package/src/task/omp-command.ts +26 -0
  907. package/src/task/output-manager.ts +88 -0
  908. package/src/task/parallel.ts +116 -0
  909. package/src/task/render.ts +1381 -0
  910. package/src/task/repair-args.ts +129 -0
  911. package/src/task/subprocess-tool-registry.ts +88 -0
  912. package/src/task/types.ts +336 -0
  913. package/src/task/worktree.ts +514 -0
  914. package/src/telemetry-export.ts +144 -0
  915. package/src/thinking.ts +167 -0
  916. package/src/tiny/compiled-runtime.ts +179 -0
  917. package/src/tiny/device.ts +111 -0
  918. package/src/tiny/dtype.ts +101 -0
  919. package/src/tiny/models.ts +242 -0
  920. package/src/tiny/text.ts +165 -0
  921. package/src/tiny/title-client.ts +543 -0
  922. package/src/tiny/title-protocol.ts +56 -0
  923. package/src/tiny/worker.ts +568 -0
  924. package/src/tool-discovery/mode.ts +24 -0
  925. package/src/tool-discovery/tool-index.ts +256 -0
  926. package/src/tools/approval.ts +189 -0
  927. package/src/tools/archive-reader.ts +721 -0
  928. package/src/tools/ask.ts +928 -0
  929. package/src/tools/ast-edit.ts +642 -0
  930. package/src/tools/ast-grep.ts +452 -0
  931. package/src/tools/auto-generated-guard.ts +322 -0
  932. package/src/tools/bash-command-fixup.ts +37 -0
  933. package/src/tools/bash-interactive.ts +408 -0
  934. package/src/tools/bash-interceptor.ts +67 -0
  935. package/src/tools/bash-pty-selection.ts +14 -0
  936. package/src/tools/bash-skill-urls.ts +248 -0
  937. package/src/tools/bash.ts +1386 -0
  938. package/src/tools/browser/attach.ts +175 -0
  939. package/src/tools/browser/launch.ts +660 -0
  940. package/src/tools/browser/readable.ts +112 -0
  941. package/src/tools/browser/registry.ts +197 -0
  942. package/src/tools/browser/render.ts +216 -0
  943. package/src/tools/browser/tab-protocol.ts +105 -0
  944. package/src/tools/browser/tab-supervisor.ts +628 -0
  945. package/src/tools/browser/tab-worker-entry.ts +21 -0
  946. package/src/tools/browser/tab-worker.ts +1226 -0
  947. package/src/tools/browser.ts +343 -0
  948. package/src/tools/checkpoint.ts +136 -0
  949. package/src/tools/conflict-detect.ts +718 -0
  950. package/src/tools/context.ts +39 -0
  951. package/src/tools/debug.ts +1067 -0
  952. package/src/tools/eval-backends.ts +27 -0
  953. package/src/tools/eval-render.ts +752 -0
  954. package/src/tools/eval.ts +577 -0
  955. package/src/tools/fetch.ts +1926 -0
  956. package/src/tools/file-recorder.ts +35 -0
  957. package/src/tools/find.ts +609 -0
  958. package/src/tools/fs-cache-invalidation.ts +28 -0
  959. package/src/tools/gh-cache-invalidation.ts +255 -0
  960. package/src/tools/gh-format.ts +12 -0
  961. package/src/tools/gh-renderer.ts +481 -0
  962. package/src/tools/gh.ts +3720 -0
  963. package/src/tools/github-cache.ts +637 -0
  964. package/src/tools/grouped-file-output.ts +210 -0
  965. package/src/tools/image-gen.ts +1517 -0
  966. package/src/tools/index.ts +599 -0
  967. package/src/tools/inspect-image-renderer.ts +132 -0
  968. package/src/tools/inspect-image.ts +174 -0
  969. package/src/tools/irc.ts +723 -0
  970. package/src/tools/job.ts +557 -0
  971. package/src/tools/json-tree.ts +243 -0
  972. package/src/tools/jtd-to-json-schema.ts +219 -0
  973. package/src/tools/jtd-to-typescript.ts +136 -0
  974. package/src/tools/jtd-utils.ts +102 -0
  975. package/src/tools/list-limit.ts +40 -0
  976. package/src/tools/match-line-format.ts +20 -0
  977. package/src/tools/memory-edit.ts +59 -0
  978. package/src/tools/memory-recall.ts +100 -0
  979. package/src/tools/memory-reflect.ts +88 -0
  980. package/src/tools/memory-render.ts +202 -0
  981. package/src/tools/memory-retain.ts +91 -0
  982. package/src/tools/output-meta.ts +754 -0
  983. package/src/tools/output-schema-validator.ts +132 -0
  984. package/src/tools/path-utils.ts +1054 -0
  985. package/src/tools/plan-mode-guard.ts +108 -0
  986. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  987. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  988. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  989. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  990. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  991. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  992. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  993. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  994. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  995. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  996. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  997. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  998. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  999. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  1000. package/src/tools/read.ts +2929 -0
  1001. package/src/tools/render-mermaid.ts +69 -0
  1002. package/src/tools/render-utils.ts +838 -0
  1003. package/src/tools/renderers.ts +77 -0
  1004. package/src/tools/report-tool-issue.ts +534 -0
  1005. package/src/tools/resolve.ts +276 -0
  1006. package/src/tools/review.ts +253 -0
  1007. package/src/tools/search-tool-bm25.ts +351 -0
  1008. package/src/tools/search.ts +1580 -0
  1009. package/src/tools/sqlite-reader.ts +828 -0
  1010. package/src/tools/ssh.ts +349 -0
  1011. package/src/tools/todo.ts +982 -0
  1012. package/src/tools/tool-errors.ts +62 -0
  1013. package/src/tools/tool-result.ts +94 -0
  1014. package/src/tools/tool-timeouts.ts +30 -0
  1015. package/src/tools/tts.ts +133 -0
  1016. package/src/tools/write.ts +1217 -0
  1017. package/src/tools/yield.ts +269 -0
  1018. package/src/tui/code-cell.ts +216 -0
  1019. package/src/tui/file-list.ts +55 -0
  1020. package/src/tui/hyperlink.ts +175 -0
  1021. package/src/tui/index.ts +12 -0
  1022. package/src/tui/output-block.ts +240 -0
  1023. package/src/tui/status-line.ts +54 -0
  1024. package/src/tui/tree-list.ts +84 -0
  1025. package/src/tui/types.ts +15 -0
  1026. package/src/tui/utils.ts +103 -0
  1027. package/src/utils/block-context.ts +312 -0
  1028. package/src/utils/changelog.ts +132 -0
  1029. package/src/utils/clipboard.ts +193 -0
  1030. package/src/utils/command-args.ts +76 -0
  1031. package/src/utils/commit-message-generator.ts +151 -0
  1032. package/src/utils/edit-mode.ts +41 -0
  1033. package/src/utils/enhanced-paste.ts +230 -0
  1034. package/src/utils/event-bus.ts +33 -0
  1035. package/src/utils/external-editor.ts +65 -0
  1036. package/src/utils/file-display-mode.ts +45 -0
  1037. package/src/utils/file-mentions.ts +281 -0
  1038. package/src/utils/git.ts +1833 -0
  1039. package/src/utils/image-loading.ts +132 -0
  1040. package/src/utils/image-resize.ts +309 -0
  1041. package/src/utils/jj.ts +248 -0
  1042. package/src/utils/lang-from-path.ts +239 -0
  1043. package/src/utils/markit.ts +89 -0
  1044. package/src/utils/open.ts +55 -0
  1045. package/src/utils/session-color.ts +68 -0
  1046. package/src/utils/shell-snapshot.ts +187 -0
  1047. package/src/utils/sixel.ts +69 -0
  1048. package/src/utils/title-generator.ts +373 -0
  1049. package/src/utils/tool-choice.ts +33 -0
  1050. package/src/utils/tools-manager.ts +363 -0
  1051. package/src/web/kagi.ts +305 -0
  1052. package/src/web/parallel.ts +353 -0
  1053. package/src/web/scrapers/artifacthub.ts +207 -0
  1054. package/src/web/scrapers/arxiv.ts +83 -0
  1055. package/src/web/scrapers/aur.ts +162 -0
  1056. package/src/web/scrapers/biorxiv.ts +133 -0
  1057. package/src/web/scrapers/bluesky.ts +262 -0
  1058. package/src/web/scrapers/brew.ts +172 -0
  1059. package/src/web/scrapers/cheatsh.ts +68 -0
  1060. package/src/web/scrapers/chocolatey.ts +196 -0
  1061. package/src/web/scrapers/choosealicense.ts +95 -0
  1062. package/src/web/scrapers/cisa-kev.ts +87 -0
  1063. package/src/web/scrapers/clojars.ts +154 -0
  1064. package/src/web/scrapers/coingecko.ts +177 -0
  1065. package/src/web/scrapers/crates-io.ts +97 -0
  1066. package/src/web/scrapers/crossref.ts +136 -0
  1067. package/src/web/scrapers/devto.ts +147 -0
  1068. package/src/web/scrapers/discogs.ts +306 -0
  1069. package/src/web/scrapers/discourse.ts +197 -0
  1070. package/src/web/scrapers/dockerhub.ts +138 -0
  1071. package/src/web/scrapers/docs-rs.ts +653 -0
  1072. package/src/web/scrapers/fdroid.ts +134 -0
  1073. package/src/web/scrapers/firefox-addons.ts +191 -0
  1074. package/src/web/scrapers/flathub.ts +223 -0
  1075. package/src/web/scrapers/github-gist.ts +58 -0
  1076. package/src/web/scrapers/github.ts +704 -0
  1077. package/src/web/scrapers/gitlab.ts +401 -0
  1078. package/src/web/scrapers/go-pkg.ts +266 -0
  1079. package/src/web/scrapers/hackage.ts +140 -0
  1080. package/src/web/scrapers/hackernews.ts +189 -0
  1081. package/src/web/scrapers/hex.ts +105 -0
  1082. package/src/web/scrapers/huggingface.ts +321 -0
  1083. package/src/web/scrapers/iacr.ts +89 -0
  1084. package/src/web/scrapers/index.ts +252 -0
  1085. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1086. package/src/web/scrapers/lemmy.ts +203 -0
  1087. package/src/web/scrapers/lobsters.ts +175 -0
  1088. package/src/web/scrapers/mastodon.ts +292 -0
  1089. package/src/web/scrapers/maven.ts +138 -0
  1090. package/src/web/scrapers/mdn.ts +173 -0
  1091. package/src/web/scrapers/metacpan.ts +222 -0
  1092. package/src/web/scrapers/musicbrainz.ts +250 -0
  1093. package/src/web/scrapers/npm.ts +98 -0
  1094. package/src/web/scrapers/nuget.ts +183 -0
  1095. package/src/web/scrapers/nvd.ts +222 -0
  1096. package/src/web/scrapers/ollama.ts +239 -0
  1097. package/src/web/scrapers/open-vsx.ts +106 -0
  1098. package/src/web/scrapers/opencorporates.ts +292 -0
  1099. package/src/web/scrapers/openlibrary.ts +336 -0
  1100. package/src/web/scrapers/orcid.ts +286 -0
  1101. package/src/web/scrapers/osv.ts +176 -0
  1102. package/src/web/scrapers/packagist.ts +160 -0
  1103. package/src/web/scrapers/pub-dev.ts +143 -0
  1104. package/src/web/scrapers/pubmed.ts +211 -0
  1105. package/src/web/scrapers/pypi.ts +112 -0
  1106. package/src/web/scrapers/rawg.ts +110 -0
  1107. package/src/web/scrapers/readthedocs.ts +120 -0
  1108. package/src/web/scrapers/reddit.ts +95 -0
  1109. package/src/web/scrapers/repology.ts +251 -0
  1110. package/src/web/scrapers/rfc.ts +201 -0
  1111. package/src/web/scrapers/rubygems.ts +103 -0
  1112. package/src/web/scrapers/searchcode.ts +189 -0
  1113. package/src/web/scrapers/sec-edgar.ts +261 -0
  1114. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1115. package/src/web/scrapers/snapcraft.ts +187 -0
  1116. package/src/web/scrapers/sourcegraph.ts +336 -0
  1117. package/src/web/scrapers/spdx.ts +108 -0
  1118. package/src/web/scrapers/spotify.ts +198 -0
  1119. package/src/web/scrapers/stackoverflow.ts +120 -0
  1120. package/src/web/scrapers/terraform.ts +277 -0
  1121. package/src/web/scrapers/tldr.ts +47 -0
  1122. package/src/web/scrapers/twitter.ts +94 -0
  1123. package/src/web/scrapers/types.ts +397 -0
  1124. package/src/web/scrapers/utils.ts +109 -0
  1125. package/src/web/scrapers/vimeo.ts +133 -0
  1126. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1127. package/src/web/scrapers/w3c.ts +156 -0
  1128. package/src/web/scrapers/wikidata.ts +344 -0
  1129. package/src/web/scrapers/wikipedia.ts +84 -0
  1130. package/src/web/scrapers/youtube.ts +325 -0
  1131. package/src/web/search/index.ts +292 -0
  1132. package/src/web/search/provider.ts +157 -0
  1133. package/src/web/search/providers/anthropic.ts +318 -0
  1134. package/src/web/search/providers/base.ts +89 -0
  1135. package/src/web/search/providers/brave.ts +152 -0
  1136. package/src/web/search/providers/codex.ts +591 -0
  1137. package/src/web/search/providers/exa.ts +400 -0
  1138. package/src/web/search/providers/gemini.ts +460 -0
  1139. package/src/web/search/providers/jina.ts +111 -0
  1140. package/src/web/search/providers/kagi.ts +86 -0
  1141. package/src/web/search/providers/kimi.ts +196 -0
  1142. package/src/web/search/providers/parallel.ts +225 -0
  1143. package/src/web/search/providers/perplexity.ts +730 -0
  1144. package/src/web/search/providers/searxng.ts +313 -0
  1145. package/src/web/search/providers/synthetic.ts +114 -0
  1146. package/src/web/search/providers/tavily.ts +176 -0
  1147. package/src/web/search/providers/utils.ts +128 -0
  1148. package/src/web/search/providers/zai.ts +333 -0
  1149. package/src/web/search/render.ts +262 -0
  1150. package/src/web/search/types.ts +482 -0
  1151. package/src/web/search/utils.ts +17 -0
  1152. package/src/workspace-tree.ts +286 -0
@@ -0,0 +1,1193 @@
1
+ import * as path from "node:path";
2
+ import { isEnoent, logger, ptree, untilAborted } from "@oh-my-pi/pi-utils";
3
+ import { ToolAbortError, throwIfAborted } from "../tools/tool-errors";
4
+ import { applyWorkspaceEdit } from "./edits";
5
+ import { getLspmuxCommand, isLspmuxSupported } from "./lspmux";
6
+ import type {
7
+ LspClient,
8
+ LspJsonRpcNotification,
9
+ LspJsonRpcRequest,
10
+ LspJsonRpcResponse,
11
+ PublishDiagnosticsParams,
12
+ ServerConfig,
13
+ WorkspaceEdit,
14
+ } from "./types";
15
+ import { detectLanguageId, fileToUri } from "./utils";
16
+
17
+ // =============================================================================
18
+ // Client State
19
+ // =============================================================================
20
+
21
+ const clients = new Map<string, LspClient>();
22
+ const clientLocks = new Map<string, Promise<LspClient>>();
23
+ const fileOperationLocks = new Map<string, Promise<void>>();
24
+
25
+ /** Negative cache of recent init failures so a broken server fails fast instead of re-spawning per call. */
26
+ const INIT_FAILURE_BACKOFF_MS = 3 * 60 * 1000;
27
+ const initFailures = new Map<string, { at: number; message: string }>();
28
+
29
+ // Idle timeout configuration (disabled by default)
30
+ let idleTimeoutMs: number | null = null;
31
+ let idleCheckInterval: NodeJS.Timeout | null = null;
32
+ const IDLE_CHECK_INTERVAL_MS = 60 * 1000;
33
+
34
+ /**
35
+ * Configure the idle timeout for LSP clients.
36
+ * @param ms - Timeout in milliseconds, or null/undefined to disable
37
+ */
38
+ export function setIdleTimeout(ms: number | null | undefined): void {
39
+ idleTimeoutMs = ms ?? null;
40
+
41
+ if (idleTimeoutMs && idleTimeoutMs > 0) {
42
+ startIdleChecker();
43
+ } else {
44
+ stopIdleChecker();
45
+ }
46
+ }
47
+
48
+ function startIdleChecker(): void {
49
+ if (idleCheckInterval) return;
50
+ idleCheckInterval = setInterval(() => {
51
+ if (!idleTimeoutMs) return;
52
+ const now = Date.now();
53
+ for (const [key, client] of Array.from(clients.entries())) {
54
+ if (now - client.lastActivity > idleTimeoutMs) {
55
+ void shutdownClient(key);
56
+ }
57
+ }
58
+ }, IDLE_CHECK_INTERVAL_MS);
59
+ }
60
+
61
+ function stopIdleChecker(): void {
62
+ if (idleCheckInterval) {
63
+ clearInterval(idleCheckInterval);
64
+ idleCheckInterval = null;
65
+ }
66
+ }
67
+
68
+ // =============================================================================
69
+ // Client Capabilities
70
+ // =============================================================================
71
+
72
+ const CLIENT_CAPABILITIES = {
73
+ textDocument: {
74
+ synchronization: {
75
+ didSave: true,
76
+ dynamicRegistration: false,
77
+ willSave: false,
78
+ willSaveWaitUntil: false,
79
+ },
80
+ hover: {
81
+ contentFormat: ["markdown", "plaintext"],
82
+ dynamicRegistration: false,
83
+ },
84
+ definition: {
85
+ dynamicRegistration: false,
86
+ linkSupport: true,
87
+ },
88
+ typeDefinition: {
89
+ dynamicRegistration: false,
90
+ linkSupport: true,
91
+ },
92
+ implementation: {
93
+ dynamicRegistration: false,
94
+ linkSupport: true,
95
+ },
96
+ references: {
97
+ dynamicRegistration: false,
98
+ },
99
+ documentSymbol: {
100
+ dynamicRegistration: false,
101
+ hierarchicalDocumentSymbolSupport: true,
102
+ symbolKind: {
103
+ valueSet: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
104
+ },
105
+ },
106
+ rename: {
107
+ dynamicRegistration: false,
108
+ prepareSupport: true,
109
+ },
110
+ codeAction: {
111
+ dynamicRegistration: false,
112
+ codeActionLiteralSupport: {
113
+ codeActionKind: {
114
+ valueSet: [
115
+ "quickfix",
116
+ "refactor",
117
+ "refactor.extract",
118
+ "refactor.inline",
119
+ "refactor.rewrite",
120
+ "source",
121
+ "source.organizeImports",
122
+ "source.fixAll",
123
+ ],
124
+ },
125
+ },
126
+ resolveSupport: {
127
+ properties: ["edit"],
128
+ },
129
+ },
130
+ formatting: {
131
+ dynamicRegistration: false,
132
+ },
133
+ rangeFormatting: {
134
+ dynamicRegistration: false,
135
+ },
136
+ publishDiagnostics: {
137
+ relatedInformation: true,
138
+ versionSupport: true,
139
+ tagSupport: { valueSet: [1, 2] },
140
+ codeDescriptionSupport: true,
141
+ dataSupport: true,
142
+ },
143
+ },
144
+ window: {
145
+ workDoneProgress: true,
146
+ },
147
+ workspace: {
148
+ applyEdit: true,
149
+ workspaceEdit: {
150
+ documentChanges: true,
151
+ resourceOperations: ["create", "rename", "delete"],
152
+ failureHandling: "textOnlyTransactional",
153
+ },
154
+ configuration: true,
155
+ workspaceFolders: true,
156
+ symbol: {
157
+ dynamicRegistration: false,
158
+ symbolKind: {
159
+ valueSet: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
160
+ },
161
+ },
162
+ fileOperations: {
163
+ dynamicRegistration: false,
164
+ willCreate: false,
165
+ didCreate: false,
166
+ willRename: true,
167
+ didRename: true,
168
+ willDelete: false,
169
+ didDelete: false,
170
+ },
171
+ },
172
+ experimental: {
173
+ snippetTextEdit: true,
174
+ },
175
+ };
176
+
177
+ // =============================================================================
178
+ // LSP Message Protocol
179
+ // =============================================================================
180
+
181
+ // Reused for all full (non-streaming) decodes; each decode() resets state, so a
182
+ // single instance is safe and avoids per-message TextDecoder allocation.
183
+ const MESSAGE_DECODER = new TextDecoder("utf-8");
184
+
185
+ /**
186
+ * Locate the `\r\n\r\n` header terminator across the pending chunk list.
187
+ * Returns the absolute byte index of the first `\r`, or -1 when not present.
188
+ * Equivalent to scanning the contiguous concatenation of the chunks.
189
+ */
190
+ function findHeaderEndInChunks(chunks: Buffer[]): number {
191
+ let global = 0;
192
+ let b0 = -1;
193
+ let b1 = -1;
194
+ let b2 = -1;
195
+ for (const chunk of chunks) {
196
+ for (let i = 0; i < chunk.length; i++) {
197
+ const b3 = chunk[i];
198
+ if (b0 === 13 && b1 === 10 && b2 === 13 && b3 === 10) {
199
+ return global - 3;
200
+ }
201
+ b0 = b1;
202
+ b1 = b2;
203
+ b2 = b3;
204
+ global++;
205
+ }
206
+ }
207
+ return -1;
208
+ }
209
+
210
+ /** Copy the byte range [from, to) out of the pending chunk list into one Buffer. */
211
+ function copyChunkRange(chunks: Buffer[], from: number, to: number): Buffer {
212
+ const out = Buffer.allocUnsafe(to - from);
213
+ let global = 0;
214
+ let written = 0;
215
+ for (const chunk of chunks) {
216
+ const chunkEnd = global + chunk.length;
217
+ if (chunkEnd > from && global < to) {
218
+ const start = Math.max(from, global) - global;
219
+ const end = Math.min(to, chunkEnd) - global;
220
+ chunk.copy(out, written, start, end);
221
+ written += end - start;
222
+ }
223
+ global = chunkEnd;
224
+ if (global >= to) break;
225
+ }
226
+ return out;
227
+ }
228
+
229
+ /** Drop the first `count` bytes from the pending chunk list in place. */
230
+ function dropChunkFront(chunks: Buffer[], count: number): void {
231
+ let removed = 0;
232
+ while (chunks.length > 0) {
233
+ const head = chunks[0];
234
+ if (removed + head.length <= count) {
235
+ removed += head.length;
236
+ chunks.shift();
237
+ } else {
238
+ chunks[0] = head.subarray(count - removed);
239
+ break;
240
+ }
241
+ }
242
+ }
243
+
244
+ async function writeMessage(
245
+ sink: Bun.FileSink,
246
+ message: LspJsonRpcRequest | LspJsonRpcNotification | LspJsonRpcResponse,
247
+ ): Promise<void> {
248
+ const content = JSON.stringify(message);
249
+ sink.write(`Content-Length: ${Buffer.byteLength(content, "utf-8")}\r\n\r\n${content}`);
250
+ await sink.flush();
251
+ }
252
+
253
+ function queueWriteMessage(
254
+ client: LspClient,
255
+ message: LspJsonRpcRequest | LspJsonRpcNotification | LspJsonRpcResponse,
256
+ ): Promise<void> {
257
+ const write = client.writeQueue.catch(() => {}).then(() => writeMessage(client.proc.stdin, message));
258
+ client.writeQueue = write.catch(() => {});
259
+ return write;
260
+ }
261
+
262
+ // =============================================================================
263
+ // Message Reader
264
+ // =============================================================================
265
+
266
+ /**
267
+ * Start background message reader for a client.
268
+ * Routes responses to pending requests and handles notifications.
269
+ */
270
+ async function startMessageReader(client: LspClient): Promise<void> {
271
+ if (client.isReading) return;
272
+ client.isReading = true;
273
+
274
+ const reader = (client.proc.stdout as ReadableStream<Uint8Array>).getReader();
275
+
276
+ // Incoming bytes are buffered as a list of chunks and only joined when a full
277
+ // message is framed. Concatenating the accumulator on every read was O(n^2)
278
+ // for messages that span many reads (e.g. a large initial diagnostics burst).
279
+ const pendingChunks: Buffer[] = [];
280
+ let pendingLen = 0;
281
+ if (client.messageBuffer.length > 0) {
282
+ const seed = Buffer.from(client.messageBuffer);
283
+ pendingChunks.push(seed);
284
+ pendingLen = seed.length;
285
+ }
286
+
287
+ try {
288
+ while (true) {
289
+ const { done, value } = await reader.read();
290
+ if (done) break;
291
+
292
+ pendingChunks.push(Buffer.from(value));
293
+ pendingLen += value.length;
294
+
295
+ // Drain every complete message currently buffered.
296
+ while (true) {
297
+ const headerEnd = findHeaderEndInChunks(pendingChunks);
298
+ if (headerEnd === -1) break;
299
+
300
+ const headerText = MESSAGE_DECODER.decode(copyChunkRange(pendingChunks, 0, headerEnd));
301
+ const contentLengthMatch = headerText.match(/Content-Length: (\d+)/i);
302
+ if (!contentLengthMatch) {
303
+ // Non-protocol bytes on stdout (e.g. a wrapper script printing).
304
+ // Drop past the bogus terminator and resync instead of stalling
305
+ // on the same junk header forever.
306
+ logger.warn("LSP framing resync: header block without Content-Length", {
307
+ server: client.name,
308
+ header: headerText.slice(0, 200),
309
+ });
310
+ dropChunkFront(pendingChunks, headerEnd + 4);
311
+ pendingLen -= headerEnd + 4;
312
+ continue;
313
+ }
314
+
315
+ const contentLength = Number.parseInt(contentLengthMatch[1], 10);
316
+ const messageStart = headerEnd + 4; // Skip \r\n\r\n
317
+ const messageEnd = messageStart + contentLength;
318
+ if (pendingLen < messageEnd) break;
319
+
320
+ const messageText = MESSAGE_DECODER.decode(copyChunkRange(pendingChunks, messageStart, messageEnd));
321
+ dropChunkFront(pendingChunks, messageEnd);
322
+ pendingLen -= messageEnd;
323
+
324
+ // A malformed message or a throwing server-request handler must not
325
+ // kill the reader — later messages are still well-framed.
326
+ try {
327
+ const message: LspJsonRpcResponse | LspJsonRpcNotification = JSON.parse(messageText);
328
+
329
+ // Route message
330
+ if ("id" in message && message.id !== undefined) {
331
+ // Response to a request
332
+ const pending = client.pendingRequests.get(message.id);
333
+ if (pending) {
334
+ client.pendingRequests.delete(message.id);
335
+ if ("error" in message && message.error) {
336
+ pending.reject(new Error(`LSP error: ${message.error.message}`));
337
+ } else {
338
+ pending.resolve(message.result);
339
+ }
340
+ } else if ("method" in message) {
341
+ await handleServerRequest(client, message as LspJsonRpcRequest);
342
+ }
343
+ } else if ("method" in message) {
344
+ // Server notification
345
+ if (message.method === "textDocument/publishDiagnostics" && message.params) {
346
+ const params = message.params as PublishDiagnosticsParams;
347
+ client.diagnostics.set(params.uri, {
348
+ diagnostics: params.diagnostics,
349
+ version: params.version ?? null,
350
+ });
351
+ client.diagnosticsVersion += 1;
352
+ } else if (message.method === "$/progress" && message.params) {
353
+ const params = message.params as { token: string | number; value?: { kind?: string } };
354
+ if (params.value?.kind === "begin") {
355
+ client.activeProgressTokens.add(params.token);
356
+ } else if (params.value?.kind === "end") {
357
+ client.activeProgressTokens.delete(params.token);
358
+ if (client.activeProgressTokens.size === 0) {
359
+ client.resolveProjectLoaded();
360
+ }
361
+ }
362
+ }
363
+ }
364
+ } catch (err) {
365
+ logger.warn("LSP message handling failed", {
366
+ server: client.name,
367
+ error: err instanceof Error ? err.message : String(err),
368
+ });
369
+ }
370
+ }
371
+ }
372
+ } catch (err) {
373
+ // Connection closed or error - reject all pending requests
374
+ for (const pending of Array.from(client.pendingRequests.values())) {
375
+ pending.reject(new Error(`LSP connection closed: ${err}`));
376
+ }
377
+ client.pendingRequests.clear();
378
+ } finally {
379
+ // Persist any unparsed remainder so a restarted reader resumes mid-message.
380
+ client.messageBuffer =
381
+ pendingChunks.length === 0
382
+ ? new Uint8Array(0)
383
+ : pendingChunks.length === 1
384
+ ? pendingChunks[0]
385
+ : Buffer.concat(pendingChunks, pendingLen);
386
+ reader.releaseLock();
387
+ client.isReading = false;
388
+ // Reader exited while the server process is still alive (unrecoverable
389
+ // read error or bad stream state): nothing will route responses anymore,
390
+ // so tear the client down — the next call respawns instead of timing out.
391
+ if (client.proc.exitCode === null) {
392
+ client.status = "error";
393
+ if (clients.get(client.name) === client) {
394
+ clients.delete(client.name);
395
+ }
396
+ const teardownErr = new Error("LSP reader stopped; client torn down");
397
+ for (const pending of client.pendingRequests.values()) {
398
+ pending.reject(teardownErr);
399
+ }
400
+ client.pendingRequests.clear();
401
+ client.resolveProjectLoaded();
402
+ client.proc.kill();
403
+ }
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Build the workspace folder list advertised to the server. Identical shape
409
+ * for `initialize` params and `workspace/workspaceFolders` server requests.
410
+ */
411
+ function currentWorkspaceFolders(client: LspClient): Array<{ uri: string; name: string }> {
412
+ return [{ uri: fileToUri(client.cwd), name: path.basename(client.cwd) || "workspace" }];
413
+ }
414
+
415
+ /**
416
+ * Handle workspace/workspaceFolders requests from the server.
417
+ */
418
+ async function handleWorkspaceFoldersRequest(client: LspClient, message: LspJsonRpcRequest): Promise<void> {
419
+ if (typeof message.id !== "number") return;
420
+ await sendResponse(client, message.id, currentWorkspaceFolders(client), "workspace/workspaceFolders");
421
+ }
422
+
423
+ /**
424
+ * Handle workspace/configuration requests from the server.
425
+ */
426
+ async function handleConfigurationRequest(client: LspClient, message: LspJsonRpcRequest): Promise<void> {
427
+ if (typeof message.id !== "number") return;
428
+ const params = message.params as { items?: Array<{ section?: string }> };
429
+ const items = params?.items ?? [];
430
+ const result = items.map(item => {
431
+ const section = item.section ?? "";
432
+ return client.config.settings?.[section] ?? {};
433
+ });
434
+ await sendResponse(client, message.id, result, "workspace/configuration");
435
+ }
436
+
437
+ /**
438
+ * Handle workspace/applyEdit requests from the server.
439
+ */
440
+ async function handleApplyEditRequest(client: LspClient, message: LspJsonRpcRequest): Promise<void> {
441
+ if (typeof message.id !== "number") return;
442
+ const params = message.params as { edit?: WorkspaceEdit };
443
+ if (!params?.edit) {
444
+ await sendResponse(
445
+ client,
446
+ message.id,
447
+ { applied: false, failureReason: "No edit provided" },
448
+ "workspace/applyEdit",
449
+ );
450
+ return;
451
+ }
452
+
453
+ try {
454
+ await applyWorkspaceEdit(params.edit, client.cwd);
455
+ await sendResponse(client, message.id, { applied: true }, "workspace/applyEdit");
456
+ } catch (err) {
457
+ await sendResponse(client, message.id, { applied: false, failureReason: String(err) }, "workspace/applyEdit");
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Respond to a server-initiated request.
463
+ */
464
+ async function handleServerRequest(client: LspClient, message: LspJsonRpcRequest): Promise<void> {
465
+ if (message.method === "workspace/configuration") {
466
+ await handleConfigurationRequest(client, message);
467
+ return;
468
+ }
469
+ if (message.method === "workspace/workspaceFolders") {
470
+ await handleWorkspaceFoldersRequest(client, message);
471
+ return;
472
+ }
473
+ if (message.method === "workspace/applyEdit") {
474
+ await handleApplyEditRequest(client, message);
475
+ return;
476
+ }
477
+ if (message.method === "window/workDoneProgress/create") {
478
+ // Accept progress token registration from the server
479
+ if (typeof message.id === "number") {
480
+ await sendResponse(client, message.id, null, message.method);
481
+ }
482
+ return;
483
+ }
484
+ if (typeof message.id !== "number") return;
485
+ await sendResponse(client, message.id, null, message.method, {
486
+ code: -32601,
487
+ message: `Method not found: ${message.method}`,
488
+ });
489
+ }
490
+
491
+ /**
492
+ * Send an LSP response to the server.
493
+ */
494
+ async function sendResponse(
495
+ client: LspClient,
496
+ id: number,
497
+ result: unknown,
498
+ method: string,
499
+ error?: { code: number; message: string; data?: unknown },
500
+ ): Promise<void> {
501
+ const response: LspJsonRpcResponse = {
502
+ jsonrpc: "2.0",
503
+ id,
504
+ ...(error ? { error } : { result }),
505
+ };
506
+
507
+ try {
508
+ await queueWriteMessage(client, response);
509
+ } catch (err) {
510
+ logger.error("LSP failed to respond.", { method, error: String(err) });
511
+ }
512
+ }
513
+
514
+ // =============================================================================
515
+ // Client Management
516
+ // =============================================================================
517
+
518
+ /** Timeout for warmup initialize requests (5 seconds) */
519
+ export const WARMUP_TIMEOUT_MS = 5000;
520
+
521
+ /** Max time to poll rust-analyzer after progress ends but before Cargo workspaces are ready. */
522
+ const RUST_ANALYZER_WORKSPACE_READY_TIMEOUT_MS = 5_000;
523
+ const RUST_ANALYZER_WORKSPACE_READY_POLL_MS = 100;
524
+ const RUST_ANALYZER_WORKSPACE_READY_SETTLE_MS = 2_000;
525
+ const RUST_ANALYZER_STATUS_REQUEST_TIMEOUT_MS = 1_000;
526
+ const rustAnalyzerReadyClients = new WeakSet<LspClient>();
527
+
528
+ function commandBasename(command: string): string {
529
+ const slash = command.lastIndexOf("/");
530
+ const backslash = command.lastIndexOf("\\");
531
+ const separator = Math.max(slash, backslash);
532
+ return separator === -1 ? command : command.slice(separator + 1);
533
+ }
534
+
535
+ function isRustAnalyzerClient(client: LspClient): boolean {
536
+ return (
537
+ commandBasename(client.config.command) === "rust-analyzer" ||
538
+ (client.config.resolvedCommand ? commandBasename(client.config.resolvedCommand) === "rust-analyzer" : false)
539
+ );
540
+ }
541
+
542
+ function isRustAnalyzerStatusTimeout(err: unknown): boolean {
543
+ return err instanceof Error && err.message.startsWith("LSP request rust-analyzer/analyzerStatus timed out after ");
544
+ }
545
+
546
+ async function waitForRustAnalyzerWorkspace(client: LspClient, signal?: AbortSignal): Promise<void> {
547
+ if (rustAnalyzerReadyClients.has(client)) {
548
+ return;
549
+ }
550
+ const timings = client.config.workspaceReadyTimings;
551
+ const timeoutMs = timings?.timeoutMs ?? RUST_ANALYZER_WORKSPACE_READY_TIMEOUT_MS;
552
+ const pollMs = timings?.pollMs ?? RUST_ANALYZER_WORKSPACE_READY_POLL_MS;
553
+ const settleMs = timings?.settleMs ?? RUST_ANALYZER_WORKSPACE_READY_SETTLE_MS;
554
+ const statusRequestTimeoutMs = timings?.statusRequestTimeoutMs ?? RUST_ANALYZER_STATUS_REQUEST_TIMEOUT_MS;
555
+ const started = Date.now();
556
+ const deadline = started + timeoutMs;
557
+ while (true) {
558
+ throwIfAborted(signal);
559
+ let status: unknown;
560
+ try {
561
+ status = await sendRequest(client, "rust-analyzer/analyzerStatus", {}, signal, statusRequestTimeoutMs);
562
+ } catch (err) {
563
+ if (!isRustAnalyzerStatusTimeout(err) || Date.now() >= deadline) {
564
+ return;
565
+ }
566
+ await Bun.sleep(pollMs);
567
+ continue;
568
+ }
569
+ const ready = typeof status === "string" && !status.startsWith("No workspaces");
570
+ if (ready && Date.now() - started >= settleMs) {
571
+ rustAnalyzerReadyClients.add(client);
572
+ return;
573
+ }
574
+ if (Date.now() >= deadline) {
575
+ return;
576
+ }
577
+ await Bun.sleep(pollMs);
578
+ }
579
+ }
580
+
581
+ const PROJECT_LOAD_TIMEOUT_MS = 15_000;
582
+
583
+ /** Max time to wait for graceful LSP shutdown and process exit. */
584
+ const SHUTDOWN_TIMEOUT_MS = 5_000;
585
+ const EXIT_TIMEOUT_MS = 1_000;
586
+
587
+ /**
588
+ * Get or create an LSP client for the given server configuration and working directory.
589
+ * @param config - Server configuration
590
+ * @param cwd - Working directory
591
+ * @param initTimeoutMs - Optional timeout for the initialize request (defaults to 30s)
592
+ */
593
+ export async function getOrCreateClient(config: ServerConfig, cwd: string, initTimeoutMs?: number): Promise<LspClient> {
594
+ const key = `${config.command}:${cwd}`;
595
+
596
+ // Check if client already exists
597
+ const existingClient = clients.get(key);
598
+ if (existingClient) {
599
+ existingClient.lastActivity = Date.now();
600
+ return existingClient;
601
+ }
602
+
603
+ // Check if another coroutine is already creating this client
604
+ const existingLock = clientLocks.get(key);
605
+ if (existingLock) {
606
+ return existingLock;
607
+ }
608
+
609
+ // Fail fast on a recent deterministic init failure instead of re-spawning
610
+ // a broken server (and paying its full init wait) on every call.
611
+ const recentFailure = initFailures.get(key);
612
+ if (recentFailure) {
613
+ if (Date.now() - recentFailure.at < INIT_FAILURE_BACKOFF_MS) {
614
+ throw new Error(`LSP server ${config.command} failed to initialize recently: ${recentFailure.message}`);
615
+ }
616
+ initFailures.delete(key);
617
+ }
618
+
619
+ // Create new client with lock
620
+ const clientPromise = (async () => {
621
+ const baseCommand = config.resolvedCommand ?? config.command;
622
+ const baseArgs = config.args ?? [];
623
+
624
+ // Wrap with lspmux if available and supported
625
+ const { command, args, env } = isLspmuxSupported(baseCommand)
626
+ ? await getLspmuxCommand(baseCommand, baseArgs)
627
+ : { command: baseCommand, args: baseArgs };
628
+
629
+ const proc = ptree.spawn([command, ...args], {
630
+ cwd,
631
+ stdin: "pipe",
632
+ env: env ? { ...Bun.env, ...env } : undefined,
633
+ });
634
+
635
+ let resolveProjectLoaded!: () => void;
636
+ const projectLoaded = new Promise<void>(resolve => {
637
+ resolveProjectLoaded = resolve;
638
+ });
639
+ // Auto-resolve after timeout in case server doesn't use progress tokens
640
+ const projectLoadTimeout = setTimeout(resolveProjectLoaded, PROJECT_LOAD_TIMEOUT_MS);
641
+ const originalResolve = resolveProjectLoaded;
642
+ resolveProjectLoaded = () => {
643
+ clearTimeout(projectLoadTimeout);
644
+ originalResolve();
645
+ };
646
+
647
+ const client: LspClient = {
648
+ name: key,
649
+ cwd,
650
+ proc,
651
+ config,
652
+ requestId: 0,
653
+ diagnostics: new Map(),
654
+ diagnosticsVersion: 0,
655
+ openFiles: new Map(),
656
+ pendingRequests: new Map(),
657
+ messageBuffer: new Uint8Array(0),
658
+ isReading: false,
659
+ status: "connecting",
660
+ lastActivity: Date.now(),
661
+ writeQueue: Promise.resolve(),
662
+ activeProgressTokens: new Set(),
663
+ projectLoaded,
664
+ resolveProjectLoaded,
665
+ };
666
+
667
+ // Register crash recovery - remove client on process exit
668
+ proc.exited.then(() => {
669
+ if (clients.get(key) === client) clients.delete(key);
670
+ if (clientLocks.get(key) === clientPromise) clientLocks.delete(key);
671
+ client.resolveProjectLoaded();
672
+
673
+ // Reject any pending requests — the server is gone, they will never complete.
674
+ if (client.pendingRequests.size > 0) {
675
+ // Strip informational log lines (e.g. marksman's [INF]/[DBG] prefix)
676
+ // — they are startup noise, not actionable errors.
677
+ const rawStderr = proc.peekStderr().trim();
678
+ const stderr = rawStderr
679
+ .split("\n")
680
+ .filter(line => !/^\[\d{2}:\d{2}:\d{2} (?:INF|DBG|VRB)\]/.test(line))
681
+ .join("\n")
682
+ .trim();
683
+ const code = proc.exitCode;
684
+ const err = new Error(
685
+ stderr ? `LSP server exited (code ${code}): ${stderr}` : `LSP server exited unexpectedly (code ${code})`,
686
+ );
687
+ for (const pending of client.pendingRequests.values()) {
688
+ pending.reject(err);
689
+ }
690
+ client.pendingRequests.clear();
691
+ }
692
+ });
693
+
694
+ // Start background message reader
695
+ startMessageReader(client);
696
+
697
+ try {
698
+ // Send initialize request
699
+ const initResult = (await sendRequest(
700
+ client,
701
+ "initialize",
702
+ {
703
+ processId: process.pid,
704
+ rootUri: fileToUri(cwd),
705
+ rootPath: cwd,
706
+ capabilities: CLIENT_CAPABILITIES,
707
+ initializationOptions: config.initOptions ?? {},
708
+ workspaceFolders: currentWorkspaceFolders(client),
709
+ },
710
+ undefined, // signal
711
+ initTimeoutMs,
712
+ )) as { capabilities?: unknown };
713
+
714
+ if (!initResult) {
715
+ throw new Error("Failed to initialize LSP: no response");
716
+ }
717
+
718
+ client.serverCapabilities = initResult.capabilities as LspClient["serverCapabilities"];
719
+
720
+ // Send initialized notification
721
+ await sendNotification(client, "initialized", {});
722
+
723
+ client.status = "ready";
724
+ // Publish only after init succeeds: pre-init clients are reachable
725
+ // solely through clientLocks, so concurrent callers (warmup vs first
726
+ // tool call) wait for init instead of using an unacknowledged client.
727
+ clients.set(key, client);
728
+ initFailures.delete(key);
729
+ return client;
730
+ } catch (err) {
731
+ // Clean up on initialization failure
732
+ client.status = "error";
733
+ if (clients.get(key) === client) clients.delete(key);
734
+ proc.kill();
735
+ const message = err instanceof Error ? err.message : String(err);
736
+ // Negative-cache deterministic failures. Timeouts under a
737
+ // caller-shortened deadline (warmup/writethrough) are not cached —
738
+ // the server may simply be slow and a later call with the full
739
+ // deadline can still succeed.
740
+ if (!(initTimeoutMs !== undefined && message.includes("timed out"))) {
741
+ initFailures.set(key, { at: Date.now(), message });
742
+ }
743
+ throw err;
744
+ } finally {
745
+ clientLocks.delete(key);
746
+ }
747
+ })();
748
+
749
+ clientLocks.set(key, clientPromise);
750
+ return clientPromise;
751
+ }
752
+
753
+ /**
754
+ * Ensure a file is opened in the LSP client.
755
+ * Sends didOpen notification if the file is not already tracked.
756
+ */
757
+ export async function ensureFileOpen(client: LspClient, filePath: string, signal?: AbortSignal): Promise<void> {
758
+ throwIfAborted(signal);
759
+ const uri = fileToUri(filePath);
760
+ const lockKey = `${client.name}:${uri}`;
761
+
762
+ // Check if file is already open
763
+ if (client.openFiles.has(uri)) {
764
+ return;
765
+ }
766
+
767
+ // Check if another operation is already opening this file
768
+ const existingLock = fileOperationLocks.get(lockKey);
769
+ if (existingLock) {
770
+ await untilAborted(signal, () => existingLock);
771
+ return;
772
+ }
773
+
774
+ // Lock and open file
775
+ const openPromise = (async () => {
776
+ throwIfAborted(signal);
777
+ // Double-check after acquiring lock
778
+ if (client.openFiles.has(uri)) {
779
+ return;
780
+ }
781
+
782
+ let content: string;
783
+ try {
784
+ content = await Bun.file(filePath).text();
785
+ throwIfAborted(signal);
786
+ } catch (err) {
787
+ if (isEnoent(err)) return;
788
+ throw err;
789
+ }
790
+ const languageId = detectLanguageId(filePath);
791
+ throwIfAborted(signal);
792
+
793
+ await sendNotification(client, "textDocument/didOpen", {
794
+ textDocument: {
795
+ uri,
796
+ languageId,
797
+ version: 1,
798
+ text: content,
799
+ },
800
+ });
801
+
802
+ client.openFiles.set(uri, { version: 1, languageId });
803
+ client.lastActivity = Date.now();
804
+ })();
805
+
806
+ fileOperationLocks.set(lockKey, openPromise);
807
+ try {
808
+ await openPromise;
809
+ } finally {
810
+ fileOperationLocks.delete(lockKey);
811
+ }
812
+ }
813
+
814
+ /**
815
+ * Wait for the server's initial project loading to complete.
816
+ * Races the server's $/progress tracking against the abort signal.
817
+ * Returns immediately if loading already completed or timed out.
818
+ */
819
+ export async function waitForProjectLoaded(client: LspClient, signal?: AbortSignal): Promise<void> {
820
+ if (signal?.aborted) return;
821
+ await Promise.race([
822
+ client.projectLoaded,
823
+ ...(signal
824
+ ? [new Promise<void>(resolve => signal.addEventListener("abort", () => resolve(), { once: true }))]
825
+ : []),
826
+ ]);
827
+ if (isRustAnalyzerClient(client)) {
828
+ await waitForRustAnalyzerWorkspace(client, signal);
829
+ }
830
+ }
831
+
832
+ /**
833
+ * Sync in-memory content to the LSP client without reading from disk.
834
+ * Use this to provide instant feedback during edits before the file is saved.
835
+ */
836
+ export async function syncContent(
837
+ client: LspClient,
838
+ filePath: string,
839
+ content: string,
840
+ signal?: AbortSignal,
841
+ ): Promise<void> {
842
+ const uri = fileToUri(filePath);
843
+ const lockKey = `${client.name}:${uri}`;
844
+ throwIfAborted(signal);
845
+
846
+ const existingLock = fileOperationLocks.get(lockKey);
847
+ if (existingLock) {
848
+ await untilAborted(signal, () => existingLock);
849
+ }
850
+
851
+ const syncPromise = (async () => {
852
+ // Clear stale diagnostics before syncing new content
853
+ client.diagnostics.delete(uri);
854
+
855
+ const info = client.openFiles.get(uri);
856
+
857
+ if (!info) {
858
+ // Open file with provided content instead of reading from disk
859
+ const languageId = detectLanguageId(filePath);
860
+ throwIfAborted(signal);
861
+ await sendNotification(client, "textDocument/didOpen", {
862
+ textDocument: {
863
+ uri,
864
+ languageId,
865
+ version: 1,
866
+ text: content,
867
+ },
868
+ });
869
+ client.openFiles.set(uri, { version: 1, languageId });
870
+ client.lastActivity = Date.now();
871
+ return;
872
+ }
873
+
874
+ const version = ++info.version;
875
+ throwIfAborted(signal);
876
+ await sendNotification(client, "textDocument/didChange", {
877
+ textDocument: { uri, version },
878
+ contentChanges: [{ text: content }],
879
+ });
880
+ client.lastActivity = Date.now();
881
+ })();
882
+
883
+ fileOperationLocks.set(lockKey, syncPromise);
884
+ try {
885
+ await syncPromise;
886
+ } finally {
887
+ fileOperationLocks.delete(lockKey);
888
+ }
889
+ }
890
+
891
+ /**
892
+ * Notify LSP that a file was saved.
893
+ * Assumes content was already synced via syncContent - just sends didSave.
894
+ */
895
+ export async function notifySaved(client: LspClient, filePath: string, signal?: AbortSignal): Promise<void> {
896
+ const uri = fileToUri(filePath);
897
+ const info = client.openFiles.get(uri);
898
+ if (!info) return; // File not open, nothing to notify
899
+
900
+ throwIfAborted(signal);
901
+ await sendNotification(client, "textDocument/didSave", {
902
+ textDocument: { uri },
903
+ });
904
+ client.lastActivity = Date.now();
905
+ }
906
+
907
+ /**
908
+ * Refresh a file in the LSP client.
909
+ * Increments version, sends didChange and didSave notifications.
910
+ */
911
+ export async function refreshFile(client: LspClient, filePath: string, signal?: AbortSignal): Promise<void> {
912
+ throwIfAborted(signal);
913
+ const uri = fileToUri(filePath);
914
+ const lockKey = `${client.name}:${uri}`;
915
+
916
+ const existingLock = fileOperationLocks.get(lockKey);
917
+ if (existingLock) {
918
+ await untilAborted(signal, () => existingLock);
919
+ }
920
+
921
+ const refreshPromise = (async () => {
922
+ throwIfAborted(signal);
923
+ // Drop cached diagnostics for this URI before asking the server to recompute.
924
+ // Otherwise an unrelated publishDiagnostics notification can advance the global
925
+ // diagnostics version and cause waiters to accept stale unversioned diagnostics.
926
+ client.diagnostics.delete(uri);
927
+ const info = client.openFiles.get(uri);
928
+
929
+ if (!info) {
930
+ await ensureFileOpen(client, filePath, signal);
931
+ return;
932
+ }
933
+
934
+ let content: string;
935
+ try {
936
+ content = await Bun.file(filePath).text();
937
+ throwIfAborted(signal);
938
+ } catch (err) {
939
+ if (isEnoent(err)) return;
940
+ throw err;
941
+ }
942
+ const version = ++info.version;
943
+ throwIfAborted(signal);
944
+
945
+ await sendNotification(client, "textDocument/didChange", {
946
+ textDocument: { uri, version },
947
+ contentChanges: [{ text: content }],
948
+ });
949
+ throwIfAborted(signal);
950
+
951
+ await sendNotification(client, "textDocument/didSave", {
952
+ textDocument: { uri },
953
+ text: content,
954
+ });
955
+
956
+ client.lastActivity = Date.now();
957
+ })();
958
+
959
+ fileOperationLocks.set(lockKey, refreshPromise);
960
+ try {
961
+ await refreshPromise;
962
+ } finally {
963
+ fileOperationLocks.delete(lockKey);
964
+ }
965
+ }
966
+
967
+ async function waitForExit(client: LspClient, timeoutMs: number): Promise<boolean> {
968
+ return await Promise.race([
969
+ client.proc.exited.then(
970
+ () => true,
971
+ () => true,
972
+ ),
973
+ Bun.sleep(timeoutMs).then(() => false),
974
+ ]);
975
+ }
976
+
977
+ /**
978
+ * Shutdown a specific client instance using the LSP shutdown/exit handshake.
979
+ */
980
+ async function shutdownClientInstance(client: LspClient): Promise<void> {
981
+ const err = new Error("LSP client shutdown");
982
+ for (const pending of Array.from(client.pendingRequests.values())) {
983
+ pending.reject(err);
984
+ }
985
+ client.pendingRequests.clear();
986
+
987
+ const shutdownCompleted = await sendRequest(client, "shutdown", null, undefined, SHUTDOWN_TIMEOUT_MS).then(
988
+ () => true,
989
+ () => false,
990
+ );
991
+ if (shutdownCompleted) {
992
+ await sendNotification(client, "exit", undefined).catch(() => {});
993
+ if (await waitForExit(client, EXIT_TIMEOUT_MS)) return;
994
+ }
995
+
996
+ client.proc.kill();
997
+ await waitForExit(client, EXIT_TIMEOUT_MS);
998
+ }
999
+
1000
+ /**
1001
+ * Shutdown a specific client by key.
1002
+ */
1003
+ export async function shutdownClient(key: string): Promise<void> {
1004
+ const client = clients.get(key);
1005
+ if (!client) return;
1006
+ clients.delete(key);
1007
+ await shutdownClientInstance(client);
1008
+ }
1009
+
1010
+ // =============================================================================
1011
+ // LSP Protocol Methods
1012
+ // =============================================================================
1013
+
1014
+ /** Default timeout for LSP requests when no abort signal is provided (30 seconds) */
1015
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
1016
+
1017
+ /**
1018
+ * Send an LSP request and wait for response.
1019
+ *
1020
+ * Timeout policy:
1021
+ * - If `timeoutMs` is explicitly provided, that value is used.
1022
+ * - Else, if `signal` is provided, no internal timer is installed (the caller
1023
+ * owns the deadline via the signal — typically a wall-clock `AbortSignal.timeout`
1024
+ * from the LSP tool). Installing a second hard-coded 30s timer here used to
1025
+ * cause "timed out after 30000ms" errors even when the caller had requested
1026
+ * `timeout: 60`.
1027
+ * - Else (no signal, no explicit timeout), fall back to `DEFAULT_REQUEST_TIMEOUT_MS`
1028
+ * to avoid leaking pending requests forever.
1029
+ */
1030
+ export async function sendRequest(
1031
+ client: LspClient,
1032
+ method: string,
1033
+ params: unknown,
1034
+ signal?: AbortSignal,
1035
+ timeoutMs?: number,
1036
+ ): Promise<unknown> {
1037
+ // Atomically increment and capture request ID
1038
+ const id = ++client.requestId;
1039
+ if (signal?.aborted) {
1040
+ const reason = signal.reason instanceof Error ? signal.reason : new ToolAbortError();
1041
+ return Promise.reject(reason);
1042
+ }
1043
+
1044
+ const request: LspJsonRpcRequest = {
1045
+ jsonrpc: "2.0",
1046
+ id,
1047
+ method,
1048
+ params,
1049
+ };
1050
+
1051
+ client.lastActivity = Date.now();
1052
+
1053
+ const { promise, resolve, reject } = Promise.withResolvers<unknown>();
1054
+ let timeout: NodeJS.Timeout | undefined;
1055
+ const cleanup = () => {
1056
+ if (signal) {
1057
+ signal.removeEventListener("abort", abortHandler);
1058
+ }
1059
+ };
1060
+ const abortHandler = () => {
1061
+ if (client.pendingRequests.has(id)) {
1062
+ client.pendingRequests.delete(id);
1063
+ }
1064
+ void sendNotification(client, "$/cancelRequest", { id }).catch(() => {});
1065
+ if (timeout) clearTimeout(timeout);
1066
+ cleanup();
1067
+ const reason = signal?.reason instanceof Error ? signal.reason : new ToolAbortError();
1068
+ reject(reason);
1069
+ };
1070
+
1071
+ const effectiveTimeoutMs = timeoutMs ?? (signal ? undefined : DEFAULT_REQUEST_TIMEOUT_MS);
1072
+ if (effectiveTimeoutMs !== undefined) {
1073
+ timeout = setTimeout(() => {
1074
+ if (client.pendingRequests.has(id)) {
1075
+ client.pendingRequests.delete(id);
1076
+ const err = new Error(`LSP request ${method} timed out after ${effectiveTimeoutMs}ms`);
1077
+ cleanup();
1078
+ reject(err);
1079
+ }
1080
+ }, effectiveTimeoutMs);
1081
+ }
1082
+ if (signal) {
1083
+ signal.addEventListener("abort", abortHandler, { once: true });
1084
+ if (signal.aborted) {
1085
+ abortHandler();
1086
+ return promise;
1087
+ }
1088
+ }
1089
+
1090
+ // Register pending request with timeout wrapper
1091
+ client.pendingRequests.set(id, {
1092
+ resolve: result => {
1093
+ if (timeout) clearTimeout(timeout);
1094
+ cleanup();
1095
+ resolve(result);
1096
+ },
1097
+ reject: err => {
1098
+ if (timeout) clearTimeout(timeout);
1099
+ cleanup();
1100
+ reject(err);
1101
+ },
1102
+ method,
1103
+ });
1104
+
1105
+ // Write request
1106
+ queueWriteMessage(client, request).catch(err => {
1107
+ if (timeout) clearTimeout(timeout);
1108
+ client.pendingRequests.delete(id);
1109
+ cleanup();
1110
+ reject(err);
1111
+ });
1112
+ return promise;
1113
+ }
1114
+
1115
+ /**
1116
+ * Send an LSP notification (no response expected).
1117
+ */
1118
+ export async function sendNotification(client: LspClient, method: string, params: unknown): Promise<void> {
1119
+ const notification: LspJsonRpcNotification = {
1120
+ jsonrpc: "2.0",
1121
+ method,
1122
+ params,
1123
+ };
1124
+
1125
+ client.lastActivity = Date.now();
1126
+ await queueWriteMessage(client, notification);
1127
+ }
1128
+
1129
+ /**
1130
+ * Shutdown all LSP clients.
1131
+ */
1132
+ export async function shutdownAll(): Promise<void> {
1133
+ const clientsToShutdown = Array.from(clients.values());
1134
+ clients.clear();
1135
+ // Mid-initialize clients live only in clientLocks (publication is deferred
1136
+ // until init succeeds) — without this, their server processes outlive
1137
+ // shutdown. Failed init promises already cleaned up after themselves.
1138
+ const pendingClients = Array.from(clientLocks.values());
1139
+ clientLocks.clear();
1140
+ const seen = new Set<LspClient>(clientsToShutdown);
1141
+ await Promise.allSettled([
1142
+ ...clientsToShutdown.map(client => shutdownClientInstance(client)),
1143
+ ...pendingClients.map(pending =>
1144
+ pending.then(client => {
1145
+ if (seen.has(client)) return;
1146
+ seen.add(client);
1147
+ return shutdownClientInstance(client);
1148
+ }),
1149
+ ),
1150
+ ]);
1151
+ }
1152
+
1153
+ /** Status of an LSP server */
1154
+ export interface LspServerStatus {
1155
+ name: string;
1156
+ status: "connecting" | "ready" | "error";
1157
+ fileTypes: string[];
1158
+ error?: string;
1159
+ }
1160
+
1161
+ /**
1162
+ * Get status of all active LSP clients.
1163
+ */
1164
+ export function getActiveClients(): LspServerStatus[] {
1165
+ return Array.from(clients.values()).map(client => ({
1166
+ name: client.config.command,
1167
+ status: client.status,
1168
+ fileTypes: client.config.fileTypes,
1169
+ }));
1170
+ }
1171
+
1172
+ // =============================================================================
1173
+ // Process Cleanup
1174
+ // =============================================================================
1175
+
1176
+ // Register cleanup on module unload
1177
+ if (typeof process !== "undefined") {
1178
+ process.on("beforeExit", () => {
1179
+ void shutdownAll();
1180
+ });
1181
+ process.on("SIGINT", () => {
1182
+ void (async () => {
1183
+ await shutdownAll();
1184
+ process.exit(0);
1185
+ })();
1186
+ });
1187
+ process.on("SIGTERM", () => {
1188
+ void (async () => {
1189
+ await shutdownAll();
1190
+ process.exit(0);
1191
+ })();
1192
+ });
1193
+ }