@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,1178 @@
1
+ /**
2
+ * Settings singleton with sync get/set and background persistence.
3
+ *
4
+ * Usage:
5
+ * import { settings } from "./settings";
6
+ *
7
+ * const enabled = settings.get("compaction.enabled"); // sync read
8
+ * settings.set("theme.dark", "titanium"); // sync write, saves in background
9
+ *
10
+ * For tests:
11
+ * const isolated = Settings.isolated({ "compaction.enabled": false });
12
+ */
13
+
14
+ import * as fs from "node:fs";
15
+ import * as os from "node:os";
16
+ import * as path from "node:path";
17
+ import {
18
+ getAgentDbPath,
19
+ getAgentDir,
20
+ getLastChangelogVersionPath,
21
+ getProjectDir,
22
+ isEnoent,
23
+ logger,
24
+ procmgr,
25
+ setDefaultTabWidth,
26
+ } from "@oh-my-pi/pi-utils";
27
+ import { YAML } from "bun";
28
+ import { type Settings as SettingsCapabilityItem, settingsCapability } from "../capability/settings";
29
+ import type { ModelRole } from "../config/model-roles";
30
+ import { loadCapability } from "../discovery";
31
+ import { isLightTheme, setAutoThemeMapping, setColorBlindMode, setSymbolPreset } from "../modes/theme/theme";
32
+ import { AgentStorage } from "../session/agent-storage";
33
+ import { type EditMode, normalizeEditMode } from "../utils/edit-mode";
34
+ import { withFileLock } from "./file-lock";
35
+ import {
36
+ type BashInterceptorRule,
37
+ type GroupPrefix,
38
+ type GroupTypeMap,
39
+ getDefault,
40
+ SETTINGS_SCHEMA,
41
+ type SettingPath,
42
+ type SettingValue,
43
+ } from "./settings-schema";
44
+
45
+ // Re-export types that callers need
46
+ export type * from "./settings-schema";
47
+ export * from "./settings-schema";
48
+
49
+ // ═══════════════════════════════════════════════════════════════════════════
50
+ // Types
51
+ // ═══════════════════════════════════════════════════════════════════════════
52
+
53
+ /** Raw settings object as stored in YAML */
54
+ export interface RawSettings {
55
+ [key: string]: unknown;
56
+ }
57
+
58
+ export interface SettingsOptions {
59
+ /** Current working directory for project settings discovery */
60
+ cwd?: string;
61
+ /** Agent directory for config.yml storage */
62
+ agentDir?: string;
63
+ /** Don't persist to disk (for tests) */
64
+ inMemory?: boolean;
65
+ /** Initial overrides */
66
+ overrides?: Partial<Record<SettingPath, unknown>>;
67
+ /** Extra config.yml-style overlays loaded after global/project settings */
68
+ configFiles?: string[];
69
+ }
70
+
71
+ // ═══════════════════════════════════════════════════════════════════════════
72
+ // Path Utilities
73
+ // ═══════════════════════════════════════════════════════════════════════════
74
+
75
+ /**
76
+ * Get a nested value from an object by path segments.
77
+ */
78
+ function getByPath(obj: RawSettings, segments: readonly string[]): unknown {
79
+ let current: unknown = obj;
80
+ for (const segment of segments) {
81
+ if (current === null || current === undefined || typeof current !== "object") {
82
+ return undefined;
83
+ }
84
+ current = (current as Record<string, unknown>)[segment];
85
+ }
86
+ return current;
87
+ }
88
+
89
+ const SETTING_PATH_SEGMENTS: Record<SettingPath, readonly string[]> = Object.fromEntries(
90
+ (Object.keys(SETTINGS_SCHEMA) as SettingPath[]).map(settingPath => [settingPath, settingPath.split(".")]),
91
+ ) as unknown as Record<SettingPath, readonly string[]>;
92
+
93
+ /**
94
+ * Set a nested value in an object by path segments.
95
+ * Creates intermediate objects as needed.
96
+ */
97
+ function setByPath(obj: RawSettings, segments: string[], value: unknown): void {
98
+ let current = obj;
99
+ for (let i = 0; i < segments.length - 1; i++) {
100
+ const segment = segments[i];
101
+ if (!(segment in current) || typeof current[segment] !== "object" || current[segment] === null) {
102
+ current[segment] = {};
103
+ }
104
+ current = current[segment] as RawSettings;
105
+ }
106
+ current[segments[segments.length - 1]] = value;
107
+ }
108
+
109
+ const PATH_SCOPED_ARRAY_SETTINGS = new Set<SettingPath>(["enabledModels", "disabledProviders"]);
110
+ type PathScopedStringArrayEntry = {
111
+ path?: unknown;
112
+ paths?: unknown;
113
+ pathPrefix?: unknown;
114
+ pathPrefixes?: unknown;
115
+ values?: unknown;
116
+ items?: unknown;
117
+ models?: unknown;
118
+ providers?: unknown;
119
+ };
120
+
121
+ function expandTilde(p: string): string {
122
+ return p === "~" ? os.homedir() : p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
123
+ }
124
+
125
+ function normalizePathPrefix(prefix: string): string {
126
+ return path.resolve(expandTilde(prefix));
127
+ }
128
+
129
+ function pathMatchesPrefix(cwd: string, prefix: string): boolean {
130
+ const relative = path.relative(normalizePathPrefix(prefix), path.resolve(cwd));
131
+ return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
132
+ }
133
+
134
+ function stringArrayFromUnknown(value: unknown): string[] {
135
+ if (typeof value === "string") return [value];
136
+ if (Array.isArray(value)) return value.filter((item): item is string => typeof item === "string");
137
+ return [];
138
+ }
139
+
140
+ function shallowStringRecord(value: unknown): Record<string, string> {
141
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
142
+
143
+ const result: Record<string, string> = {};
144
+ for (const [key, item] of Object.entries(value)) {
145
+ if (typeof item === "string") {
146
+ result[key] = item;
147
+ }
148
+ }
149
+ return result;
150
+ }
151
+
152
+ function resolvePathScopedStringArray(settingPath: SettingPath, value: unknown, cwd: string): string[] | undefined {
153
+ if (!PATH_SCOPED_ARRAY_SETTINGS.has(settingPath) || !Array.isArray(value)) return undefined;
154
+
155
+ const resolved: string[] = [];
156
+ for (const entry of value) {
157
+ if (typeof entry === "string") {
158
+ resolved.push(entry);
159
+ continue;
160
+ }
161
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
162
+
163
+ const scoped = entry as PathScopedStringArrayEntry;
164
+ const prefixes = [
165
+ ...stringArrayFromUnknown(scoped.path),
166
+ ...stringArrayFromUnknown(scoped.paths),
167
+ ...stringArrayFromUnknown(scoped.pathPrefix),
168
+ ...stringArrayFromUnknown(scoped.pathPrefixes),
169
+ ];
170
+ if (prefixes.length === 0 || !prefixes.some(prefix => pathMatchesPrefix(cwd, prefix))) continue;
171
+
172
+ const values =
173
+ settingPath === "enabledModels"
174
+ ? [
175
+ ...stringArrayFromUnknown(scoped.values),
176
+ ...stringArrayFromUnknown(scoped.items),
177
+ ...stringArrayFromUnknown(scoped.models),
178
+ ]
179
+ : [
180
+ ...stringArrayFromUnknown(scoped.values),
181
+ ...stringArrayFromUnknown(scoped.items),
182
+ ...stringArrayFromUnknown(scoped.providers),
183
+ ];
184
+ resolved.push(...values);
185
+ }
186
+
187
+ return resolved;
188
+ }
189
+
190
+ // ═══════════════════════════════════════════════════════════════════════════
191
+ // Settings Class
192
+ // ═══════════════════════════════════════════════════════════════════════════
193
+
194
+ export class Settings {
195
+ #configPath: string | null;
196
+ #cwd: string;
197
+ #agentDir: string;
198
+ #storage: AgentStorage | null = null;
199
+
200
+ #configFiles: string[] = [];
201
+ /** Global settings from config.yml */
202
+ #global: RawSettings = {};
203
+ /** Project settings from .claude/settings.yml etc */
204
+ #project: RawSettings = {};
205
+ /** Extra config.yml-style overlays passed by CLI */
206
+ #configOverlay: RawSettings = {};
207
+ /** Runtime overrides (not persisted) */
208
+ #overrides: RawSettings = {};
209
+ /** Merged view (global + project + overrides) */
210
+ #merged: RawSettings = {};
211
+ /** Cached resolved values from the merged view, including defaults/path scoping */
212
+ #resolvedCache = new Map<SettingPath, unknown>();
213
+
214
+ /** Paths modified during this session (for partial save) */
215
+ #modified = new Set<string>();
216
+
217
+ /** Legacy `lastChangelogVersion` captured from config.yml during migration (now a marker file). */
218
+ #legacyLastChangelogVersion?: string;
219
+
220
+ /** Pending save (debounced) */
221
+ #saveTimer?: NodeJS.Timeout;
222
+ #savePromise?: Promise<void>;
223
+
224
+ /** Whether to persist changes */
225
+ #persist: boolean;
226
+
227
+ private constructor(options: SettingsOptions = {}) {
228
+ this.#cwd = path.normalize(options.cwd ?? getProjectDir());
229
+ this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
230
+ this.#configPath = options.inMemory ? null : path.join(this.#agentDir, "config.yml");
231
+ this.#configFiles = options.configFiles?.map(file => path.resolve(this.#cwd, expandTilde(file))) ?? [];
232
+ this.#persist = !options.inMemory;
233
+
234
+ if (options.overrides) {
235
+ for (const [key, value] of Object.entries(options.overrides)) {
236
+ setByPath(this.#overrides, key.split("."), value);
237
+ }
238
+
239
+ this.#overrides = this.#migrateRawSettings(this.#overrides);
240
+ }
241
+ }
242
+
243
+ // ─────────────────────────────────────────────────────────────────────────
244
+ // Factory Methods
245
+ // ─────────────────────────────────────────────────────────────────────────
246
+
247
+ /**
248
+ * Initialize the global singleton.
249
+ * Call once at startup before accessing `settings`.
250
+ */
251
+ static init(options: SettingsOptions = {}): Promise<Settings> {
252
+ if (globalInstancePromise) return globalInstancePromise;
253
+
254
+ const instance = new Settings(options);
255
+ const promise = instance.#load();
256
+ globalInstancePromise = promise;
257
+
258
+ return promise.then(
259
+ instance => {
260
+ globalInstance = instance;
261
+ clearBoundSettingsMethods();
262
+ globalInstancePromise = Promise.resolve(instance);
263
+ return instance;
264
+ },
265
+ error => {
266
+ globalInstance = null;
267
+ globalInstancePromise = null;
268
+ clearBoundSettingsMethods();
269
+ throw error;
270
+ },
271
+ );
272
+ }
273
+
274
+ /**
275
+ * Create an isolated instance for testing.
276
+ * Does not affect the global singleton.
277
+ */
278
+ static isolated(overrides: Partial<Record<SettingPath, unknown>> = {}): Settings {
279
+ const instance = new Settings({ inMemory: true, overrides });
280
+ instance.#rebuildMerged();
281
+ return instance;
282
+ }
283
+
284
+ /**
285
+ * Get the global singleton.
286
+ * Throws if not initialized.
287
+ */
288
+ static get instance(): Settings {
289
+ if (!globalInstance) {
290
+ throw new Error("Settings not initialized. Call Settings.init() first.");
291
+ }
292
+ return globalInstance;
293
+ }
294
+
295
+ // ─────────────────────────────────────────────────────────────────────────
296
+ // Core API
297
+ // ─────────────────────────────────────────────────────────────────────────
298
+
299
+ /**
300
+ * Get a setting value (sync).
301
+ * Returns the merged value from global + project + overrides, or the default.
302
+ */
303
+ get<P extends SettingPath>(path: P): SettingValue<P> {
304
+ if (this.#resolvedCache.has(path)) {
305
+ return this.#resolvedCache.get(path) as SettingValue<P>;
306
+ }
307
+
308
+ const value = getByPath(this.#merged, SETTING_PATH_SEGMENTS[path]);
309
+ const resolved =
310
+ value !== undefined ? (resolvePathScopedStringArray(path, value, this.#cwd) ?? value) : getDefault(path);
311
+ this.#resolvedCache.set(path, resolved);
312
+ return resolved as SettingValue<P>;
313
+ }
314
+
315
+ /**
316
+ * Whether `path` has an explicitly configured value (global config, project
317
+ * config, or runtime override) rather than falling back to the schema default.
318
+ */
319
+ isConfigured(path: SettingPath): boolean {
320
+ return getByPath(this.#merged, SETTING_PATH_SEGMENTS[path]) !== undefined;
321
+ }
322
+
323
+ /**
324
+ * Set a setting value (sync).
325
+ * Updates global settings and queues a background save.
326
+ * Triggers hooks for settings that have side effects.
327
+ */
328
+ set<P extends SettingPath>(path: P, value: SettingValue<P>): void {
329
+ const prev = this.get(path);
330
+ const segments = path.split(".");
331
+ setByPath(this.#global, segments, value);
332
+ this.#modified.add(path);
333
+ this.#rebuildMerged();
334
+ const next = this.get(path);
335
+ this.#queueSave();
336
+
337
+ // Trigger hook if exists
338
+ const hook = SETTING_HOOKS[path];
339
+ if (hook) {
340
+ hook(value, prev);
341
+ }
342
+ this.#fireEffectiveSettingChanged(path, next, prev);
343
+ }
344
+
345
+ /**
346
+ * Apply runtime overrides (not persisted).
347
+ */
348
+ override<P extends SettingPath>(path: P, value: SettingValue<P>): void {
349
+ const prev = this.get(path);
350
+ const segments = path.split(".");
351
+ setByPath(this.#overrides, segments, value);
352
+ this.#rebuildMerged();
353
+ this.#fireEffectiveSettingChanged(path, this.get(path), prev);
354
+ }
355
+
356
+ /**
357
+ * Clear a runtime override.
358
+ */
359
+ clearOverride(path: SettingPath): void {
360
+ const prev = this.get(path);
361
+ const segments = path.split(".");
362
+ let current = this.#overrides;
363
+ for (let i = 0; i < segments.length - 1; i++) {
364
+ const segment = segments[i];
365
+ if (!(segment in current)) return;
366
+ current = current[segment] as RawSettings;
367
+ }
368
+ delete current[segments[segments.length - 1]];
369
+ this.#rebuildMerged();
370
+ this.#fireEffectiveSettingChanged(path, this.get(path), prev);
371
+ }
372
+
373
+ #fireEffectiveSettingChanged(path: SettingPath, value: unknown, prev: unknown): void {
374
+ if (Object.is(value, prev)) return;
375
+ if (path === "statusLine.sessionAccent") {
376
+ statusLineSessionAccentSignal.fire();
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Flush any pending saves to disk.
382
+ * Call before exit to ensure all changes are persisted.
383
+ */
384
+ async flush(): Promise<void> {
385
+ if (this.#saveTimer) {
386
+ clearTimeout(this.#saveTimer);
387
+ this.#saveTimer = undefined;
388
+ }
389
+ if (this.#savePromise) {
390
+ await this.#savePromise;
391
+ }
392
+ if (this.#modified.size > 0) {
393
+ await this.#saveNow();
394
+ }
395
+ }
396
+
397
+ async cloneForCwd(cwd: string): Promise<Settings> {
398
+ const cloned = new Settings({
399
+ cwd,
400
+ agentDir: this.#agentDir,
401
+ inMemory: !this.#persist,
402
+ });
403
+ cloned.#storage = this.#storage;
404
+ cloned.#global = structuredClone(this.#global);
405
+ cloned.#project = this.#persist ? await cloned.#loadProjectSettings() : structuredClone(this.#project);
406
+ cloned.#configFiles = [...this.#configFiles];
407
+ cloned.#configOverlay = structuredClone(this.#configOverlay);
408
+ cloned.#overrides = structuredClone(this.#overrides);
409
+ cloned.#rebuildMerged();
410
+ cloned.#fireAllHooks();
411
+ return cloned;
412
+ }
413
+
414
+ /**
415
+ * Re-scope this instance to a new working directory *in place*: reload the
416
+ * project layer (`.claude/settings.yml` etc.) from `cwd`, re-resolve
417
+ * path-scoped settings against it, and re-fire side-effect hooks (theme,
418
+ * symbols, tab width, …). Global settings and runtime overrides are preserved.
419
+ *
420
+ * Unlike {@link cloneForCwd}, this mutates the live instance, so every holder
421
+ * (the `settings` proxy, the active session, controllers) observes the new
422
+ * project scope without swapping references — used when the process changes
423
+ * directory mid-run (`/move`, cross-project resume). No-op when `cwd` is
424
+ * already the current scope.
425
+ */
426
+ async reloadForCwd(cwd: string): Promise<void> {
427
+ const normalized = path.normalize(cwd);
428
+ if (normalized === this.#cwd) return;
429
+ this.#cwd = normalized;
430
+ if (this.#persist) {
431
+ this.#project = await this.#loadProjectSettings();
432
+ }
433
+ this.#rebuildMerged();
434
+ this.#fireAllHooks();
435
+ }
436
+
437
+ // ─────────────────────────────────────────────────────────────────────────
438
+ // Accessors
439
+ // ─────────────────────────────────────────────────────────────────────────
440
+
441
+ getStorage(): AgentStorage | null {
442
+ return this.#storage;
443
+ }
444
+
445
+ getCwd(): string {
446
+ return this.#cwd;
447
+ }
448
+
449
+ getAgentDir(): string {
450
+ return this.#agentDir;
451
+ }
452
+
453
+ getPlansDirectory(): string {
454
+ return path.join(this.#agentDir, "plans");
455
+ }
456
+
457
+ /**
458
+ * Get shell configuration based on settings.
459
+ */
460
+ getShellConfig() {
461
+ const shell = this.get("shellPath");
462
+ return procmgr.getShellConfig(shell);
463
+ }
464
+
465
+ /**
466
+ * Get all settings in a group with full type safety.
467
+ */
468
+ getGroup<G extends GroupPrefix>(prefix: G): GroupTypeMap[G] {
469
+ const result: Record<string, unknown> = {};
470
+ for (const key of Object.keys(SETTINGS_SCHEMA) as SettingPath[]) {
471
+ if (key.startsWith(`${prefix}.`)) {
472
+ const suffix = key.slice(prefix.length + 1);
473
+ result[suffix] = this.get(key);
474
+ }
475
+ }
476
+ return result as unknown as GroupTypeMap[G];
477
+ }
478
+
479
+ /**
480
+ * Get the edit variant for a specific model.
481
+ * Returns "patch", "replace", "hashline", "apply_patch", or null (use global default).
482
+ */
483
+ getEditVariantForModel(model: string | undefined): EditMode | null {
484
+ if (!model) return null;
485
+ const variants = (this.#merged.edit as { modelVariants?: Record<string, string> })?.modelVariants;
486
+ if (!variants) return null;
487
+ for (const pattern in variants) {
488
+ if (model.includes(pattern)) {
489
+ const value = normalizeEditMode(variants[pattern]);
490
+ if (value) {
491
+ return value;
492
+ }
493
+ }
494
+ }
495
+ return null;
496
+ }
497
+
498
+ /**
499
+ * Get bash interceptor rules (typed accessor for complex array config).
500
+ */
501
+ getBashInterceptorRules(): BashInterceptorRule[] {
502
+ return this.get("bashInterceptor.patterns");
503
+ }
504
+
505
+ /**
506
+ * Set a model role (helper for modelRoles record).
507
+ */
508
+ setModelRole(role: ModelRole | string, modelId: string): void {
509
+ const current = shallowStringRecord(getByPath(this.#global, ["modelRoles"]));
510
+ const runtimeOverrides = getByPath(this.#overrides, ["modelRoles"]);
511
+ const updateRuntimeOverride =
512
+ !!runtimeOverrides &&
513
+ typeof runtimeOverrides === "object" &&
514
+ !Array.isArray(runtimeOverrides) &&
515
+ Object.hasOwn(runtimeOverrides, role);
516
+
517
+ this.set("modelRoles", { ...current, [role]: modelId });
518
+
519
+ if (updateRuntimeOverride) {
520
+ this.override("modelRoles", { ...shallowStringRecord(runtimeOverrides), [role]: modelId });
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Get a model role (helper for modelRoles record).
526
+ */
527
+ getModelRole(role: ModelRole | string): string | undefined {
528
+ const roles = this.get("modelRoles");
529
+ return roles[role];
530
+ }
531
+
532
+ /**
533
+ * Get all model roles (helper for modelRoles record).
534
+ */
535
+ getModelRoles(): ReadOnlyDict<string> {
536
+ return { ...this.get("modelRoles") };
537
+ }
538
+
539
+ /*
540
+ * Override model roles (helper for modelRoles record).
541
+ */
542
+ overrideModelRoles(roles: ReadOnlyDict<string>): void {
543
+ const next = shallowStringRecord(getByPath(this.#overrides, ["modelRoles"]));
544
+ for (const [role, modelId] of Object.entries(roles)) {
545
+ if (modelId) {
546
+ next[role] = modelId;
547
+ }
548
+ }
549
+ this.override("modelRoles", next);
550
+ }
551
+
552
+ /**
553
+ * Set disabled providers (for compatibility with discovery system).
554
+ */
555
+ setDisabledProviders(ids: string[]): void {
556
+ this.set("disabledProviders", ids);
557
+ }
558
+
559
+ // ─────────────────────────────────────────────────────────────────────────
560
+ // Loading
561
+ // ─────────────────────────────────────────────────────────────────────────
562
+
563
+ async #load(): Promise<Settings> {
564
+ // Project settings load (loadCapability scans cwd) is independent of the
565
+ // persist chain (storage open → legacy migration → global config.yml read),
566
+ // so kick it off first and await after the persist chain completes. The
567
+ // persist steps remain sequential: migration may write config.yml, which
568
+ // #loadYaml then reads; migration's db fallback needs #storage opened.
569
+ const projectPromise = this.#loadProjectSettings();
570
+
571
+ if (this.#persist) {
572
+ this.#storage = await AgentStorage.open(getAgentDbPath(this.#agentDir));
573
+ await this.#migrateFromLegacy();
574
+ this.#global = await this.#loadYaml(this.#configPath!);
575
+ await this.#seedLastChangelogVersionMarker();
576
+ }
577
+
578
+ this.#project = await projectPromise;
579
+ this.#configOverlay = await this.#loadConfigOverlays();
580
+
581
+ // Build merged view (global → project → overrides; project wins over global)
582
+ this.#rebuildMerged();
583
+ this.#fireAllHooks();
584
+ return this;
585
+ }
586
+
587
+ async #loadYaml(filePath: string): Promise<RawSettings> {
588
+ try {
589
+ const content = await Bun.file(filePath).text();
590
+ const parsed = YAML.parse(content);
591
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
592
+ return {};
593
+ }
594
+ return this.#migrateRawSettings(parsed as RawSettings);
595
+ } catch (error) {
596
+ if (isEnoent(error)) return {};
597
+ logger.warn("Settings: failed to load", { path: filePath, error: String(error) });
598
+ return {};
599
+ }
600
+ }
601
+
602
+ async #loadProjectSettings(): Promise<RawSettings> {
603
+ try {
604
+ const result = await loadCapability(settingsCapability.id, { cwd: this.#cwd });
605
+ let merged: RawSettings = {};
606
+ for (const item of result.items as SettingsCapabilityItem[]) {
607
+ if (item.level === "project") {
608
+ merged = this.#deepMerge(merged, item.data as RawSettings);
609
+ }
610
+ }
611
+ return this.#migrateRawSettings(merged);
612
+ } catch {
613
+ return {};
614
+ }
615
+ }
616
+
617
+ async #loadConfigOverlays(): Promise<RawSettings> {
618
+ let merged: RawSettings = {};
619
+ for (const filePath of this.#configFiles) {
620
+ merged = this.#deepMerge(merged, await this.#loadOverlayYaml(filePath));
621
+ }
622
+ return merged;
623
+ }
624
+
625
+ /**
626
+ * Strict loader for explicit `--config` overlays: unlike `#loadYaml`,
627
+ * missing or malformed files are hard errors so a typo'd path cannot
628
+ * silently fall back to the persistent settings.
629
+ */
630
+ async #loadOverlayYaml(filePath: string): Promise<RawSettings> {
631
+ let content: string;
632
+ try {
633
+ content = await Bun.file(filePath).text();
634
+ } catch (error) {
635
+ throw new Error(
636
+ isEnoent(error)
637
+ ? `Config overlay not found: ${filePath}`
638
+ : `Failed to read config overlay ${filePath}: ${String(error)}`,
639
+ );
640
+ }
641
+ let parsed: unknown;
642
+ try {
643
+ parsed = YAML.parse(content);
644
+ } catch (error) {
645
+ throw new Error(`Failed to parse config overlay ${filePath}: ${String(error)}`);
646
+ }
647
+ if (parsed === null || parsed === undefined) return {};
648
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
649
+ throw new Error(`Config overlay must be a YAML mapping: ${filePath}`);
650
+ }
651
+ return this.#migrateRawSettings(parsed as RawSettings);
652
+ }
653
+
654
+ async #migrateFromLegacy(): Promise<void> {
655
+ if (!this.#configPath) return;
656
+
657
+ // Check if config.yml already exists
658
+ try {
659
+ await Bun.file(this.#configPath).text();
660
+ return; // Already exists, no migration needed
661
+ } catch (err) {
662
+ if (!isEnoent(err)) return;
663
+ }
664
+
665
+ let settings: RawSettings = {};
666
+ let migrated = false;
667
+
668
+ // 1. Migrate from settings.json
669
+ const settingsJsonPath = path.join(this.#agentDir, "settings.json");
670
+ try {
671
+ const parsed = JSON.parse(await Bun.file(settingsJsonPath).text());
672
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
673
+ settings = this.#deepMerge(settings, this.#migrateRawSettings(parsed));
674
+ migrated = true;
675
+ try {
676
+ fs.renameSync(settingsJsonPath, `${settingsJsonPath}.bak`);
677
+ } catch {}
678
+ }
679
+ } catch {}
680
+
681
+ // 2. Migrate from agent.db
682
+ try {
683
+ const dbSettings = this.#storage?.getSettings();
684
+ if (dbSettings) {
685
+ settings = this.#deepMerge(settings, this.#migrateRawSettings(dbSettings as RawSettings));
686
+ migrated = true;
687
+ }
688
+ } catch {}
689
+
690
+ // 3. Write merged settings
691
+ if (migrated && Object.keys(settings).length > 0) {
692
+ try {
693
+ await Bun.write(this.#configPath, YAML.stringify(settings, null, 2));
694
+ logger.debug("Settings: migrated to config.yml", { path: this.#configPath });
695
+ } catch {}
696
+ }
697
+ }
698
+
699
+ /** Apply schema migrations to raw settings */
700
+ #migrateRawSettings(raw: RawSettings): RawSettings {
701
+ // queueMode -> steeringMode
702
+ if ("queueMode" in raw && !("steeringMode" in raw)) {
703
+ raw.steeringMode = raw.queueMode;
704
+ delete raw.queueMode;
705
+ }
706
+
707
+ // lastChangelogVersion moved out of config.yml into the
708
+ // <agentDir>/last-changelog-version marker file so version bumps no
709
+ // longer dirty user-tracked configs. Capture for marker seeding (see
710
+ // #seedLastChangelogVersionMarker), then strip the key — the next
711
+ // config save drops it from disk.
712
+ if (typeof raw.lastChangelogVersion === "string") {
713
+ this.#legacyLastChangelogVersion ??= raw.lastChangelogVersion;
714
+ }
715
+ delete raw.lastChangelogVersion;
716
+
717
+ // ask.timeout: ms -> seconds (if value > 1000, it's old ms format)
718
+ if (raw.ask && typeof (raw.ask as Record<string, unknown>).timeout === "number") {
719
+ const oldValue = (raw.ask as Record<string, unknown>).timeout as number;
720
+ if (oldValue > 1000) {
721
+ (raw.ask as Record<string, unknown>).timeout = Math.round(oldValue / 1000);
722
+ }
723
+ }
724
+
725
+ // Migrate old flat "theme" string to nested theme.dark/theme.light
726
+ if (typeof raw.theme === "string") {
727
+ const oldTheme = raw.theme;
728
+ if (oldTheme === "light" || oldTheme === "dark") {
729
+ // Built-in defaults — just remove, let new defaults apply
730
+ delete raw.theme;
731
+ } else {
732
+ // Custom theme — detect luminance to place in correct slot
733
+ const slot = isLightTheme(oldTheme) ? "light" : "dark";
734
+ raw.theme = { [slot]: oldTheme };
735
+ }
736
+ }
737
+
738
+ // task.isolation.enabled (boolean) -> task.isolation.mode (enum)
739
+ const taskObj = raw.task as Record<string, unknown> | undefined;
740
+ const isolationObj = taskObj?.isolation as Record<string, unknown> | undefined;
741
+ if (isolationObj && "enabled" in isolationObj) {
742
+ if (typeof isolationObj.enabled === "boolean") {
743
+ isolationObj.mode = isolationObj.enabled ? "auto" : "none";
744
+ }
745
+ delete isolationObj.enabled;
746
+ }
747
+
748
+ // task.simple: removed — the task tool no longer accepts a per-call
749
+ // schema (workflows drive structured output via eval agent()) and the
750
+ // batch/context shape is gated by task.batch instead.
751
+ if (taskObj && "simple" in taskObj) {
752
+ delete taskObj.simple;
753
+ }
754
+
755
+ // task.isolation.mode: legacy values from before the pi-iso PAL refactor.
756
+ // `worktree` was git worktree → now lives under `rcopy`. `fuse-overlay`
757
+ // and `fuse-projfs` are now the platform-named `overlayfs` / `projfs`
758
+ // kinds; the PAL falls back internally when the chosen one isn't
759
+ // available, so we don't need the old TS-side platform guards.
760
+ if (isolationObj && typeof isolationObj.mode === "string") {
761
+ const legacy: Record<string, string> = {
762
+ worktree: "rcopy",
763
+ "fuse-overlay": "overlayfs",
764
+ "fuse-projfs": "projfs",
765
+ };
766
+ const mapped = legacy[isolationObj.mode as string];
767
+ if (mapped !== undefined) {
768
+ isolationObj.mode = mapped;
769
+ }
770
+ }
771
+
772
+ // edit.mode: removed "atom" and "vim" variants map back to "hashline"
773
+ const editObj = raw.edit as Record<string, unknown> | undefined;
774
+ if (editObj) {
775
+ if (editObj.mode === "atom" || editObj.mode === "vim") {
776
+ editObj.mode = "hashline";
777
+ }
778
+ const modelVariants = editObj.modelVariants as Record<string, unknown> | undefined;
779
+ if (modelVariants && typeof modelVariants === "object" && !Array.isArray(modelVariants)) {
780
+ for (const [pattern, variant] of Object.entries(modelVariants)) {
781
+ if (variant === "atom" || variant === "vim") {
782
+ modelVariants[pattern] = "hashline";
783
+ }
784
+ }
785
+ }
786
+ }
787
+ if (raw["edit.mode"] === "atom" || raw["edit.mode"] === "vim") {
788
+ raw["edit.mode"] = "hashline";
789
+ }
790
+
791
+ // compaction.strategy: removed local-model shake-summary mode; plain shake
792
+ // keeps the same mechanical artifact-backed reduction without background CPU.
793
+ const compactionObj = raw.compaction as Record<string, unknown> | undefined;
794
+ if (compactionObj?.strategy === "shake-summary") {
795
+ compactionObj.strategy = "shake";
796
+ }
797
+ if (raw["compaction.strategy"] === "shake-summary") {
798
+ raw["compaction.strategy"] = "shake";
799
+ }
800
+
801
+ // statusLine: rename "plan_mode" segment to "mode"
802
+ const statusLineObj = raw.statusLine as Record<string, unknown> | undefined;
803
+ if (statusLineObj) {
804
+ for (const key of ["leftSegments", "rightSegments"] as const) {
805
+ const segments = statusLineObj[key];
806
+ if (Array.isArray(segments)) {
807
+ statusLineObj[key] = segments.map(seg => (seg === "plan_mode" ? "mode" : seg));
808
+ }
809
+ }
810
+ const segmentOptions = statusLineObj.segmentOptions as Record<string, unknown> | undefined;
811
+ if (segmentOptions && "plan_mode" in segmentOptions && !("mode" in segmentOptions)) {
812
+ segmentOptions.mode = segmentOptions.plan_mode;
813
+ delete segmentOptions.plan_mode;
814
+ }
815
+ }
816
+
817
+ // providers.parallelFetch (boolean) replaced by the providers.fetch reader
818
+ // priority enum. The new default ("auto") supersedes both old values —
819
+ // Parallel is now a deep fallback in the auto chain rather than the first
820
+ // choice — so drop the legacy key (flat and nested) and let the enum
821
+ // default apply.
822
+ const providersObj = raw.providers as Record<string, unknown> | undefined;
823
+ if (providersObj && "parallelFetch" in providersObj) {
824
+ delete providersObj.parallelFetch;
825
+ }
826
+ delete raw["providers.parallelFetch"];
827
+
828
+ // Map legacy `memories.enabled` boolean to the explicit `memory.backend`
829
+ // enum if the latter hasn't been set yet. Idempotent: subsequent
830
+ // migrations are no-ops once memory.backend is materialised.
831
+ const memoryBackendObj = raw.memory as Record<string, unknown> | undefined;
832
+ const memoryBackendSet = memoryBackendObj && typeof memoryBackendObj.backend === "string";
833
+ const memoriesObj = raw.memories as Record<string, unknown> | undefined;
834
+ if (!memoryBackendSet && memoriesObj && typeof memoriesObj.enabled === "boolean") {
835
+ const next = memoriesObj.enabled ? "local" : "off";
836
+ const memoryRoot = (memoryBackendObj ?? {}) as Record<string, unknown>;
837
+ memoryRoot.backend = next;
838
+ raw.memory = memoryRoot;
839
+ }
840
+
841
+ // Rename the legacy local `mnemosyne` memory backend to `mnemopi`.
842
+ // - `memory.backend: "mnemosyne"` now selects the renamed backend.
843
+ // - the top-level `mnemosyne` settings object becomes `mnemopi`.
844
+ // Idempotent: skips the object move once `mnemopi` is materialised.
845
+ if (memoryBackendObj && memoryBackendObj.backend === "mnemosyne") {
846
+ memoryBackendObj.backend = "mnemopi";
847
+ }
848
+ if ("mnemosyne" in raw && !("mnemopi" in raw)) {
849
+ raw.mnemopi = raw.mnemosyne;
850
+ delete raw.mnemosyne;
851
+ }
852
+
853
+ // hindsight: dynamicBankId/agentName -> scoping enum + bankId
854
+ // - dynamicBankId=true → scoping="per-project" (closest semantic match;
855
+ // the legacy `agent::project::channel::user` tuple was per-project in
856
+ // practice — the channel/user env vars were rarely set).
857
+ // - hindsight.agentName was only used as the agent slot in the legacy
858
+ // dynamic tuple; if the user customised it we surface it as the new
859
+ // bankId base when no explicit bankId is set.
860
+ const hindsightObj = raw.hindsight as Record<string, unknown> | undefined;
861
+ if (hindsightObj) {
862
+ if ("dynamicBankId" in hindsightObj) {
863
+ if (!("scoping" in hindsightObj) && hindsightObj.dynamicBankId === true) {
864
+ hindsightObj.scoping = "per-project";
865
+ }
866
+ delete hindsightObj.dynamicBankId;
867
+ }
868
+ if ("agentName" in hindsightObj) {
869
+ const agentName = hindsightObj.agentName;
870
+ if (
871
+ !("bankId" in hindsightObj) &&
872
+ typeof agentName === "string" &&
873
+ agentName.trim().length > 0 &&
874
+ agentName !== "omp"
875
+ ) {
876
+ hindsightObj.bankId = agentName;
877
+ }
878
+ delete hindsightObj.agentName;
879
+ }
880
+ }
881
+
882
+ return raw;
883
+ }
884
+
885
+ /**
886
+ * One-time migration: seed the last-changelog-version marker file from the
887
+ * legacy config.yml key. An existing marker always wins — it is the newer
888
+ * source of truth.
889
+ */
890
+ async #seedLastChangelogVersionMarker(): Promise<void> {
891
+ const legacy = this.#legacyLastChangelogVersion;
892
+ if (!legacy) return;
893
+ const markerPath = getLastChangelogVersionPath(this.#agentDir);
894
+ try {
895
+ if ((await Bun.file(markerPath).text()).trim()) return;
896
+ } catch (error) {
897
+ if (!isEnoent(error)) return;
898
+ }
899
+ try {
900
+ await Bun.write(markerPath, legacy);
901
+ } catch (error) {
902
+ logger.warn("Settings: failed to seed last-changelog-version marker", { error: String(error) });
903
+ }
904
+ }
905
+
906
+ // ─────────────────────────────────────────────────────────────────────────
907
+ // Saving
908
+ // ─────────────────────────────────────────────────────────────────────────
909
+
910
+ #queueSave(): void {
911
+ if (!this.#persist || !this.#configPath) return;
912
+
913
+ // Debounce: wait 100ms for more changes
914
+ if (this.#saveTimer) {
915
+ clearTimeout(this.#saveTimer);
916
+ }
917
+ this.#saveTimer = setTimeout(() => {
918
+ this.#saveTimer = undefined;
919
+ this.#saveNow().catch(err => {
920
+ logger.warn("Settings: background save failed", { error: String(err) });
921
+ });
922
+ }, 100);
923
+ }
924
+
925
+ async #saveNow(): Promise<void> {
926
+ if (!this.#persist || !this.#configPath || this.#modified.size === 0) return;
927
+
928
+ const configPath = this.#configPath;
929
+ const modifiedPaths = [...this.#modified];
930
+ this.#modified.clear();
931
+
932
+ try {
933
+ await withFileLock(configPath, async () => {
934
+ // Re-read to preserve external changes
935
+ const current = await this.#loadYaml(configPath);
936
+
937
+ // Apply only our modified paths
938
+ for (const modPath of modifiedPaths) {
939
+ const segments = modPath.split(".");
940
+ const value = getByPath(this.#global, segments);
941
+ setByPath(current, segments, value);
942
+ }
943
+
944
+ // Update our global with any external changes we preserved
945
+ this.#global = current;
946
+ await Bun.write(configPath, YAML.stringify(this.#global, null, 2));
947
+ });
948
+ } catch (error) {
949
+ logger.warn("Settings: save failed", { error: String(error) });
950
+ // Re-add failed paths for retry
951
+ for (const p of modifiedPaths) {
952
+ this.#modified.add(p);
953
+ }
954
+ }
955
+
956
+ this.#rebuildMerged();
957
+ }
958
+
959
+ // ─────────────────────────────────────────────────────────────────────────
960
+ // Utilities
961
+ // ─────────────────────────────────────────────────────────────────────────
962
+
963
+ #rebuildMerged(): void {
964
+ this.#merged = this.#deepMerge(this.#deepMerge({}, this.#global), this.#project);
965
+ this.#merged = this.#deepMerge(this.#merged, this.#configOverlay);
966
+ this.#merged = this.#deepMerge(this.#merged, this.#overrides);
967
+ this.#resolvedCache.clear();
968
+ }
969
+
970
+ #fireAllHooks(): void {
971
+ for (const key of Object.keys(SETTING_HOOKS) as SettingPath[]) {
972
+ const hook = SETTING_HOOKS[key];
973
+ if (hook) {
974
+ const value = this.get(key);
975
+ hook(value, value);
976
+ }
977
+ }
978
+ }
979
+
980
+ #deepMerge(base: RawSettings, overrides: RawSettings): RawSettings {
981
+ const result = { ...base };
982
+ for (const key of Object.keys(overrides)) {
983
+ const override = overrides[key];
984
+ const baseVal = base[key];
985
+
986
+ if (override === undefined) continue;
987
+
988
+ if (
989
+ typeof override === "object" &&
990
+ override !== null &&
991
+ !Array.isArray(override) &&
992
+ typeof baseVal === "object" &&
993
+ baseVal !== null &&
994
+ !Array.isArray(baseVal)
995
+ ) {
996
+ result[key] = this.#deepMerge(baseVal as RawSettings, override as RawSettings);
997
+ } else {
998
+ result[key] = override;
999
+ }
1000
+ }
1001
+ return result;
1002
+ }
1003
+ }
1004
+
1005
+ // ═══════════════════════════════════════════════════════════════════════════
1006
+ // Setting Hooks
1007
+ // ═══════════════════════════════════════════════════════════════════════════
1008
+
1009
+ type SettingHook<P extends SettingPath> = (value: SettingValue<P>, prev: SettingValue<P>) => void;
1010
+
1011
+ /**
1012
+ * Minimal change-notification primitive backing the exported `on*Changed`
1013
+ * subscriptions. Holds a listener set, hands out unsubscribe closures, and
1014
+ * isolates errors so a single throwing listener can't abort the rest or bubble
1015
+ * out of `Settings.set()`.
1016
+ *
1017
+ * @typeParam A - argument tuple forwarded to each listener on `fire`.
1018
+ */
1019
+ class SettingSignal<A extends unknown[] = []> {
1020
+ #listeners = new Set<(...args: A) => void>();
1021
+
1022
+ constructor(private readonly label: string) {}
1023
+
1024
+ /** Subscribe `cb`; returns an unsubscribe function. */
1025
+ on(cb: (...args: A) => void): () => void {
1026
+ this.#listeners.add(cb);
1027
+ return () => {
1028
+ this.#listeners.delete(cb);
1029
+ };
1030
+ }
1031
+
1032
+ /**
1033
+ * Invoke every listener with `args`. Iterates a snapshot so a listener may
1034
+ * (un)subscribe mid-fire without re-entrancy — the Hindsight backend
1035
+ * re-registers the fresh state's listener on every rebuild — and wraps each
1036
+ * call so a throwing listener is logged and skipped instead of aborting the
1037
+ * rest.
1038
+ */
1039
+ fire(...args: A): void {
1040
+ for (const cb of [...this.#listeners]) {
1041
+ try {
1042
+ cb(...args);
1043
+ } catch (err) {
1044
+ logger.warn(`Settings: ${this.label} hook failed`, { error: String(err) });
1045
+ }
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook<any>>> = {
1051
+ "theme.dark": value => {
1052
+ if (typeof value === "string") {
1053
+ setAutoThemeMapping("dark", value);
1054
+ }
1055
+ },
1056
+ "theme.light": value => {
1057
+ if (typeof value === "string") {
1058
+ setAutoThemeMapping("light", value);
1059
+ }
1060
+ },
1061
+ symbolPreset: value => {
1062
+ if (typeof value === "string" && (value === "unicode" || value === "nerd" || value === "ascii")) {
1063
+ setSymbolPreset(value).catch(err => {
1064
+ logger.warn("Settings: symbolPreset hook failed", { preset: value, error: String(err) });
1065
+ });
1066
+ }
1067
+ },
1068
+ colorBlindMode: value => {
1069
+ if (typeof value === "boolean") {
1070
+ setColorBlindMode(value).catch(err => {
1071
+ logger.warn("Settings: colorBlindMode hook failed", { enabled: value, error: String(err) });
1072
+ });
1073
+ }
1074
+ },
1075
+ "display.tabWidth": value => {
1076
+ if (typeof value === "number") {
1077
+ setDefaultTabWidth(value);
1078
+ }
1079
+ },
1080
+ "provider.appendOnlyContext": value => {
1081
+ if (typeof value === "string") {
1082
+ appendOnlyModeSignal.fire(value);
1083
+ }
1084
+ },
1085
+ "hindsight.bankId": () => hindsightScopeSignal.fire(),
1086
+ "hindsight.bankIdPrefix": () => hindsightScopeSignal.fire(),
1087
+ "hindsight.scoping": () => hindsightScopeSignal.fire(),
1088
+ };
1089
+ /** Fires when `provider.appendOnlyContext` changes at runtime. */
1090
+ const appendOnlyModeSignal = new SettingSignal<[value: string]>("provider.appendOnlyContext");
1091
+
1092
+ /**
1093
+ * Subscribe to append-only mode setting changes.
1094
+ * Returns an unsubscribe function. Multiple sessions (main + subagents)
1095
+ * can register independently without overwriting each other.
1096
+ */
1097
+ export const onAppendOnlyModeChanged = (cb: (value: string) => void) => appendOnlyModeSignal.on(cb);
1098
+
1099
+ /** Fires when `statusLine.sessionAccent` changes at runtime. */
1100
+ const statusLineSessionAccentSignal = new SettingSignal("statusLine.sessionAccent");
1101
+
1102
+ /**
1103
+ * Subscribe to session-accent setting changes.
1104
+ * Returns an unsubscribe function. Callers should re-read settings in the callback.
1105
+ */
1106
+ export const onStatusLineSessionAccentChanged = (cb: () => void) => statusLineSessionAccentSignal.on(cb);
1107
+
1108
+ /** Fires when any `hindsight.bankId` / `bankIdPrefix` / `scoping` value changes. */
1109
+ const hindsightScopeSignal = new SettingSignal("hindsight scope");
1110
+
1111
+ /**
1112
+ * Subscribe to changes in the Hindsight bank-scoping settings. Lets the
1113
+ * Hindsight backend rebuild the active `HindsightSessionState` when the
1114
+ * operator switches `hindsight.bankId`, `hindsight.bankIdPrefix`, or
1115
+ * `hindsight.scoping` mid-session so subsequent retain/recall calls land in
1116
+ * the new bank instead of the one selected at session start.
1117
+ *
1118
+ * Returns an unsubscribe function. The callback receives no arguments — the
1119
+ * caller is expected to re-read the relevant settings via `Settings.get`.
1120
+ */
1121
+ export const onHindsightScopeChanged = (cb: () => void) => hindsightScopeSignal.on(cb);
1122
+
1123
+ // ═══════════════════════════════════════════════════════════════════════════
1124
+ // Global Singleton
1125
+ // ═══════════════════════════════════════════════════════════════════════════
1126
+
1127
+ let globalInstance: Settings | null = null;
1128
+ let globalInstancePromise: Promise<Settings> | null = null;
1129
+ let boundSettingsInstance: Settings | null = null;
1130
+ let boundSettingsMethods = new Map<PropertyKey, unknown>();
1131
+
1132
+ function clearBoundSettingsMethods(): void {
1133
+ boundSettingsInstance = null;
1134
+ boundSettingsMethods = new Map<PropertyKey, unknown>();
1135
+ }
1136
+
1137
+ export function isSettingsInitialized(): boolean {
1138
+ return globalInstance !== null;
1139
+ }
1140
+
1141
+ /**
1142
+ * Reset the global singleton for testing.
1143
+ * @internal
1144
+ */
1145
+ export function resetSettingsForTest(): void {
1146
+ globalInstance = null;
1147
+ globalInstancePromise = null;
1148
+ clearBoundSettingsMethods();
1149
+ }
1150
+
1151
+ /**
1152
+ * The global settings singleton.
1153
+ * Must call `Settings.init()` before using.
1154
+ */
1155
+ export const settings = new Proxy({} as Settings, {
1156
+ get(_target, prop) {
1157
+ if (!globalInstance) {
1158
+ throw new Error("Settings not initialized. Call Settings.init() first.");
1159
+ }
1160
+ if (boundSettingsInstance !== globalInstance) {
1161
+ clearBoundSettingsMethods();
1162
+ boundSettingsInstance = globalInstance;
1163
+ }
1164
+ const value = (globalInstance as unknown as Record<PropertyKey, unknown>)[prop];
1165
+ if (typeof value === "function") {
1166
+ const cached = boundSettingsMethods.get(prop);
1167
+ if (cached) return cached;
1168
+ const bound = value.bind(globalInstance);
1169
+ boundSettingsMethods.set(prop, bound);
1170
+ return bound;
1171
+ }
1172
+ return value;
1173
+ },
1174
+ });
1175
+
1176
+ // ═══════════════════════════════════════════════════════════════════════════
1177
+ // Helpers
1178
+ // ═══════════════════════════════════════════════════════════════════════════