@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.12
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 +142 -7
- package/dist/cli.js +23108 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +2 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/cli-commands.d.ts +12 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/api-key-resolver.d.ts +3 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +8 -219
- package/dist/types/config/model-resolver.d.ts +34 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +41 -8
- package/dist/types/config/settings.d.ts +8 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/eval/py/executor.d.ts +5 -0
- package/dist/types/eval/py/kernel.d.ts +6 -1
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -2
- package/dist/types/extensibility/extensions/types.d.ts +6 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/runtime.d.ts +4 -0
- package/dist/types/memory-backend/types.d.ts +66 -1
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +25 -6
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/index.d.ts +3 -3
- package/dist/types/modes/interactive-mode.d.ts +8 -3
- package/dist/types/modes/oauth-manual-input.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
- package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
- package/dist/types/modes/setup-wizard/index.d.ts +5 -1
- package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +8 -2
- package/dist/types/session/agent-session.d.ts +15 -3
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/slash-commands/types.d.ts +1 -1
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/task/index.d.ts +2 -2
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/thinking.d.ts +4 -0
- package/dist/types/tiny/title-client.d.ts +11 -0
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/index.d.ts +6 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/utils/git.d.ts +15 -2
- package/dist/types/utils/title-generator.d.ts +3 -2
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/auto-thinking/classifier.ts +1 -0
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +4 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +2 -1
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +30 -0
- package/src/cli.ts +76 -13
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +5 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +4 -3
- package/src/config/api-key-resolver.ts +8 -6
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +320 -1041
- package/src/config/model-resolver.ts +173 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +61 -19
- package/src/config/settings.ts +98 -4
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/edit/diff.ts +47 -3
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +47 -4
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +3 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +40 -22
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/executor.ts +29 -7
- package/src/eval/py/index.ts +6 -3
- package/src/eval/py/kernel.ts +43 -4
- package/src/eval/py/runner.py +107 -3
- package/src/eval/py/runtime.ts +37 -0
- package/src/exec/bash-executor.ts +85 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/get-commands-handler.ts +2 -1
- package/src/extensibility/extensions/runner.ts +6 -1
- package/src/extensibility/extensions/types.ts +6 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/bank.ts +17 -2
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +183 -23
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +4 -1
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/local-backend.ts +9 -0
- package/src/memory-backend/off-backend.ts +9 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +81 -1
- package/src/mnemopi/backend.ts +176 -7
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/acp/acp-agent.ts +119 -11
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +32 -28
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +4 -2
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +119 -35
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +258 -53
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +11 -4
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +2 -1
- package/src/modes/controllers/mcp-command-controller.ts +39 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/index.ts +3 -21
- package/src/modes/interactive-mode.ts +42 -18
- package/src/modes/oauth-manual-input.ts +30 -3
- package/src/modes/rpc/rpc-client.ts +154 -3
- package/src/modes/rpc/rpc-mode.ts +97 -12
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +81 -1
- package/src/modes/setup-wizard/index.ts +12 -2
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +4 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +13 -24
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +1 -1
- package/src/sdk.ts +31 -11
- package/src/secrets/index.ts +8 -1
- package/src/secrets/obfuscator.ts +39 -18
- package/src/session/agent-session.ts +223 -64
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +188 -11
- package/src/slash-commands/acp-builtins.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +40 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/slash-commands/types.ts +1 -1
- package/src/ssh/connection-manager.ts +27 -0
- package/src/system-prompt.ts +14 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +74 -65
- package/src/task/index.ts +146 -68
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +20 -5
- package/src/task/types.ts +9 -0
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +9 -1
- package/src/tiny/title-client.ts +60 -16
- package/src/tiny/title-protocol.ts +1 -1
- package/src/tiny/worker.ts +6 -4
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +100 -18
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +14 -13
- package/src/tools/index.ts +13 -1
- package/src/tools/inspect-image.ts +1 -0
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +38 -8
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/file-mentions.ts +2 -1
- package/src/utils/git.ts +267 -13
- package/src/utils/title-generator.ts +24 -5
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
|
@@ -11,6 +11,16 @@ export const DEFAULT_MAX_LINES = 3000;
|
|
|
11
11
|
export const DEFAULT_MAX_BYTES = 50 * 1024; // 50KB
|
|
12
12
|
export const DEFAULT_MAX_COLUMN = 512; // Max chars per grep match line
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Default artifact-on-disk cap for {@link OutputSink}.
|
|
16
|
+
*
|
|
17
|
+
* `0` means unbounded: by default, `artifact://<id>` references preserve the
|
|
18
|
+
* complete raw stream instead of a capped head/tail sample.
|
|
19
|
+
*/
|
|
20
|
+
export const ARTIFACT_DEFAULT_MAX_BYTES = 0;
|
|
21
|
+
/** Default head budget; the remainder becomes the rolling tail window. */
|
|
22
|
+
export const ARTIFACT_DEFAULT_HEAD_BYTES = 3 * 1024 * 1024; // 3 MiB
|
|
23
|
+
|
|
14
24
|
const NL = "\n";
|
|
15
25
|
const ELLIPSIS = "…";
|
|
16
26
|
|
|
@@ -58,6 +68,20 @@ export interface OutputSinkOptions {
|
|
|
58
68
|
onChunk?: (chunk: string) => void;
|
|
59
69
|
/** Minimum ms between onChunk calls. 0 = every chunk (default). */
|
|
60
70
|
chunkThrottleMs?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Optional cap on bytes written to the artifact-on-disk file. When the cap
|
|
73
|
+
* is hit, the head window is preserved verbatim and subsequent output feeds
|
|
74
|
+
* a rolling tail window; on close, the sink writes a single
|
|
75
|
+
* `[ARTIFACT TRUNCATED: …]` notice between them. Default
|
|
76
|
+
* {@link ARTIFACT_DEFAULT_MAX_BYTES} (unbounded).
|
|
77
|
+
*/
|
|
78
|
+
artifactMaxBytes?: number;
|
|
79
|
+
/**
|
|
80
|
+
* Bytes reserved for the head window of the capped artifact file. The
|
|
81
|
+
* tail window receives `artifactMaxBytes - artifactHeadBytes`. Default
|
|
82
|
+
* {@link ARTIFACT_DEFAULT_HEAD_BYTES}; clamped to `[0, artifactMaxBytes]`.
|
|
83
|
+
*/
|
|
84
|
+
artifactHeadBytes?: number;
|
|
61
85
|
}
|
|
62
86
|
|
|
63
87
|
export interface TruncationResult {
|
|
@@ -650,6 +674,7 @@ export class OutputSink {
|
|
|
650
674
|
#sawData = false;
|
|
651
675
|
#truncated = false;
|
|
652
676
|
#lastChunkTime = 0;
|
|
677
|
+
#pendingChunk = "";
|
|
653
678
|
|
|
654
679
|
// Per-line column cap streaming state (persists across `push` calls so a
|
|
655
680
|
// long line split across chunks still trips the same trigger).
|
|
@@ -675,6 +700,21 @@ export class OutputSink {
|
|
|
675
700
|
readonly #chunkThrottleMs: number;
|
|
676
701
|
readonly #maxColumns: number;
|
|
677
702
|
|
|
703
|
+
// Optional artifact-on-disk cap. When `#artifactMaxBytes > 0` the file sink
|
|
704
|
+
// owns a head budget + a rolling tail buffer; once the head is closed,
|
|
705
|
+
// subsequent chunks are diverted into `#artifactTailRing` (bounded by
|
|
706
|
+
// `#artifactTailBudget`). On `dump()` the tail is flushed back to the sink
|
|
707
|
+
// behind a `[ARTIFACT TRUNCATED: …]` notice. The default cap is disabled so
|
|
708
|
+
// advertised `artifact://<id>` captures are lossless.
|
|
709
|
+
readonly #artifactMaxBytes: number;
|
|
710
|
+
readonly #artifactHeadBudget: number;
|
|
711
|
+
readonly #artifactTailBudget: number;
|
|
712
|
+
#artifactHeadBytesWritten = 0;
|
|
713
|
+
#artifactHeadClosed = false;
|
|
714
|
+
#artifactTailRing = "";
|
|
715
|
+
#artifactTailRingBytes = 0;
|
|
716
|
+
#artifactTailIncomingBytes = 0;
|
|
717
|
+
|
|
678
718
|
constructor(options?: OutputSinkOptions) {
|
|
679
719
|
const {
|
|
680
720
|
artifactPath,
|
|
@@ -684,6 +724,8 @@ export class OutputSink {
|
|
|
684
724
|
maxColumns = 0,
|
|
685
725
|
onChunk,
|
|
686
726
|
chunkThrottleMs = 0,
|
|
727
|
+
artifactMaxBytes = ARTIFACT_DEFAULT_MAX_BYTES,
|
|
728
|
+
artifactHeadBytes = ARTIFACT_DEFAULT_HEAD_BYTES,
|
|
687
729
|
} = options ?? {};
|
|
688
730
|
this.#artifactPath = artifactPath;
|
|
689
731
|
this.#artifactId = artifactId;
|
|
@@ -692,6 +734,9 @@ export class OutputSink {
|
|
|
692
734
|
this.#maxColumns = Math.max(0, maxColumns);
|
|
693
735
|
this.#onChunk = onChunk;
|
|
694
736
|
this.#chunkThrottleMs = chunkThrottleMs;
|
|
737
|
+
this.#artifactMaxBytes = Math.max(0, artifactMaxBytes);
|
|
738
|
+
this.#artifactHeadBudget = Math.max(0, Math.min(artifactHeadBytes, this.#artifactMaxBytes));
|
|
739
|
+
this.#artifactTailBudget = Math.max(0, this.#artifactMaxBytes - this.#artifactHeadBudget);
|
|
695
740
|
}
|
|
696
741
|
|
|
697
742
|
/**
|
|
@@ -701,14 +746,20 @@ export class OutputSink {
|
|
|
701
746
|
push(chunk: string): void {
|
|
702
747
|
chunk = sanitizeWithOptionalSixelPassthrough(chunk, sanitizeText);
|
|
703
748
|
|
|
704
|
-
// Throttled onChunk:
|
|
749
|
+
// Throttled onChunk: coalesce chunks arriving inside the throttle window
|
|
750
|
+
// and flush the buffered concatenation on the next eligible tick (plus a
|
|
751
|
+
// final flush in dump()) so the preview never has silent gaps.
|
|
705
752
|
// Live preview gets the raw (pre-cap) chunk so the TUI never lags behind
|
|
706
753
|
// what reached the sink — the column cap is for the persisted LLM view.
|
|
707
754
|
if (this.#onChunk) {
|
|
708
755
|
const now = Date.now();
|
|
709
756
|
if (now - this.#lastChunkTime >= this.#chunkThrottleMs) {
|
|
710
757
|
this.#lastChunkTime = now;
|
|
711
|
-
this.#
|
|
758
|
+
const merged = this.#pendingChunk + chunk;
|
|
759
|
+
this.#pendingChunk = "";
|
|
760
|
+
this.#onChunk(merged);
|
|
761
|
+
} else {
|
|
762
|
+
this.#pendingChunk += chunk;
|
|
712
763
|
}
|
|
713
764
|
}
|
|
714
765
|
|
|
@@ -858,14 +909,18 @@ export class OutputSink {
|
|
|
858
909
|
/**
|
|
859
910
|
* Write a chunk to the artifact file. Handles the async file sink creation
|
|
860
911
|
* by queuing writes until the sink is ready, then draining synchronously.
|
|
912
|
+
* Once the sink is up, every byte flows through {@link #emitToSink} which
|
|
913
|
+
* owns the head + tail cap so artifacts cannot grow beyond
|
|
914
|
+
* `#artifactMaxBytes` on disk.
|
|
861
915
|
*/
|
|
862
916
|
#writeToFile(chunk: string): void {
|
|
863
917
|
if (this.#fileReady && this.#file) {
|
|
864
|
-
|
|
865
|
-
this.#file.sink.write(chunk);
|
|
918
|
+
this.#emitToSink(chunk);
|
|
866
919
|
return;
|
|
867
920
|
}
|
|
868
|
-
// File sink not yet created — queue this chunk and kick off creation
|
|
921
|
+
// File sink not yet created — queue this chunk and kick off creation.
|
|
922
|
+
// The queue is bounded only by how many chunks arrive before the open
|
|
923
|
+
// resolves (typically <2). The cap is enforced on drain.
|
|
869
924
|
if (!this.#pendingFileWrites) {
|
|
870
925
|
this.#pendingFileWrites = [chunk];
|
|
871
926
|
void this.#createFileSink();
|
|
@@ -874,26 +929,99 @@ export class OutputSink {
|
|
|
874
929
|
}
|
|
875
930
|
}
|
|
876
931
|
|
|
932
|
+
/**
|
|
933
|
+
* Cap-aware sink writer. Bytes flow into the head window verbatim until the
|
|
934
|
+
* budget is exhausted; subsequent bytes are diverted into a rolling tail
|
|
935
|
+
* ring, evicted from the front so total RAM stays bounded by
|
|
936
|
+
* `#artifactTailBudget`. `dump()` replays the ring behind a single notice
|
|
937
|
+
* line before closing the sink.
|
|
938
|
+
*
|
|
939
|
+
* When the cap is disabled (`#artifactMaxBytes === 0`) this collapses to a
|
|
940
|
+
* straight pass-through, preserving the historical "stream everything"
|
|
941
|
+
* contract.
|
|
942
|
+
*/
|
|
943
|
+
#emitToSink(chunk: string): void {
|
|
944
|
+
if (!this.#file || chunk.length === 0) return;
|
|
945
|
+
if (this.#artifactMaxBytes === 0) {
|
|
946
|
+
this.#file.sink.write(chunk);
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const chunkBytes = Buffer.byteLength(chunk, "utf-8");
|
|
950
|
+
const room = this.#artifactHeadClosed ? 0 : this.#artifactHeadBudget - this.#artifactHeadBytesWritten;
|
|
951
|
+
if (room >= chunkBytes) {
|
|
952
|
+
this.#file.sink.write(chunk);
|
|
953
|
+
this.#artifactHeadBytesWritten += chunkBytes;
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
let overflow = chunk;
|
|
957
|
+
if (room > 0) {
|
|
958
|
+
const headSlice = truncateHeadBytes(chunk, room);
|
|
959
|
+
if (headSlice.bytes > 0) {
|
|
960
|
+
this.#file.sink.write(headSlice.text);
|
|
961
|
+
this.#artifactHeadBytesWritten += headSlice.bytes;
|
|
962
|
+
}
|
|
963
|
+
// Even when UTF-8 boundary safety leaves a few bytes of nominal room,
|
|
964
|
+
// this chunk has already overflowed the head window. Close it now so a
|
|
965
|
+
// later small ASCII chunk cannot be written before this overflow tail.
|
|
966
|
+
this.#artifactHeadClosed = true;
|
|
967
|
+
overflow = chunk.substring(headSlice.text.length);
|
|
968
|
+
}
|
|
969
|
+
if (overflow.length === 0 || this.#artifactTailBudget === 0) {
|
|
970
|
+
// No tail budget: count the dropped bytes so the notice reflects them.
|
|
971
|
+
if (overflow.length > 0) {
|
|
972
|
+
this.#artifactTailIncomingBytes += Buffer.byteLength(overflow, "utf-8");
|
|
973
|
+
}
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
this.#pushArtifactTail(overflow);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
#pushArtifactTail(chunk: string): void {
|
|
980
|
+
const chunkBytes = Buffer.byteLength(chunk, "utf-8");
|
|
981
|
+
this.#artifactTailIncomingBytes += chunkBytes;
|
|
982
|
+
const budget = this.#artifactTailBudget;
|
|
983
|
+
if (chunkBytes >= budget) {
|
|
984
|
+
// Chunk alone dominates — keep only its tail slice.
|
|
985
|
+
const { text, bytes } = truncateTailBytes(chunk, budget);
|
|
986
|
+
this.#artifactTailRing = text;
|
|
987
|
+
this.#artifactTailRingBytes = bytes;
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
this.#artifactTailRing += chunk;
|
|
991
|
+
this.#artifactTailRingBytes += chunkBytes;
|
|
992
|
+
if (this.#artifactTailRingBytes > budget) {
|
|
993
|
+
const { text, bytes } = truncateTailBytes(this.#artifactTailRing, budget);
|
|
994
|
+
this.#artifactTailRing = text;
|
|
995
|
+
this.#artifactTailRingBytes = bytes;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
877
999
|
async #createFileSink(): Promise<void> {
|
|
878
1000
|
if (!this.#artifactPath || this.#fileReady) return;
|
|
879
1001
|
try {
|
|
880
1002
|
const sink = Bun.file(this.#artifactPath).writer();
|
|
881
1003
|
this.#file = { path: this.#artifactPath, artifactId: this.#artifactId, sink };
|
|
1004
|
+
this.#fileReady = true;
|
|
1005
|
+
|
|
1006
|
+
// Head-retained bytes precede the rolling tail buffer in the capture.
|
|
1007
|
+
// Route through #emitToSink so they count against the artifact head
|
|
1008
|
+
// budget — a direct sink.write would let them escape the cap.
|
|
1009
|
+
if (this.#head.length > 0) {
|
|
1010
|
+
this.#emitToSink(this.#head);
|
|
1011
|
+
}
|
|
882
1012
|
|
|
883
1013
|
// Flush existing buffer to file BEFORE it gets trimmed further.
|
|
884
1014
|
if (this.#buffer.length > 0) {
|
|
885
|
-
|
|
1015
|
+
this.#emitToSink(this.#buffer);
|
|
886
1016
|
}
|
|
887
1017
|
|
|
888
|
-
// Drain any chunks that arrived while the sink was being created
|
|
1018
|
+
// Drain any chunks that arrived while the sink was being created.
|
|
889
1019
|
if (this.#pendingFileWrites) {
|
|
890
1020
|
for (const pending of this.#pendingFileWrites) {
|
|
891
|
-
|
|
1021
|
+
this.#emitToSink(pending);
|
|
892
1022
|
}
|
|
893
1023
|
this.#pendingFileWrites = undefined;
|
|
894
1024
|
}
|
|
895
|
-
|
|
896
|
-
this.#fileReady = true;
|
|
897
1025
|
} catch {
|
|
898
1026
|
try {
|
|
899
1027
|
await this.#file?.sink?.end();
|
|
@@ -902,6 +1030,7 @@ export class OutputSink {
|
|
|
902
1030
|
}
|
|
903
1031
|
this.#file = undefined;
|
|
904
1032
|
this.#pendingFileWrites = undefined;
|
|
1033
|
+
this.#fileReady = false;
|
|
905
1034
|
}
|
|
906
1035
|
}
|
|
907
1036
|
|
|
@@ -946,13 +1075,61 @@ export class OutputSink {
|
|
|
946
1075
|
this.#columnEllipsisAdded = false;
|
|
947
1076
|
this.#columnDroppedBytes = 0;
|
|
948
1077
|
this.#columnTruncatedLines = 0;
|
|
1078
|
+
this.#pendingChunk = "";
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Replay the rolling tail ring back into the artifact sink. When bytes
|
|
1083
|
+
* were actually dropped from the middle (the head budget was exhausted
|
|
1084
|
+
* *and* the tail ring evicted), a single `[ARTIFACT TRUNCATED: …]`
|
|
1085
|
+
* notice is injected between head and tail so a reader of
|
|
1086
|
+
* `artifact://<id>` understands the gap. When the total stream simply
|
|
1087
|
+
* spilled past the head budget but still fits below `artifactMaxBytes`,
|
|
1088
|
+
* `droppedBytes` is zero — head + tail together are the verbatim stream
|
|
1089
|
+
* and the notice is suppressed so we don't corrupt the artifact with a
|
|
1090
|
+
* misleading "0 B elided" marker (PR #2083 review by codex).
|
|
1091
|
+
*
|
|
1092
|
+
* No-op when the cap was never hit at all (head budget never exhausted,
|
|
1093
|
+
* tail ring empty).
|
|
1094
|
+
*/
|
|
1095
|
+
#flushArtifactTailIfCapped(): void {
|
|
1096
|
+
if (!this.#file) return;
|
|
1097
|
+
if (this.#artifactMaxBytes === 0) return;
|
|
1098
|
+
const tailBytes = this.#artifactTailRingBytes;
|
|
1099
|
+
const droppedBytes = Math.max(0, this.#artifactTailIncomingBytes - tailBytes);
|
|
1100
|
+
if (tailBytes === 0 && droppedBytes === 0) return;
|
|
1101
|
+
|
|
1102
|
+
if (droppedBytes > 0) {
|
|
1103
|
+
const headWritten = this.#artifactHeadBytesWritten;
|
|
1104
|
+
const totalCapped = headWritten + this.#artifactTailIncomingBytes;
|
|
1105
|
+
const headSep = headWritten > 0 ? "\n" : "";
|
|
1106
|
+
const tailSep = tailBytes > 0 && !this.#artifactTailRing.startsWith("\n") ? "\n" : "";
|
|
1107
|
+
const notice =
|
|
1108
|
+
`${headSep}[ARTIFACT TRUNCATED: kept first ${formatBytes(headWritten)} + last ${formatBytes(tailBytes)} ` +
|
|
1109
|
+
`of ${formatBytes(totalCapped)}; ${formatBytes(droppedBytes)} elided from the middle]${tailSep}`;
|
|
1110
|
+
this.#file.sink.write(notice);
|
|
1111
|
+
}
|
|
1112
|
+
if (tailBytes > 0) {
|
|
1113
|
+
this.#file.sink.write(this.#artifactTailRing);
|
|
1114
|
+
}
|
|
949
1115
|
}
|
|
950
1116
|
|
|
951
1117
|
async dump(notice?: string): Promise<OutputSummary> {
|
|
952
1118
|
const noticeLine = notice ? `[${notice}]\n` : "";
|
|
1119
|
+
|
|
1120
|
+
// Flush any chunk still held back by the throttle so the live preview
|
|
1121
|
+
// ends with the complete stream.
|
|
1122
|
+
if (this.#onChunk && this.#pendingChunk.length > 0) {
|
|
1123
|
+
const pending = this.#pendingChunk;
|
|
1124
|
+
this.#pendingChunk = "";
|
|
1125
|
+
this.#onChunk(pending);
|
|
1126
|
+
}
|
|
953
1127
|
const totalLines = this.#sawData ? this.#totalLines + 1 : 0;
|
|
954
1128
|
|
|
955
|
-
if (this.#file)
|
|
1129
|
+
if (this.#file) {
|
|
1130
|
+
this.#flushArtifactTailIfCapped();
|
|
1131
|
+
await this.#file.sink.end();
|
|
1132
|
+
}
|
|
956
1133
|
|
|
957
1134
|
// Compose the visible output. With head retention, splice head + marker
|
|
958
1135
|
// + tail when content was elided. Otherwise return the rolling buffer.
|
|
@@ -5,6 +5,30 @@ import type { AcpBuiltinSlashCommandResult, SlashCommandRuntime } from "./types"
|
|
|
5
5
|
|
|
6
6
|
export type { AcpBuiltinSlashCommandResult } from "./types";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* All names (primary + aliases) that are reserved by ACP builtins. Used to
|
|
10
|
+
* filter out extension commands that would shadow a builtin or its alias at
|
|
11
|
+
* dispatch time (e.g. `models` is an alias for `/model`, so an extension
|
|
12
|
+
* registering `models` would appear in the palette but execute the builtin).
|
|
13
|
+
*/
|
|
14
|
+
export const ACP_BUILTIN_RESERVED_NAMES: ReadonlySet<string> = new Set(
|
|
15
|
+
BUILTIN_SLASH_COMMANDS_INTERNAL.filter(c => c.handle !== undefined).flatMap(c => [c.name, ...(c.aliases ?? [])]),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether an extension command named `name` would be captured by ACP builtin
|
|
20
|
+
* dispatch before reaching the extension handler. Beyond exact name/alias
|
|
21
|
+
* collisions, `parseSlashCommand` treats `:` as a name/args separator, so a
|
|
22
|
+
* colon-namespaced name whose prefix is a handled builtin (e.g. `model:foo`)
|
|
23
|
+
* executes the `/model` builtin with `foo` as args. Such names must not be
|
|
24
|
+
* advertised to ACP clients.
|
|
25
|
+
*/
|
|
26
|
+
export function isAcpBuiltinShadowedName(name: string): boolean {
|
|
27
|
+
if (ACP_BUILTIN_RESERVED_NAMES.has(name)) return true;
|
|
28
|
+
const colon = name.indexOf(":");
|
|
29
|
+
return colon !== -1 && ACP_BUILTIN_RESERVED_NAMES.has(name.slice(0, colon));
|
|
30
|
+
}
|
|
31
|
+
|
|
8
32
|
/**
|
|
9
33
|
* Commands advertised to ACP clients. Entries without a text-mode `handle`
|
|
10
34
|
* (e.g. `/quit`, `/login`, dashboards) are filtered out so the client doesn't
|
|
@@ -30,6 +30,7 @@ import { createMarketplaceManager } from "./helpers/marketplace-manager";
|
|
|
30
30
|
import { handleMcpAcp } from "./helpers/mcp";
|
|
31
31
|
import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
|
|
32
32
|
import { handleSshAcp } from "./helpers/ssh";
|
|
33
|
+
import { launchStatsDashboard, parseStatsDashboardArgs } from "./helpers/stats-dashboard";
|
|
33
34
|
import { handleTodoAcp } from "./helpers/todo";
|
|
34
35
|
import { buildUsageReportText } from "./helpers/usage-report";
|
|
35
36
|
import { parseMarketplaceInstallArgs, parsePluginScopeArgs } from "./marketplace-install-parser";
|
|
@@ -81,6 +82,23 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
81
82
|
runtime.ctx.editor.setText("");
|
|
82
83
|
},
|
|
83
84
|
},
|
|
85
|
+
{
|
|
86
|
+
name: "setup",
|
|
87
|
+
aliases: ["providers"],
|
|
88
|
+
description: "Open provider setup",
|
|
89
|
+
allowArgs: true,
|
|
90
|
+
subcommands: [{ name: "providers", description: "Configure sign-in and web search providers" }],
|
|
91
|
+
handleTui: async (command, runtime) => {
|
|
92
|
+
const args = command.args.trim().toLowerCase();
|
|
93
|
+
const opensProviders = args === "" || args === "providers";
|
|
94
|
+
if (opensProviders) {
|
|
95
|
+
await runtime.ctx.showProviderSetup();
|
|
96
|
+
} else {
|
|
97
|
+
runtime.ctx.showWarning(`Usage: /${command.name} [providers]`);
|
|
98
|
+
}
|
|
99
|
+
runtime.ctx.editor.setText("");
|
|
100
|
+
},
|
|
101
|
+
},
|
|
84
102
|
{
|
|
85
103
|
name: "plan",
|
|
86
104
|
description: "Toggle plan mode (agent plans before executing)",
|
|
@@ -542,6 +560,25 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
542
560
|
runtime.ctx.editor.setText("");
|
|
543
561
|
},
|
|
544
562
|
},
|
|
563
|
+
{
|
|
564
|
+
name: "stats",
|
|
565
|
+
description: "Launch the local stats dashboard",
|
|
566
|
+
inlineHint: "[--port <port>]",
|
|
567
|
+
allowArgs: true,
|
|
568
|
+
handle: async (command, runtime) => {
|
|
569
|
+
const parsed = parseStatsDashboardArgs(command.args);
|
|
570
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
571
|
+
|
|
572
|
+
await runtime.output("Syncing session files...");
|
|
573
|
+
try {
|
|
574
|
+
const result = await launchStatsDashboard(parsed);
|
|
575
|
+
await runtime.output(result.message);
|
|
576
|
+
} catch (error) {
|
|
577
|
+
await runtime.output(`Stats dashboard failed: ${errorMessage(error)}`);
|
|
578
|
+
}
|
|
579
|
+
return commandConsumed();
|
|
580
|
+
},
|
|
581
|
+
},
|
|
545
582
|
{
|
|
546
583
|
name: "changelog",
|
|
547
584
|
description: "Show changelog entries",
|
|
@@ -1677,10 +1714,13 @@ for (const command of BUILTIN_SLASH_COMMAND_REGISTRY) {
|
|
|
1677
1714
|
}
|
|
1678
1715
|
}
|
|
1679
1716
|
|
|
1717
|
+
export const BUILTIN_SLASH_COMMAND_RESERVED_NAMES: ReadonlySet<string> = new Set(BUILTIN_SLASH_COMMAND_LOOKUP.keys());
|
|
1718
|
+
|
|
1680
1719
|
/** Builtin command metadata used for slash-command autocomplete and help text. */
|
|
1681
1720
|
export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = BUILTIN_SLASH_COMMAND_REGISTRY.map(
|
|
1682
1721
|
command => ({
|
|
1683
1722
|
name: command.name,
|
|
1723
|
+
aliases: command.aliases,
|
|
1684
1724
|
description: command.description,
|
|
1685
1725
|
subcommands: command.subcommands,
|
|
1686
1726
|
inlineHint: command.inlineHint,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as stats from "@oh-my-pi/omp-stats";
|
|
2
|
+
import * as openUtils from "../../utils/open";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_STATS_DASHBOARD_PORT = 3847;
|
|
5
|
+
|
|
6
|
+
interface StatsDashboardServer {
|
|
7
|
+
port: number;
|
|
8
|
+
stop: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StatsDashboardArgs {
|
|
12
|
+
port: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StatsDashboardLaunchResult {
|
|
16
|
+
url: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let activeStatsServer: StatsDashboardServer | undefined;
|
|
21
|
+
|
|
22
|
+
const STATS_DASHBOARD_USAGE = "Usage: /stats [--port <port>]";
|
|
23
|
+
|
|
24
|
+
function parsePort(value: string | undefined): number | string {
|
|
25
|
+
if (!value) return `Missing port. ${STATS_DASHBOARD_USAGE}`;
|
|
26
|
+
if (!/^\d+$/.test(value)) return `Invalid port: ${value}`;
|
|
27
|
+
const port = Number(value);
|
|
28
|
+
if (!Number.isInteger(port) || port < 0 || port > 65_535) return `Invalid port: ${value}`;
|
|
29
|
+
return port;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function parseStatsDashboardArgs(args: string): StatsDashboardArgs | { error: string } {
|
|
33
|
+
const tokens = args.split(/\s+/).filter(Boolean);
|
|
34
|
+
let port = DEFAULT_STATS_DASHBOARD_PORT;
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
37
|
+
const token = tokens[i];
|
|
38
|
+
if (token === "--port" || token === "-p") {
|
|
39
|
+
const parsed = parsePort(tokens[++i]);
|
|
40
|
+
if (typeof parsed === "string") return { error: parsed };
|
|
41
|
+
port = parsed;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (token.startsWith("--port=")) {
|
|
45
|
+
const parsed = parsePort(token.slice("--port=".length));
|
|
46
|
+
if (typeof parsed === "string") return { error: parsed };
|
|
47
|
+
port = parsed;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return { error: `Unknown option: ${token}. ${STATS_DASHBOARD_USAGE}` };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { port };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function launchStatsDashboard(args: StatsDashboardArgs): Promise<StatsDashboardLaunchResult> {
|
|
57
|
+
const { processed, files } = await stats.syncAllSessions();
|
|
58
|
+
const total = await stats.getTotalMessageCount();
|
|
59
|
+
let requestedPortIgnored = false;
|
|
60
|
+
|
|
61
|
+
if (!activeStatsServer) {
|
|
62
|
+
activeStatsServer = await stats.startServer(args.port);
|
|
63
|
+
} else if (args.port !== activeStatsServer.port) {
|
|
64
|
+
requestedPortIgnored = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const url = `http://localhost:${activeStatsServer.port}`;
|
|
68
|
+
openUtils.openPath(url);
|
|
69
|
+
|
|
70
|
+
const serverLine = requestedPortIgnored
|
|
71
|
+
? `Dashboard already running at: ${url} (requested port ${args.port} ignored)`
|
|
72
|
+
: `Dashboard available at: ${url}`;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
url,
|
|
76
|
+
message: `Synced ${processed} new entries from ${files} files (${total} total)\n${serverLine}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function stopStatsDashboard(): void {
|
|
81
|
+
if (!activeStatsServer) return;
|
|
82
|
+
activeStatsServer.stop();
|
|
83
|
+
activeStatsServer = undefined;
|
|
84
|
+
stats.closeDb();
|
|
85
|
+
}
|
|
@@ -14,6 +14,7 @@ export interface SubcommandDef {
|
|
|
14
14
|
/** Declarative builtin slash command metadata used by autocomplete and help UI. */
|
|
15
15
|
export interface BuiltinSlashCommand {
|
|
16
16
|
name: string;
|
|
17
|
+
aliases?: string[];
|
|
17
18
|
description: string;
|
|
18
19
|
/** Subcommands for dropdown completion (e.g. /mcp add, /mcp list). */
|
|
19
20
|
subcommands?: SubcommandDef[];
|
|
@@ -82,7 +83,6 @@ export interface TuiSlashCommandRuntime {
|
|
|
82
83
|
|
|
83
84
|
/** Unified slash-command spec consumed by both TUI and ACP dispatchers. */
|
|
84
85
|
export interface SlashCommandSpec extends BuiltinSlashCommand {
|
|
85
|
-
aliases?: string[];
|
|
86
86
|
/** When false, the dispatcher refuses to handle invocations that include arguments. */
|
|
87
87
|
allowArgs?: boolean;
|
|
88
88
|
/**
|
|
@@ -355,6 +355,33 @@ export async function getHostInfoForHost(host: SSHConnectionTarget): Promise<SSH
|
|
|
355
355
|
return await loadHostInfoFromDisk(host);
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Synchronous, probe-free host info lookup for startup paths.
|
|
360
|
+
*
|
|
361
|
+
* Checks the in-memory cache, then falls back to a synchronous read of the
|
|
362
|
+
* persisted host-info cache file. Never opens a connection or probes the
|
|
363
|
+
* remote host — callers get `undefined` when nothing is cached yet.
|
|
364
|
+
*/
|
|
365
|
+
export function getCachedHostInfoSync(host: SSHConnectionTarget): SSHHostInfo | undefined {
|
|
366
|
+
const cached = hostInfoCache.get(host.name);
|
|
367
|
+
if (cached) {
|
|
368
|
+
const resolved = applyCompatOverride(host, cached);
|
|
369
|
+
if (resolved !== cached) hostInfoCache.set(host.name, resolved);
|
|
370
|
+
return resolved;
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
const parsed = parseHostInfo(JSON.parse(fs.readFileSync(getHostInfoPath(host.name), "utf-8")));
|
|
374
|
+
if (!parsed) return undefined;
|
|
375
|
+
const resolved = applyCompatOverride(host, parsed);
|
|
376
|
+
hostInfoCache.set(host.name, resolved);
|
|
377
|
+
return resolved;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
if (isEnoent(err)) return undefined;
|
|
380
|
+
logger.warn("Failed to load SSH host info", { host: host.name, error: String(err) });
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
358
385
|
export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
|
|
359
386
|
const cached = hostInfoCache.get(host.name);
|
|
360
387
|
if (cached) {
|
package/src/system-prompt.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { $env, getGpuCachePath, getProjectDir, hasFsCode, isEnoent, logger, prom
|
|
|
8
8
|
import { $ } from "bun";
|
|
9
9
|
import { contextFileCapability } from "./capability/context-file";
|
|
10
10
|
import { systemPromptCapability } from "./capability/system-prompt";
|
|
11
|
+
import { findConfigFile } from "./config";
|
|
11
12
|
import type { SkillsSettings } from "./config/settings";
|
|
12
13
|
import { type ContextFile, loadCapability, type SystemPrompt as SystemPromptFile } from "./discovery";
|
|
13
14
|
import { expandAtImports } from "./discovery/at-imports";
|
|
@@ -208,6 +209,19 @@ async function getEnvironmentInfo(): Promise<Array<{ label: string; value: strin
|
|
|
208
209
|
return entries.filter((e): e is { label: string; value: string } => !!e.value);
|
|
209
210
|
}
|
|
210
211
|
|
|
212
|
+
/** Discover TITLE_SYSTEM.md file for automatic session-title prompt overrides */
|
|
213
|
+
export function discoverTitleSystemPromptFile(cwd?: string): string | undefined {
|
|
214
|
+
const projectPath = findConfigFile("TITLE_SYSTEM.md", { user: false, cwd });
|
|
215
|
+
if (projectPath) {
|
|
216
|
+
return projectPath;
|
|
217
|
+
}
|
|
218
|
+
const globalPath = findConfigFile("TITLE_SYSTEM.md", { user: true, cwd });
|
|
219
|
+
if (globalPath) {
|
|
220
|
+
return globalPath;
|
|
221
|
+
}
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
211
225
|
/** Resolve input as file path or literal string */
|
|
212
226
|
export async function resolvePromptInput(input: string | undefined, description: string): Promise<string | undefined> {
|
|
213
227
|
if (!input) {
|
package/src/task/commands.ts
CHANGED
|
@@ -120,7 +120,8 @@ export function getCommand(commands: WorkflowCommand[], name: string): WorkflowC
|
|
|
120
120
|
* Replaces $@ with the provided input.
|
|
121
121
|
*/
|
|
122
122
|
export function expandCommand(command: WorkflowCommand, input: string): string {
|
|
123
|
-
|
|
123
|
+
// Function replacement so `$`-patterns in user input ($$, $&, ...) stay literal.
|
|
124
|
+
return command.instructions.replace(/\$@/g, () => input);
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
/**
|