@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.
- package/CHANGELOG.md +10031 -0
- package/README.md +36 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +104 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/extensions/README.md +142 -0
- package/examples/extensions/api-demo.ts +79 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +31 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +549 -0
- package/examples/extensions/reload-runtime.ts +38 -0
- package/examples/extensions/thinking-note.ts +13 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +17 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +48 -0
- package/examples/hooks/confirm-destructive.ts +58 -0
- package/examples/hooks/custom-compaction.ts +115 -0
- package/examples/hooks/dirty-repo-guard.ts +51 -0
- package/examples/hooks/file-trigger.ts +40 -0
- package/examples/hooks/git-checkpoint.ts +52 -0
- package/examples/hooks/handoff.ts +149 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +118 -0
- package/examples/hooks/status-line.ts +39 -0
- package/examples/sdk/01-minimal.ts +21 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +46 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +82 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +35 -0
- package/examples/sdk/08-prompt-templates.ts +41 -0
- package/examples/sdk/08-slash-commands.ts +46 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
- package/examples/sdk/11-sessions.ts +47 -0
- package/examples/sdk/12-redis-sessions.ts +54 -0
- package/examples/sdk/13-sql-sessions.ts +61 -0
- package/examples/sdk/README.md +172 -0
- package/package.json +554 -0
- package/scripts/build-binary.ts +100 -0
- package/scripts/bundle-dist.ts +90 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +33 -0
- package/scripts/omp +42 -0
- package/scripts/omp.ts +19 -0
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +625 -0
- package/src/auto-thinking/classifier.ts +185 -0
- package/src/autoresearch/command-resume.md +14 -0
- package/src/autoresearch/dashboard.ts +436 -0
- package/src/autoresearch/git.ts +319 -0
- package/src/autoresearch/helpers.ts +218 -0
- package/src/autoresearch/index.ts +536 -0
- package/src/autoresearch/prompt-setup.md +43 -0
- package/src/autoresearch/prompt.md +103 -0
- package/src/autoresearch/resume-message.md +10 -0
- package/src/autoresearch/state.ts +273 -0
- package/src/autoresearch/storage.ts +699 -0
- package/src/autoresearch/tools/init-experiment.ts +272 -0
- package/src/autoresearch/tools/log-experiment.ts +524 -0
- package/src/autoresearch/tools/run-experiment.ts +407 -0
- package/src/autoresearch/tools/update-notes.ts +109 -0
- package/src/autoresearch/types.ts +168 -0
- package/src/bun-imports.d.ts +28 -0
- package/src/capability/context-file.ts +44 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +117 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +436 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +74 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule-buckets.ts +66 -0
- package/src/capability/rule.ts +261 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +63 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +168 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +340 -0
- package/src/cli/auth-broker-cli.ts +895 -0
- package/src/cli/auth-gateway-cli.ts +611 -0
- package/src/cli/classify-install-target.ts +76 -0
- package/src/cli/claude-trace-cli.ts +795 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/config-cli.ts +418 -0
- package/src/cli/dry-balance-cli.ts +856 -0
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/file-processor.ts +133 -0
- package/src/cli/gallery-cli.ts +230 -0
- package/src/cli/gallery-fixtures/agentic.ts +407 -0
- package/src/cli/gallery-fixtures/codeintel.ts +187 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +220 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +57 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli/grep-cli.ts +160 -0
- package/src/cli/grievances-cli.ts +256 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/list-models.ts +194 -0
- package/src/cli/plugin-cli.ts +996 -0
- package/src/cli/read-cli.ts +57 -0
- package/src/cli/session-picker.ts +79 -0
- package/src/cli/setup-cli.ts +231 -0
- package/src/cli/shell-cli.ts +176 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/startup-cwd.ts +68 -0
- package/src/cli/stats-cli.ts +238 -0
- package/src/cli/tiny-models-cli.ts +127 -0
- package/src/cli/update-cli.ts +611 -0
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli/web-search-cli.ts +132 -0
- package/src/cli/worktree-cli.ts +291 -0
- package/src/cli-commands.ts +79 -0
- package/src/cli.ts +200 -0
- package/src/commands/acp.ts +24 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/auth-broker.ts +99 -0
- package/src/commands/auth-gateway.ts +69 -0
- package/src/commands/commit.ts +46 -0
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +51 -0
- package/src/commands/install.ts +107 -0
- package/src/commands/launch.ts +169 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +38 -0
- package/src/commands/setup.ts +67 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/ssh.ts +60 -0
- package/src/commands/stats.ts +29 -0
- package/src/commands/tiny-models.ts +36 -0
- package/src/commands/update.ts +21 -0
- package/src/commands/usage.ts +35 -0
- package/src/commands/web-search.ts +42 -0
- package/src/commands/worktree.ts +56 -0
- package/src/commit/agentic/agent.ts +317 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +355 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +25 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +38 -0
- package/src/commit/agentic/state.ts +60 -0
- package/src/commit/agentic/tools/analyze-file.ts +146 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +81 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +144 -0
- package/src/commit/agentic/tools/propose-commit.ts +109 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +23 -0
- package/src/commit/agentic/tools/split-commit.ts +245 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +183 -0
- package/src/commit/analysis/conventional.ts +64 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +105 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +97 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +85 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +69 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +49 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +92 -0
- package/src/commit/pipeline.ts +243 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/shared-llm.ts +77 -0
- package/src/commit/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +58 -0
- package/src/config/api-key-resolver.ts +60 -0
- package/src/config/append-only-context-mode.ts +31 -0
- package/src/config/config-file.ts +317 -0
- package/src/config/file-lock.ts +164 -0
- package/src/config/keybindings.ts +628 -0
- package/src/config/mcp-schema.json +230 -0
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +2090 -0
- package/src/config/model-resolver.ts +1502 -0
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +226 -0
- package/src/config/models-config.ts +129 -0
- package/src/config/prompt-templates.ts +185 -0
- package/src/config/resolve-config-value.ts +94 -0
- package/src/config/settings-schema.ts +3530 -0
- package/src/config/settings.ts +1178 -0
- package/src/config.ts +242 -0
- package/src/cursor.ts +340 -0
- package/src/dap/client.ts +760 -0
- package/src/dap/config.ts +189 -0
- package/src/dap/defaults.json +212 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1441 -0
- package/src/dap/types.ts +610 -0
- package/src/debug/index.ts +515 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +908 -0
- package/src/debug/profiler.ts +162 -0
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/raw-sse-buffer.ts +273 -0
- package/src/debug/raw-sse.ts +292 -0
- package/src/debug/report-bundle.ts +374 -0
- package/src/debug/system-info.ts +111 -0
- package/src/debug/terminal-info.ts +124 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +230 -0
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/index.ts +54 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
- package/src/discovery/builtin-rules/rs-result-type.md +19 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
- package/src/discovery/builtin-rules/ts-import-type.md +42 -0
- package/src/discovery/builtin-rules/ts-no-any.md +56 -0
- package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +44 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/builtin.ts +906 -0
- package/src/discovery/claude-plugins.ts +386 -0
- package/src/discovery/claude.ts +584 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +522 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +383 -0
- package/src/discovery/github.ts +154 -0
- package/src/discovery/helpers.ts +1016 -0
- package/src/discovery/index.ts +81 -0
- package/src/discovery/mcp-json.ts +171 -0
- package/src/discovery/omp-extension-roots.ts +190 -0
- package/src/discovery/omp-plugins.ts +383 -0
- package/src/discovery/opencode.ts +398 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +29 -0
- package/src/discovery/vscode.ts +105 -0
- package/src/discovery/windsurf.ts +147 -0
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +999 -0
- package/src/edit/file-snapshot-store.ts +91 -0
- package/src/edit/hashline/block-resolver.ts +33 -0
- package/src/edit/hashline/diff.ts +290 -0
- package/src/edit/hashline/execute.ts +242 -0
- package/src/edit/hashline/filesystem.ts +130 -0
- package/src/edit/hashline/index.ts +5 -0
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/hashline/params.ts +18 -0
- package/src/edit/index.ts +571 -0
- package/src/edit/modes/apply-patch.lark +19 -0
- package/src/edit/modes/apply-patch.ts +53 -0
- package/src/edit/modes/patch.ts +1891 -0
- package/src/edit/modes/replace.ts +1137 -0
- package/src/edit/normalize.ts +345 -0
- package/src/edit/notebook.ts +242 -0
- package/src/edit/read-file.ts +25 -0
- package/src/edit/renderer.ts +769 -0
- package/src/edit/streaming.ts +517 -0
- package/src/eval/__tests__/agent-bridge.test.ts +708 -0
- package/src/eval/__tests__/bridge-timeout.test.ts +64 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/completion-bridge.test.ts +412 -0
- package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
- package/src/eval/__tests__/idle-timeout.test.ts +80 -0
- package/src/eval/__tests__/js-context-manager.test.ts +241 -0
- package/src/eval/__tests__/kernel-spawn.test.ts +103 -0
- package/src/eval/agent-bridge.ts +319 -0
- package/src/eval/backend.ts +71 -0
- package/src/eval/bridge-timeout.ts +44 -0
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/completion-bridge.ts +207 -0
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/idle-timeout.ts +91 -0
- package/src/eval/index.ts +4 -0
- package/src/eval/js/context-manager.ts +502 -0
- package/src/eval/js/executor.ts +173 -0
- package/src/eval/js/index.ts +51 -0
- package/src/eval/js/shared/helpers.ts +283 -0
- package/src/eval/js/shared/indirect-eval.ts +30 -0
- package/src/eval/js/shared/local-module-loader.ts +342 -0
- package/src/eval/js/shared/prelude.ts +2 -0
- package/src/eval/js/shared/prelude.txt +246 -0
- package/src/eval/js/shared/rewrite-imports.ts +532 -0
- package/src/eval/js/shared/runtime.ts +352 -0
- package/src/eval/js/shared/types.ts +18 -0
- package/src/eval/js/tool-bridge.ts +162 -0
- package/src/eval/js/worker-core.ts +132 -0
- package/src/eval/js/worker-entry.ts +30 -0
- package/src/eval/js/worker-protocol.ts +47 -0
- package/src/eval/py/__tests__/prelude.test.ts +19 -0
- package/src/eval/py/display.ts +71 -0
- package/src/eval/py/executor.ts +742 -0
- package/src/eval/py/index.ts +68 -0
- package/src/eval/py/kernel.ts +748 -0
- package/src/eval/py/prelude.py +658 -0
- package/src/eval/py/prelude.ts +3 -0
- package/src/eval/py/runner.py +1133 -0
- package/src/eval/py/runtime.ts +276 -0
- package/src/eval/py/spawn-options.ts +126 -0
- package/src/eval/py/tool-bridge.ts +182 -0
- package/src/eval/session-id.ts +8 -0
- package/src/eval/types.ts +48 -0
- package/src/exa/index.ts +2 -0
- package/src/exa/mcp-client.ts +370 -0
- package/src/exa/types.ts +69 -0
- package/src/exec/bash-executor.ts +419 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/non-interactive-env.ts +48 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +164 -0
- package/src/export/html/template.css +1051 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +46 -0
- package/src/export/html/template.js +2271 -0
- package/src/export/html/template.macro.ts +25 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/ttsr.ts +583 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +489 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +238 -0
- package/src/extensibility/custom-commands/types.ts +113 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +269 -0
- package/src/extensibility/custom-tools/types.ts +270 -0
- package/src/extensibility/custom-tools/wrapper.ts +47 -0
- package/src/extensibility/extensions/compact-handler.ts +40 -0
- package/src/extensibility/extensions/get-commands-handler.ts +78 -0
- package/src/extensibility/extensions/index.ts +16 -0
- package/src/extensibility/extensions/loader.ts +572 -0
- package/src/extensibility/extensions/runner.ts +922 -0
- package/src/extensibility/extensions/types.ts +1322 -0
- package/src/extensibility/extensions/wrapper.ts +223 -0
- package/src/extensibility/hooks/index.ts +5 -0
- package/src/extensibility/hooks/loader.ts +257 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +107 -0
- package/src/extensibility/hooks/types.ts +606 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +24 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +15 -0
- package/src/extensibility/plugins/doctor.ts +65 -0
- package/src/extensibility/plugins/git-url.ts +367 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +192 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +682 -0
- package/src/extensibility/plugins/loader.ts +313 -0
- package/src/extensibility/plugins/manager.ts +827 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +317 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +770 -0
- package/src/extensibility/plugins/marketplace/registry.ts +196 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +191 -0
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/shared-events.ts +343 -0
- package/src/extensibility/skills.ts +312 -0
- package/src/extensibility/slash-commands.ts +227 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/extensibility/typebox.ts +418 -0
- package/src/extensibility/utils.ts +44 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +528 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +251 -0
- package/src/hindsight/backend.ts +354 -0
- package/src/hindsight/bank.ts +156 -0
- package/src/hindsight/client.ts +598 -0
- package/src/hindsight/config.ts +175 -0
- package/src/hindsight/content.ts +210 -0
- package/src/hindsight/index.ts +8 -0
- package/src/hindsight/mental-models.ts +429 -0
- package/src/hindsight/seeds.json +32 -0
- package/src/hindsight/state.ts +488 -0
- package/src/hindsight/transcript.ts +71 -0
- package/src/index.ts +59 -0
- package/src/internal-urls/agent-protocol.ts +146 -0
- package/src/internal-urls/artifact-protocol.ts +107 -0
- package/src/internal-urls/docs-index.generated.ts +106 -0
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +25 -0
- package/src/internal-urls/issue-pr-protocol.ts +584 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +287 -0
- package/src/internal-urls/mcp-protocol.ts +151 -0
- package/src/internal-urls/memory-protocol.ts +169 -0
- package/src/internal-urls/omp-protocol.ts +93 -0
- package/src/internal-urls/parse.ts +72 -0
- package/src/internal-urls/registry-helpers.ts +25 -0
- package/src/internal-urls/router.ts +105 -0
- package/src/internal-urls/rule-protocol.ts +45 -0
- package/src/internal-urls/skill-protocol.ts +96 -0
- package/src/internal-urls/types.ts +152 -0
- package/src/internal-urls/vault-protocol.ts +936 -0
- package/src/irc/bus.ts +292 -0
- package/src/lib/xai-http.ts +124 -0
- package/src/lsp/client.ts +1193 -0
- package/src/lsp/clients/biome-client.ts +264 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +93 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +502 -0
- package/src/lsp/defaults.json +493 -0
- package/src/lsp/diagnostics-ledger.ts +51 -0
- package/src/lsp/edits.ts +267 -0
- package/src/lsp/index.ts +2477 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +694 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +455 -0
- package/src/lsp/utils.ts +718 -0
- package/src/main.ts +1325 -0
- package/src/mcp/client.ts +484 -0
- package/src/mcp/config-writer.ts +225 -0
- package/src/mcp/config.ts +365 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +122 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +1275 -0
- package/src/mcp/oauth-discovery.ts +442 -0
- package/src/mcp/oauth-flow.ts +442 -0
- package/src/mcp/render.ts +132 -0
- package/src/mcp/smithery-auth.ts +104 -0
- package/src/mcp/smithery-connect.ts +145 -0
- package/src/mcp/smithery-registry.ts +477 -0
- package/src/mcp/timeout.ts +59 -0
- package/src/mcp/tool-bridge.ts +426 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/http.ts +519 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +528 -0
- package/src/mcp/types.ts +423 -0
- package/src/memories/index.ts +1150 -0
- package/src/memories/storage.ts +577 -0
- package/src/memory-backend/index.ts +18 -0
- package/src/memory-backend/local-backend.ts +39 -0
- package/src/memory-backend/off-backend.ts +25 -0
- package/src/memory-backend/resolve.ts +25 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +166 -0
- package/src/mnemopi/backend.ts +547 -0
- package/src/mnemopi/config.ts +160 -0
- package/src/mnemopi/index.ts +3 -0
- package/src/mnemopi/state.ts +584 -0
- package/src/modes/acp/acp-agent.ts +2407 -0
- package/src/modes/acp/acp-client-bridge.ts +154 -0
- package/src/modes/acp/acp-event-mapper.ts +929 -0
- package/src/modes/acp/acp-mode.ts +23 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/agent-dashboard.ts +1206 -0
- package/src/modes/components/agent-hub.ts +1071 -0
- package/src/modes/components/assistant-message.ts +307 -0
- package/src/modes/components/bash-execution.ts +220 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/branch-summary-message.ts +45 -0
- package/src/modes/components/btw-panel.ts +104 -0
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/compaction-summary-message.ts +87 -0
- package/src/modes/components/copy-selector.ts +206 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +398 -0
- package/src/modes/components/custom-message.ts +63 -0
- package/src/modes/components/diff.ts +277 -0
- package/src/modes/components/dynamic-border.ts +34 -0
- package/src/modes/components/error-banner.ts +33 -0
- package/src/modes/components/eval-execution.ts +158 -0
- package/src/modes/components/execution-shared.ts +101 -0
- package/src/modes/components/extensions/extension-dashboard.ts +399 -0
- package/src/modes/components/extensions/extension-list.ts +502 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +317 -0
- package/src/modes/components/extensions/state-manager.ts +627 -0
- package/src/modes/components/extensions/types.ts +186 -0
- package/src/modes/components/footer.ts +274 -0
- package/src/modes/components/history-search.ts +280 -0
- package/src/modes/components/hook-editor.ts +167 -0
- package/src/modes/components/hook-input.ts +87 -0
- package/src/modes/components/hook-message.ts +66 -0
- package/src/modes/components/hook-selector.ts +660 -0
- package/src/modes/components/index.ts +38 -0
- package/src/modes/components/keybinding-hints.ts +65 -0
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/login-dialog.ts +164 -0
- package/src/modes/components/mcp-add-wizard.ts +1340 -0
- package/src/modes/components/message-frame.ts +88 -0
- package/src/modes/components/model-selector.ts +1271 -0
- package/src/modes/components/oauth-selector.ts +368 -0
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +820 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/plugin-selector.ts +95 -0
- package/src/modes/components/plugin-settings.ts +722 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +670 -0
- package/src/modes/components/segment-track.ts +52 -0
- package/src/modes/components/session-selector.ts +625 -0
- package/src/modes/components/settings-defs.ts +189 -0
- package/src/modes/components/settings-selector.ts +651 -0
- package/src/modes/components/show-images-selector.ts +45 -0
- package/src/modes/components/skill-message.ts +89 -0
- package/src/modes/components/status-line/component.ts +869 -0
- package/src/modes/components/status-line/context-thresholds.ts +79 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/index.ts +5 -0
- package/src/modes/components/status-line/presets.ts +106 -0
- package/src/modes/components/status-line/segments.ts +584 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/token-rate.ts +66 -0
- package/src/modes/components/status-line/types.ts +108 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +52 -0
- package/src/modes/components/tiny-title-download-progress.ts +90 -0
- package/src/modes/components/tips.txt +19 -0
- package/src/modes/components/todo-reminder.ts +38 -0
- package/src/modes/components/tool-execution.ts +1024 -0
- package/src/modes/components/transcript-container.ts +608 -0
- package/src/modes/components/tree-selector.ts +978 -0
- package/src/modes/components/ttsr-notification.ts +122 -0
- package/src/modes/components/user-message-selector.ts +227 -0
- package/src/modes/components/user-message.ts +66 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome.ts +493 -0
- package/src/modes/controllers/btw-controller.ts +105 -0
- package/src/modes/controllers/command-controller-shared.ts +109 -0
- package/src/modes/controllers/command-controller.ts +1566 -0
- package/src/modes/controllers/event-controller.ts +1054 -0
- package/src/modes/controllers/extension-ui-controller.ts +886 -0
- package/src/modes/controllers/input-controller.ts +1073 -0
- package/src/modes/controllers/mcp-command-controller.ts +2017 -0
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1108 -0
- package/src/modes/controllers/ssh-command-controller.ts +384 -0
- package/src/modes/controllers/streaming-reveal.ts +279 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/controllers/todo-command-controller.ts +485 -0
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/gradient-highlight.ts +87 -0
- package/src/modes/image-references.ts +117 -0
- package/src/modes/index.ts +17 -0
- package/src/modes/interactive-mode.ts +3370 -0
- package/src/modes/internal-url-autocomplete.ts +143 -0
- package/src/modes/loop-limit.ts +140 -0
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/oauth-manual-input.ts +69 -0
- package/src/modes/orchestrate.ts +42 -0
- package/src/modes/print-mode.ts +126 -0
- package/src/modes/prompt-action-autocomplete.ts +260 -0
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/host-uris.ts +235 -0
- package/src/modes/rpc/rpc-client.ts +963 -0
- package/src/modes/rpc/rpc-mode.ts +947 -0
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +458 -0
- package/src/modes/runtime-init.ts +116 -0
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +99 -0
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +69 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +205 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +299 -0
- package/src/modes/setup-wizard/scenes/types.ts +48 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +129 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
- package/src/modes/shared.ts +47 -0
- package/src/modes/theme/dark.json +95 -0
- package/src/modes/theme/defaults/alabaster.json +93 -0
- package/src/modes/theme/defaults/amethyst.json +96 -0
- package/src/modes/theme/defaults/anthracite.json +93 -0
- package/src/modes/theme/defaults/basalt.json +91 -0
- package/src/modes/theme/defaults/birch.json +95 -0
- package/src/modes/theme/defaults/dark-abyss.json +91 -0
- package/src/modes/theme/defaults/dark-arctic.json +104 -0
- package/src/modes/theme/defaults/dark-aurora.json +95 -0
- package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
- package/src/modes/theme/defaults/dark-cavern.json +91 -0
- package/src/modes/theme/defaults/dark-copper.json +95 -0
- package/src/modes/theme/defaults/dark-cosmos.json +90 -0
- package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
- package/src/modes/theme/defaults/dark-dracula.json +98 -0
- package/src/modes/theme/defaults/dark-eclipse.json +91 -0
- package/src/modes/theme/defaults/dark-ember.json +95 -0
- package/src/modes/theme/defaults/dark-equinox.json +90 -0
- package/src/modes/theme/defaults/dark-forest.json +96 -0
- package/src/modes/theme/defaults/dark-github.json +105 -0
- package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
- package/src/modes/theme/defaults/dark-lavender.json +95 -0
- package/src/modes/theme/defaults/dark-lunar.json +89 -0
- package/src/modes/theme/defaults/dark-midnight.json +95 -0
- package/src/modes/theme/defaults/dark-monochrome.json +94 -0
- package/src/modes/theme/defaults/dark-monokai.json +98 -0
- package/src/modes/theme/defaults/dark-nebula.json +90 -0
- package/src/modes/theme/defaults/dark-nord.json +97 -0
- package/src/modes/theme/defaults/dark-ocean.json +101 -0
- package/src/modes/theme/defaults/dark-one.json +100 -0
- package/src/modes/theme/defaults/dark-poimandres.json +142 -0
- package/src/modes/theme/defaults/dark-rainforest.json +91 -0
- package/src/modes/theme/defaults/dark-reef.json +91 -0
- package/src/modes/theme/defaults/dark-retro.json +92 -0
- package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
- package/src/modes/theme/defaults/dark-sakura.json +95 -0
- package/src/modes/theme/defaults/dark-slate.json +95 -0
- package/src/modes/theme/defaults/dark-solarized.json +97 -0
- package/src/modes/theme/defaults/dark-solstice.json +90 -0
- package/src/modes/theme/defaults/dark-starfall.json +91 -0
- package/src/modes/theme/defaults/dark-sunset.json +99 -0
- package/src/modes/theme/defaults/dark-swamp.json +90 -0
- package/src/modes/theme/defaults/dark-synthwave.json +103 -0
- package/src/modes/theme/defaults/dark-taiga.json +91 -0
- package/src/modes/theme/defaults/dark-terminal.json +95 -0
- package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
- package/src/modes/theme/defaults/dark-tundra.json +91 -0
- package/src/modes/theme/defaults/dark-twilight.json +91 -0
- package/src/modes/theme/defaults/dark-volcanic.json +91 -0
- package/src/modes/theme/defaults/graphite.json +92 -0
- package/src/modes/theme/defaults/index.ts +199 -0
- package/src/modes/theme/defaults/light-arctic.json +107 -0
- package/src/modes/theme/defaults/light-aurora-day.json +91 -0
- package/src/modes/theme/defaults/light-canyon.json +91 -0
- package/src/modes/theme/defaults/light-catppuccin.json +106 -0
- package/src/modes/theme/defaults/light-cirrus.json +90 -0
- package/src/modes/theme/defaults/light-coral.json +95 -0
- package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
- package/src/modes/theme/defaults/light-dawn.json +90 -0
- package/src/modes/theme/defaults/light-dunes.json +91 -0
- package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
- package/src/modes/theme/defaults/light-forest.json +100 -0
- package/src/modes/theme/defaults/light-frost.json +95 -0
- package/src/modes/theme/defaults/light-github.json +115 -0
- package/src/modes/theme/defaults/light-glacier.json +91 -0
- package/src/modes/theme/defaults/light-gruvbox.json +108 -0
- package/src/modes/theme/defaults/light-haze.json +90 -0
- package/src/modes/theme/defaults/light-honeycomb.json +95 -0
- package/src/modes/theme/defaults/light-lagoon.json +91 -0
- package/src/modes/theme/defaults/light-lavender.json +95 -0
- package/src/modes/theme/defaults/light-meadow.json +91 -0
- package/src/modes/theme/defaults/light-mint.json +95 -0
- package/src/modes/theme/defaults/light-monochrome.json +101 -0
- package/src/modes/theme/defaults/light-ocean.json +99 -0
- package/src/modes/theme/defaults/light-one.json +99 -0
- package/src/modes/theme/defaults/light-opal.json +91 -0
- package/src/modes/theme/defaults/light-orchard.json +91 -0
- package/src/modes/theme/defaults/light-paper.json +95 -0
- package/src/modes/theme/defaults/light-poimandres.json +142 -0
- package/src/modes/theme/defaults/light-prism.json +90 -0
- package/src/modes/theme/defaults/light-retro.json +98 -0
- package/src/modes/theme/defaults/light-sand.json +95 -0
- package/src/modes/theme/defaults/light-savanna.json +91 -0
- package/src/modes/theme/defaults/light-solarized.json +102 -0
- package/src/modes/theme/defaults/light-soleil.json +90 -0
- package/src/modes/theme/defaults/light-sunset.json +99 -0
- package/src/modes/theme/defaults/light-synthwave.json +98 -0
- package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
- package/src/modes/theme/defaults/light-wetland.json +91 -0
- package/src/modes/theme/defaults/light-zenith.json +89 -0
- package/src/modes/theme/defaults/limestone.json +94 -0
- package/src/modes/theme/defaults/mahogany.json +97 -0
- package/src/modes/theme/defaults/marble.json +93 -0
- package/src/modes/theme/defaults/obsidian.json +91 -0
- package/src/modes/theme/defaults/onyx.json +91 -0
- package/src/modes/theme/defaults/pearl.json +93 -0
- package/src/modes/theme/defaults/porcelain.json +91 -0
- package/src/modes/theme/defaults/quartz.json +96 -0
- package/src/modes/theme/defaults/sandstone.json +95 -0
- package/src/modes/theme/defaults/titanium.json +90 -0
- package/src/modes/theme/light.json +93 -0
- package/src/modes/theme/mermaid-cache.ts +29 -0
- package/src/modes/theme/shimmer.ts +235 -0
- package/src/modes/theme/theme-schema.json +459 -0
- package/src/modes/theme/theme.ts +2676 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +359 -0
- package/src/modes/ultrathink.ts +41 -0
- package/src/modes/utils/context-usage.ts +339 -0
- package/src/modes/utils/copy-targets.ts +360 -0
- package/src/modes/utils/hotkeys-markdown.ts +61 -0
- package/src/modes/utils/keybinding-matchers.ts +51 -0
- package/src/modes/utils/tools-markdown.ts +27 -0
- package/src/modes/utils/ui-helpers.ts +801 -0
- package/src/modes/workflow.ts +42 -0
- package/src/plan-mode/approved-plan.ts +186 -0
- package/src/plan-mode/plan-handoff.ts +37 -0
- package/src/plan-mode/plan-protection.ts +31 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +41 -0
- package/src/prompts/agents/designer.md +66 -0
- package/src/prompts/agents/explore.md +58 -0
- package/src/prompts/agents/frontmatter.md +11 -0
- package/src/prompts/agents/init.md +33 -0
- package/src/prompts/agents/librarian.md +119 -0
- package/src/prompts/agents/oracle.md +55 -0
- package/src/prompts/agents/plan.md +48 -0
- package/src/prompts/agents/reviewer.md +140 -0
- package/src/prompts/agents/task.md +16 -0
- package/src/prompts/ci-green-request.md +36 -0
- package/src/prompts/dry-balance-bench.md +8 -0
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/read-path.md +11 -0
- package/src/prompts/memories/stage_one_input.md +6 -0
- package/src/prompts/memories/stage_one_system.md +21 -0
- package/src/prompts/review-custom-request.md +22 -0
- package/src/prompts/review-headless-request.md +16 -0
- package/src/prompts/review-request.md +69 -0
- package/src/prompts/steering/user-interjection.md +10 -0
- package/src/prompts/system/agent-creation-architect.md +50 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-continue.md +1 -0
- package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
- package/src/prompts/system/auto-thinking-difficulty.md +12 -0
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +14 -0
- package/src/prompts/system/custom-system-prompt.md +64 -0
- package/src/prompts/system/eager-todo.md +13 -0
- package/src/prompts/system/empty-stop-retry.md +6 -0
- package/src/prompts/system/irc-incoming.md +7 -0
- package/src/prompts/system/manual-continue.md +7 -0
- package/src/prompts/system/memory-consolidation-system.md +8 -0
- package/src/prompts/system/memory-extraction-system.md +26 -0
- package/src/prompts/system/omfg-user.md +50 -0
- package/src/prompts/system/orchestrate-notice.md +40 -0
- package/src/prompts/system/plan-mode-active.md +109 -0
- package/src/prompts/system/plan-mode-approved.md +25 -0
- package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
- package/src/prompts/system/plan-mode-reference.md +11 -0
- package/src/prompts/system/plan-mode-subagent.md +33 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/project-prompt.md +52 -0
- package/src/prompts/system/subagent-system-prompt.md +64 -0
- package/src/prompts/system/subagent-user-prompt.md +3 -0
- package/src/prompts/system/subagent-yield-reminder.md +12 -0
- package/src/prompts/system/system-prompt.md +258 -0
- package/src/prompts/system/tiny-title-system.md +8 -0
- package/src/prompts/system/title-system.md +16 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/ttsr-tool-reminder.md +5 -0
- package/src/prompts/system/ultrathink-notice.md +3 -0
- package/src/prompts/system/web-search.md +25 -0
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/apply-patch.md +65 -0
- package/src/prompts/tools/ask.md +30 -0
- package/src/prompts/tools/ast-edit.md +39 -0
- package/src/prompts/tools/ast-grep.md +42 -0
- package/src/prompts/tools/async-result.md +8 -0
- package/src/prompts/tools/bash.md +46 -0
- package/src/prompts/tools/browser.md +73 -0
- package/src/prompts/tools/checkpoint.md +16 -0
- package/src/prompts/tools/debug.md +34 -0
- package/src/prompts/tools/eval.md +92 -0
- package/src/prompts/tools/find.md +36 -0
- package/src/prompts/tools/github.md +21 -0
- package/src/prompts/tools/goal.md +18 -0
- package/src/prompts/tools/image-gen.md +7 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +32 -0
- package/src/prompts/tools/irc.md +59 -0
- package/src/prompts/tools/job.md +19 -0
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/prompts/tools/lsp.md +42 -0
- package/src/prompts/tools/memory-edit.md +8 -0
- package/src/prompts/tools/patch.md +70 -0
- package/src/prompts/tools/read.md +84 -0
- package/src/prompts/tools/recall.md +5 -0
- package/src/prompts/tools/reflect.md +5 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/prompts/tools/replace.md +30 -0
- package/src/prompts/tools/resolve.md +9 -0
- package/src/prompts/tools/retain.md +6 -0
- package/src/prompts/tools/rewind.md +13 -0
- package/src/prompts/tools/search-tool-bm25.md +32 -0
- package/src/prompts/tools/search.md +24 -0
- package/src/prompts/tools/ssh.md +31 -0
- package/src/prompts/tools/task-summary.md +17 -0
- package/src/prompts/tools/task.md +88 -0
- package/src/prompts/tools/todo.md +62 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +151 -0
- package/src/sdk.ts +2558 -0
- package/src/secrets/index.ts +123 -0
- package/src/secrets/obfuscator.ts +298 -0
- package/src/secrets/regex.ts +21 -0
- package/src/session/agent-session.ts +10121 -0
- package/src/session/agent-storage.ts +455 -0
- package/src/session/artifacts.ts +135 -0
- package/src/session/auth-broker-config.ts +131 -0
- package/src/session/auth-storage.ts +29 -0
- package/src/session/blob-store.ts +255 -0
- package/src/session/client-bridge.ts +85 -0
- package/src/session/history-storage.ts +348 -0
- package/src/session/indexed-session-storage.ts +430 -0
- package/src/session/messages.ts +541 -0
- package/src/session/redis-session-storage.ts +170 -0
- package/src/session/session-dump-format.ts +209 -0
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +3676 -0
- package/src/session/session-storage.ts +529 -0
- package/src/session/shake-types.ts +43 -0
- package/src/session/sql-session-storage.ts +314 -0
- package/src/session/streaming-output.ts +1330 -0
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/session/yield-queue.ts +173 -0
- package/src/slash-commands/acp-builtins.ts +70 -0
- package/src/slash-commands/builtin-registry.ts +1798 -0
- package/src/slash-commands/helpers/context-report.ts +39 -0
- package/src/slash-commands/helpers/format.ts +46 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +532 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/ssh.ts +195 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/slash-commands/helpers/todo.ts +279 -0
- package/src/slash-commands/helpers/usage-report.ts +95 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/types.ts +135 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +509 -0
- package/src/ssh/ssh-executor.ts +189 -0
- package/src/ssh/sshfs-mount.ts +140 -0
- package/src/ssh/utils.ts +8 -0
- package/src/stt/downloader.ts +71 -0
- package/src/stt/index.ts +3 -0
- package/src/stt/recorder.ts +351 -0
- package/src/stt/setup.ts +52 -0
- package/src/stt/stt-controller.ts +160 -0
- package/src/stt/transcribe.py +70 -0
- package/src/stt/transcriber.ts +91 -0
- package/src/stubs/natives/index.ts +814 -0
- package/src/stubs/natives/package.json +7 -0
- package/src/stubs/tui/index.ts +282 -0
- package/src/stubs/tui/package.json +7 -0
- package/src/system-prompt.ts +611 -0
- package/src/task/agents.ts +167 -0
- package/src/task/commands.ts +132 -0
- package/src/task/discovery.ts +122 -0
- package/src/task/executor.ts +2133 -0
- package/src/task/index.ts +1419 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/omp-command.ts +26 -0
- package/src/task/output-manager.ts +88 -0
- package/src/task/parallel.ts +116 -0
- package/src/task/render.ts +1381 -0
- package/src/task/repair-args.ts +129 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/types.ts +336 -0
- package/src/task/worktree.ts +514 -0
- package/src/telemetry-export.ts +144 -0
- package/src/thinking.ts +167 -0
- package/src/tiny/compiled-runtime.ts +179 -0
- package/src/tiny/device.ts +111 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/models.ts +242 -0
- package/src/tiny/text.ts +165 -0
- package/src/tiny/title-client.ts +543 -0
- package/src/tiny/title-protocol.ts +56 -0
- package/src/tiny/worker.ts +568 -0
- package/src/tool-discovery/mode.ts +24 -0
- package/src/tool-discovery/tool-index.ts +256 -0
- package/src/tools/approval.ts +189 -0
- package/src/tools/archive-reader.ts +721 -0
- package/src/tools/ask.ts +928 -0
- package/src/tools/ast-edit.ts +642 -0
- package/src/tools/ast-grep.ts +452 -0
- package/src/tools/auto-generated-guard.ts +322 -0
- package/src/tools/bash-command-fixup.ts +37 -0
- package/src/tools/bash-interactive.ts +408 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-pty-selection.ts +14 -0
- package/src/tools/bash-skill-urls.ts +248 -0
- package/src/tools/bash.ts +1386 -0
- package/src/tools/browser/attach.ts +175 -0
- package/src/tools/browser/launch.ts +660 -0
- package/src/tools/browser/readable.ts +112 -0
- package/src/tools/browser/registry.ts +197 -0
- package/src/tools/browser/render.ts +216 -0
- package/src/tools/browser/tab-protocol.ts +105 -0
- package/src/tools/browser/tab-supervisor.ts +628 -0
- package/src/tools/browser/tab-worker-entry.ts +21 -0
- package/src/tools/browser/tab-worker.ts +1226 -0
- package/src/tools/browser.ts +343 -0
- package/src/tools/checkpoint.ts +136 -0
- package/src/tools/conflict-detect.ts +718 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/debug.ts +1067 -0
- package/src/tools/eval-backends.ts +27 -0
- package/src/tools/eval-render.ts +752 -0
- package/src/tools/eval.ts +577 -0
- package/src/tools/fetch.ts +1926 -0
- package/src/tools/file-recorder.ts +35 -0
- package/src/tools/find.ts +609 -0
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/gh-cache-invalidation.ts +255 -0
- package/src/tools/gh-format.ts +12 -0
- package/src/tools/gh-renderer.ts +481 -0
- package/src/tools/gh.ts +3720 -0
- package/src/tools/github-cache.ts +637 -0
- package/src/tools/grouped-file-output.ts +210 -0
- package/src/tools/image-gen.ts +1517 -0
- package/src/tools/index.ts +599 -0
- package/src/tools/inspect-image-renderer.ts +132 -0
- package/src/tools/inspect-image.ts +174 -0
- package/src/tools/irc.ts +723 -0
- package/src/tools/job.ts +557 -0
- package/src/tools/json-tree.ts +243 -0
- package/src/tools/jtd-to-json-schema.ts +219 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/memory-edit.ts +59 -0
- package/src/tools/memory-recall.ts +100 -0
- package/src/tools/memory-reflect.ts +88 -0
- package/src/tools/memory-render.ts +202 -0
- package/src/tools/memory-retain.ts +91 -0
- package/src/tools/output-meta.ts +754 -0
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/path-utils.ts +1054 -0
- package/src/tools/plan-mode-guard.ts +108 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
- package/src/tools/read.ts +2929 -0
- package/src/tools/render-mermaid.ts +69 -0
- package/src/tools/render-utils.ts +838 -0
- package/src/tools/renderers.ts +77 -0
- package/src/tools/report-tool-issue.ts +534 -0
- package/src/tools/resolve.ts +276 -0
- package/src/tools/review.ts +253 -0
- package/src/tools/search-tool-bm25.ts +351 -0
- package/src/tools/search.ts +1580 -0
- package/src/tools/sqlite-reader.ts +828 -0
- package/src/tools/ssh.ts +349 -0
- package/src/tools/todo.ts +982 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +94 -0
- package/src/tools/tool-timeouts.ts +30 -0
- package/src/tools/tts.ts +133 -0
- package/src/tools/write.ts +1217 -0
- package/src/tools/yield.ts +269 -0
- package/src/tui/code-cell.ts +216 -0
- package/src/tui/file-list.ts +55 -0
- package/src/tui/hyperlink.ts +175 -0
- package/src/tui/index.ts +12 -0
- package/src/tui/output-block.ts +240 -0
- package/src/tui/status-line.ts +54 -0
- package/src/tui/tree-list.ts +84 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/utils/block-context.ts +312 -0
- package/src/utils/changelog.ts +132 -0
- package/src/utils/clipboard.ts +193 -0
- package/src/utils/command-args.ts +76 -0
- package/src/utils/commit-message-generator.ts +151 -0
- package/src/utils/edit-mode.ts +41 -0
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +65 -0
- package/src/utils/file-display-mode.ts +45 -0
- package/src/utils/file-mentions.ts +281 -0
- package/src/utils/git.ts +1833 -0
- package/src/utils/image-loading.ts +132 -0
- package/src/utils/image-resize.ts +309 -0
- package/src/utils/jj.ts +248 -0
- package/src/utils/lang-from-path.ts +239 -0
- package/src/utils/markit.ts +89 -0
- package/src/utils/open.ts +55 -0
- package/src/utils/session-color.ts +68 -0
- package/src/utils/shell-snapshot.ts +187 -0
- package/src/utils/sixel.ts +69 -0
- package/src/utils/title-generator.ts +373 -0
- package/src/utils/tool-choice.ts +33 -0
- package/src/utils/tools-manager.ts +363 -0
- package/src/web/kagi.ts +305 -0
- package/src/web/parallel.ts +353 -0
- package/src/web/scrapers/artifacthub.ts +207 -0
- package/src/web/scrapers/arxiv.ts +83 -0
- package/src/web/scrapers/aur.ts +162 -0
- package/src/web/scrapers/biorxiv.ts +133 -0
- package/src/web/scrapers/bluesky.ts +262 -0
- package/src/web/scrapers/brew.ts +172 -0
- package/src/web/scrapers/cheatsh.ts +68 -0
- package/src/web/scrapers/chocolatey.ts +196 -0
- package/src/web/scrapers/choosealicense.ts +95 -0
- package/src/web/scrapers/cisa-kev.ts +87 -0
- package/src/web/scrapers/clojars.ts +154 -0
- package/src/web/scrapers/coingecko.ts +177 -0
- package/src/web/scrapers/crates-io.ts +97 -0
- package/src/web/scrapers/crossref.ts +136 -0
- package/src/web/scrapers/devto.ts +147 -0
- package/src/web/scrapers/discogs.ts +306 -0
- package/src/web/scrapers/discourse.ts +197 -0
- package/src/web/scrapers/dockerhub.ts +138 -0
- package/src/web/scrapers/docs-rs.ts +653 -0
- package/src/web/scrapers/fdroid.ts +134 -0
- package/src/web/scrapers/firefox-addons.ts +191 -0
- package/src/web/scrapers/flathub.ts +223 -0
- package/src/web/scrapers/github-gist.ts +58 -0
- package/src/web/scrapers/github.ts +704 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +140 -0
- package/src/web/scrapers/hackernews.ts +189 -0
- package/src/web/scrapers/hex.ts +105 -0
- package/src/web/scrapers/huggingface.ts +321 -0
- package/src/web/scrapers/iacr.ts +89 -0
- package/src/web/scrapers/index.ts +252 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
- package/src/web/scrapers/lemmy.ts +203 -0
- package/src/web/scrapers/lobsters.ts +175 -0
- package/src/web/scrapers/mastodon.ts +292 -0
- package/src/web/scrapers/maven.ts +138 -0
- package/src/web/scrapers/mdn.ts +173 -0
- package/src/web/scrapers/metacpan.ts +222 -0
- package/src/web/scrapers/musicbrainz.ts +250 -0
- package/src/web/scrapers/npm.ts +98 -0
- package/src/web/scrapers/nuget.ts +183 -0
- package/src/web/scrapers/nvd.ts +222 -0
- package/src/web/scrapers/ollama.ts +239 -0
- package/src/web/scrapers/open-vsx.ts +106 -0
- package/src/web/scrapers/opencorporates.ts +292 -0
- package/src/web/scrapers/openlibrary.ts +336 -0
- package/src/web/scrapers/orcid.ts +286 -0
- package/src/web/scrapers/osv.ts +176 -0
- package/src/web/scrapers/packagist.ts +160 -0
- package/src/web/scrapers/pub-dev.ts +143 -0
- package/src/web/scrapers/pubmed.ts +211 -0
- package/src/web/scrapers/pypi.ts +112 -0
- package/src/web/scrapers/rawg.ts +110 -0
- package/src/web/scrapers/readthedocs.ts +120 -0
- package/src/web/scrapers/reddit.ts +95 -0
- package/src/web/scrapers/repology.ts +251 -0
- package/src/web/scrapers/rfc.ts +201 -0
- package/src/web/scrapers/rubygems.ts +103 -0
- package/src/web/scrapers/searchcode.ts +189 -0
- package/src/web/scrapers/sec-edgar.ts +261 -0
- package/src/web/scrapers/semantic-scholar.ts +171 -0
- package/src/web/scrapers/snapcraft.ts +187 -0
- package/src/web/scrapers/sourcegraph.ts +336 -0
- package/src/web/scrapers/spdx.ts +108 -0
- package/src/web/scrapers/spotify.ts +198 -0
- package/src/web/scrapers/stackoverflow.ts +120 -0
- package/src/web/scrapers/terraform.ts +277 -0
- package/src/web/scrapers/tldr.ts +47 -0
- package/src/web/scrapers/twitter.ts +94 -0
- package/src/web/scrapers/types.ts +397 -0
- package/src/web/scrapers/utils.ts +109 -0
- package/src/web/scrapers/vimeo.ts +133 -0
- package/src/web/scrapers/vscode-marketplace.ts +187 -0
- package/src/web/scrapers/w3c.ts +156 -0
- package/src/web/scrapers/wikidata.ts +344 -0
- package/src/web/scrapers/wikipedia.ts +84 -0
- package/src/web/scrapers/youtube.ts +325 -0
- package/src/web/search/index.ts +292 -0
- package/src/web/search/provider.ts +157 -0
- package/src/web/search/providers/anthropic.ts +318 -0
- package/src/web/search/providers/base.ts +89 -0
- package/src/web/search/providers/brave.ts +152 -0
- package/src/web/search/providers/codex.ts +591 -0
- package/src/web/search/providers/exa.ts +400 -0
- package/src/web/search/providers/gemini.ts +460 -0
- package/src/web/search/providers/jina.ts +111 -0
- package/src/web/search/providers/kagi.ts +86 -0
- package/src/web/search/providers/kimi.ts +196 -0
- package/src/web/search/providers/parallel.ts +225 -0
- package/src/web/search/providers/perplexity.ts +730 -0
- package/src/web/search/providers/searxng.ts +313 -0
- package/src/web/search/providers/synthetic.ts +114 -0
- package/src/web/search/providers/tavily.ts +176 -0
- package/src/web/search/providers/utils.ts +128 -0
- package/src/web/search/providers/zai.ts +333 -0
- package/src/web/search/render.ts +262 -0
- package/src/web/search/types.ts +482 -0
- package/src/web/search/utils.ts +17 -0
- 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
|
+
// ═══════════════════════════════════════════════════════════════════════════
|