@f5-sales-demo/xcsh 19.51.2
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 +7073 -0
- package/README.md +14 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +109 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/todo/index.ts +206 -0
- package/examples/extensions/README.md +143 -0
- package/examples/extensions/api-demo.ts +89 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +32 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +551 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/todo.ts +296 -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 +16 -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 +43 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +80 -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/README.md +172 -0
- package/package.json +539 -0
- package/scripts/build-info/resolvers.ts +51 -0
- package/scripts/capture-ax-fixture.ts +84 -0
- package/scripts/capture-login-fixture.ts +30 -0
- package/scripts/check-workflow-field-coverage.ts +101 -0
- package/scripts/extension-uat-harness.ts +340 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-api-spec-index.ts +580 -0
- package/scripts/generate-branding-index.ts +81 -0
- package/scripts/generate-build-info.ts +106 -0
- package/scripts/generate-console-catalog.ts +88 -0
- package/scripts/generate-console-field-metadata.ts +74 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +32 -0
- package/scripts/generate-terraform-index.ts +81 -0
- package/scripts/mermaid-gallery.ts +132 -0
- package/scripts/uat-matrix-modalities.ts +451 -0
- package/scripts/uat-matrix-report.ts +175 -0
- package/scripts/uat-matrix.ts +353 -0
- package/src/async/index.ts +2 -0
- package/src/async/job-manager.ts +416 -0
- package/src/async/support.ts +5 -0
- package/src/autoresearch/apply-contract-to-state.ts +1 -0
- package/src/autoresearch/command-resume.md +17 -0
- package/src/autoresearch/contract.ts +205 -0
- package/src/autoresearch/dashboard.ts +374 -0
- package/src/autoresearch/git.ts +191 -0
- package/src/autoresearch/helpers.ts +328 -0
- package/src/autoresearch/index.ts +402 -0
- package/src/autoresearch/prompt.md +236 -0
- package/src/autoresearch/resume-message.md +16 -0
- package/src/autoresearch/state.ts +303 -0
- package/src/autoresearch/tools/init-experiment.ts +400 -0
- package/src/autoresearch/tools/log-experiment.ts +793 -0
- package/src/autoresearch/tools/run-experiment.ts +679 -0
- package/src/autoresearch/types.ts +114 -0
- package/src/browser/acquire.ts +249 -0
- package/src/browser/actions.ts +126 -0
- package/src/browser/auth.ts +176 -0
- package/src/browser/ax.ts +134 -0
- package/src/browser/cdp-core.ts +45 -0
- package/src/browser/cdp-page-actions.ts +51 -0
- package/src/browser/chrome-locate.ts +66 -0
- package/src/browser/dom-context.ts +89 -0
- package/src/browser/dt-context.ts +100 -0
- package/src/browser/extension-bridge.ts +182 -0
- package/src/browser/extension-page-actions.ts +397 -0
- package/src/browser/extension-provider.ts +373 -0
- package/src/browser/index.ts +17 -0
- package/src/browser/input-commit.ts +74 -0
- package/src/browser/native-messaging.ts +20 -0
- package/src/browser/page-actions.ts +18 -0
- package/src/browser/provider.ts +155 -0
- package/src/browser/resolver.ts +126 -0
- package/src/browser/selector.ts +40 -0
- package/src/bun-imports.d.ts +22 -0
- package/src/capability/context-file.ts +43 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +107 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +438 -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.ts +224 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +50 -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 +286 -0
- package/src/cli/chrome-cli.ts +88 -0
- package/src/cli/classify-install-target.ts +50 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/config-cli.ts +418 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/grep-cli.ts +160 -0
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/jupyter-cli.ts +106 -0
- package/src/cli/list-models.ts +128 -0
- package/src/cli/plugin-cli.ts +964 -0
- package/src/cli/read-cli.ts +68 -0
- package/src/cli/session-picker.ts +52 -0
- package/src/cli/setup-cli.ts +479 -0
- package/src/cli/shell-cli.ts +174 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/stats-cli.ts +182 -0
- package/src/cli/update-cli.ts +446 -0
- package/src/cli/web-search-cli.ts +203 -0
- package/src/cli.ts +123 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/chrome-host.ts +71 -0
- package/src/commands/chrome.ts +28 -0
- package/src/commands/commit.ts +36 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +20 -0
- package/src/commands/jupyter.ts +32 -0
- package/src/commands/launch.ts +144 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +33 -0
- package/src/commands/setup.ts +42 -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/update.ts +21 -0
- package/src/commands/web-search.ts +46 -0
- package/src/commit/agentic/agent.ts +314 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +354 -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 +127 -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 +139 -0
- package/src/commit/agentic/tools/propose-commit.ts +122 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +251 -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 +122 -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 +101 -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 +108 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +66 -0
- package/src/commit/pipeline.ts +242 -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/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +44 -0
- package/src/config/auto-config.ts +698 -0
- package/src/config/context-schema.json +115 -0
- package/src/config/file-lock.ts +121 -0
- package/src/config/keybindings.ts +493 -0
- package/src/config/mcp-schema.json +230 -0
- package/src/config/model-equivalence.ts +675 -0
- package/src/config/model-registry.ts +2265 -0
- package/src/config/model-resolver.ts +1289 -0
- package/src/config/prompt-templates.ts +271 -0
- package/src/config/resolve-config-value.ts +110 -0
- package/src/config/settings-schema.ts +2155 -0
- package/src/config/settings.ts +730 -0
- package/src/config.ts +418 -0
- package/src/cursor.ts +323 -0
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/index.ts +440 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +908 -0
- package/src/debug/profiler.ts +158 -0
- package/src/debug/report-bundle.ts +365 -0
- package/src/debug/system-info.ts +107 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +219 -0
- package/src/discovery/builtin.ts +851 -0
- package/src/discovery/claude-plugins.ts +297 -0
- package/src/discovery/claude.ts +543 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +535 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +396 -0
- package/src/discovery/github.ts +118 -0
- package/src/discovery/helpers.ts +934 -0
- package/src/discovery/index.ts +79 -0
- package/src/discovery/language.ts +46 -0
- package/src/discovery/mcp-json.ts +171 -0
- package/src/discovery/opencode.ts +393 -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/diff.ts +818 -0
- package/src/edit/index.ts +481 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +762 -0
- package/src/edit/modes/hashline.ts +1366 -0
- package/src/edit/modes/patch.ts +1791 -0
- package/src/edit/modes/replace.ts +1115 -0
- package/src/edit/normalize.ts +375 -0
- package/src/edit/renderer.ts +627 -0
- package/src/exa/factory.ts +61 -0
- package/src/exa/index.ts +27 -0
- package/src/exa/mcp-client.ts +293 -0
- package/src/exa/render.ts +232 -0
- package/src/exa/researcher.ts +47 -0
- package/src/exa/search.ts +86 -0
- package/src/exa/types.ts +166 -0
- package/src/exa/websets.ts +247 -0
- package/src/exec/bash-executor.ts +332 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +48 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +161 -0
- package/src/export/html/template.css +1001 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +46 -0
- package/src/export/html/template.js +1950 -0
- package/src/export/html/template.macro.ts +24 -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 +434 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +25 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +456 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +236 -0
- package/src/extensibility/custom-commands/types.ts +111 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +238 -0
- package/src/extensibility/custom-tools/types.ts +251 -0
- package/src/extensibility/custom-tools/wrapper.ts +47 -0
- package/src/extensibility/extensions/index.ts +15 -0
- package/src/extensibility/extensions/loader.ts +572 -0
- package/src/extensibility/extensions/runner.ts +848 -0
- package/src/extensibility/extensions/types.ts +1394 -0
- package/src/extensibility/extensions/wrapper.ts +191 -0
- package/src/extensibility/hooks/index.ts +5 -0
- package/src/extensibility/hooks/loader.ts +255 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +108 -0
- package/src/extensibility/hooks/types.ts +826 -0
- package/src/extensibility/plugins/doctor.ts +66 -0
- package/src/extensibility/plugins/git-url.ts +281 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +192 -0
- package/src/extensibility/plugins/loader.ts +267 -0
- package/src/extensibility/plugins/manager.ts +731 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +318 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +781 -0
- package/src/extensibility/plugins/marketplace/prerequisites.ts +255 -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 +201 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/skills.ts +269 -0
- package/src/extensibility/slash-commands.ts +246 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +61 -0
- package/src/internal-urls/agent-protocol.ts +136 -0
- package/src/internal-urls/api-catalog-resolve.ts +495 -0
- package/src/internal-urls/api-catalog-types.ts +94 -0
- package/src/internal-urls/api-spec-resolve.ts +1036 -0
- package/src/internal-urls/api-spec-types.ts +239 -0
- package/src/internal-urls/artifact-protocol.ts +97 -0
- package/src/internal-urls/branding-index.generated.ts +88 -0
- package/src/internal-urls/build-info-runtime.ts +302 -0
- package/src/internal-urls/build-info.generated.ts +33 -0
- package/src/internal-urls/computer-profile.ts +692 -0
- package/src/internal-urls/console-catalog-types.ts +21 -0
- package/src/internal-urls/console-catalog.generated.ts +532 -0
- package/src/internal-urls/console-field-metadata-types.ts +59 -0
- package/src/internal-urls/console-field-metadata.generated.ts +4841 -0
- package/src/internal-urls/console-resolve.ts +178 -0
- package/src/internal-urls/docs-index.generated.ts +698 -0
- package/src/internal-urls/extension-api.md +147 -0
- package/src/internal-urls/index.ts +46 -0
- package/src/internal-urls/jobs-protocol.ts +119 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +206 -0
- package/src/internal-urls/mcp-protocol.ts +156 -0
- package/src/internal-urls/memory-protocol.ts +133 -0
- package/src/internal-urls/parse.ts +72 -0
- package/src/internal-urls/profile-collectors.ts +106 -0
- package/src/internal-urls/router.ts +55 -0
- package/src/internal-urls/rule-protocol.ts +55 -0
- package/src/internal-urls/skill-protocol.ts +111 -0
- package/src/internal-urls/terraform-index.generated.ts +1852 -0
- package/src/internal-urls/terraform-resolve.ts +184 -0
- package/src/internal-urls/terraform-types.ts +45 -0
- package/src/internal-urls/types.ts +52 -0
- package/src/internal-urls/user-profile.ts +363 -0
- package/src/internal-urls/xcsh-protocol.ts +644 -0
- package/src/ipy/cancellation.ts +28 -0
- package/src/ipy/executor.ts +1155 -0
- package/src/ipy/gateway-coordinator.ts +424 -0
- package/src/ipy/kernel.ts +1091 -0
- package/src/ipy/modules.ts +144 -0
- package/src/ipy/prelude.py +849 -0
- package/src/ipy/prelude.ts +3 -0
- package/src/ipy/runtime.ts +221 -0
- package/src/locales/ar.json +512 -0
- package/src/locales/de.json +512 -0
- package/src/locales/en.json +549 -0
- package/src/locales/es.json +512 -0
- package/src/locales/fr.json +512 -0
- package/src/locales/hi.json +512 -0
- package/src/locales/index.ts +29 -0
- package/src/locales/it.json +512 -0
- package/src/locales/ja.json +512 -0
- package/src/locales/ko.json +512 -0
- package/src/locales/pt-br.json +512 -0
- package/src/locales/th.json +512 -0
- package/src/locales/zh-cn.json +512 -0
- package/src/locales/zh-tw.json +512 -0
- package/src/lsp/client.ts +941 -0
- package/src/lsp/clients/biome-client.ts +202 -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 +418 -0
- package/src/lsp/defaults.json +999 -0
- package/src/lsp/edits.ts +109 -0
- package/src/lsp/index.ts +1746 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +692 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +444 -0
- package/src/lsp/utils.ts +691 -0
- package/src/main.ts +944 -0
- package/src/mcp/client.ts +482 -0
- package/src/mcp/config-writer.ts +225 -0
- package/src/mcp/config.ts +365 -0
- package/src/mcp/discoverable-tool-metadata.ts +202 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +84 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +1152 -0
- package/src/mcp/oauth-discovery.ts +349 -0
- package/src/mcp/oauth-flow.ts +387 -0
- package/src/mcp/render.ts +157 -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/tool-bridge.ts +417 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/http.ts +475 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +325 -0
- package/src/mcp/types.ts +423 -0
- package/src/memories/index.ts +1115 -0
- package/src/memories/storage.ts +577 -0
- package/src/modes/acp/acp-agent.ts +1361 -0
- package/src/modes/acp/acp-event-mapper.ts +538 -0
- package/src/modes/acp/acp-mode.ts +15 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/components/agent-dashboard.ts +1124 -0
- package/src/modes/components/assistant-message.ts +213 -0
- package/src/modes/components/bash-execution.ts +345 -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 +103 -0
- package/src/modes/components/compaction-summary-message.ts +51 -0
- package/src/modes/components/context-add-wizard.ts +534 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +238 -0
- package/src/modes/components/custom-message.ts +91 -0
- package/src/modes/components/diff.ts +210 -0
- package/src/modes/components/dynamic-border.ts +25 -0
- package/src/modes/components/extensions/extension-dashboard.ts +326 -0
- package/src/modes/components/extensions/extension-list.ts +492 -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 +587 -0
- package/src/modes/components/extensions/types.ts +191 -0
- package/src/modes/components/footer.ts +262 -0
- package/src/modes/components/gutter-block.ts +315 -0
- package/src/modes/components/history-search.ts +158 -0
- package/src/modes/components/hook-editor.ts +151 -0
- package/src/modes/components/hook-input.ts +81 -0
- package/src/modes/components/hook-message.ts +100 -0
- package/src/modes/components/hook-selector.ts +192 -0
- package/src/modes/components/index.ts +36 -0
- package/src/modes/components/keybinding-hints.ts +65 -0
- package/src/modes/components/login-dialog.ts +164 -0
- package/src/modes/components/mcp-add-wizard.ts +1314 -0
- package/src/modes/components/model-selector.ts +946 -0
- package/src/modes/components/oauth-selector.ts +211 -0
- package/src/modes/components/plugin-selector.ts +95 -0
- package/src/modes/components/plugin-settings.ts +477 -0
- package/src/modes/components/plugins/index.ts +5 -0
- package/src/modes/components/plugins/plugin-dashboard.ts +523 -0
- package/src/modes/components/plugins/plugin-inspector-pane.ts +84 -0
- package/src/modes/components/plugins/plugin-list-pane.ts +109 -0
- package/src/modes/components/plugins/state-manager.ts +229 -0
- package/src/modes/components/plugins/types.ts +51 -0
- package/src/modes/components/plugins/utils.ts +6 -0
- package/src/modes/components/python-execution.ts +244 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +137 -0
- package/src/modes/components/session-observer-overlay.ts +483 -0
- package/src/modes/components/session-selector.ts +343 -0
- package/src/modes/components/settings-defs.ts +568 -0
- package/src/modes/components/settings-selector.ts +641 -0
- package/src/modes/components/show-images-selector.ts +45 -0
- package/src/modes/components/skill-message.ts +90 -0
- package/src/modes/components/status-line/context-gradient.ts +64 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/hex-ansi.ts +29 -0
- package/src/modes/components/status-line/index.ts +4 -0
- package/src/modes/components/status-line/presets.ts +174 -0
- package/src/modes/components/status-line/segments.ts +494 -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 +96 -0
- package/src/modes/components/status-line-segment-editor.ts +361 -0
- package/src/modes/components/status-line.ts +729 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +52 -0
- package/src/modes/components/todo-reminder.ts +54 -0
- package/src/modes/components/tool-execution.ts +781 -0
- package/src/modes/components/tree-selector.ts +908 -0
- package/src/modes/components/ttsr-notification.ts +80 -0
- package/src/modes/components/user-message-selector.ts +141 -0
- package/src/modes/components/user-message.ts +100 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome-checks.ts +313 -0
- package/src/modes/components/welcome.ts +270 -0
- package/src/modes/controllers/btw-controller.ts +193 -0
- package/src/modes/controllers/chord-routing.ts +37 -0
- package/src/modes/controllers/command-controller.ts +1293 -0
- package/src/modes/controllers/context-command-controller.ts +61 -0
- package/src/modes/controllers/event-controller.ts +743 -0
- package/src/modes/controllers/extension-ui-controller.ts +983 -0
- package/src/modes/controllers/input-controller.ts +877 -0
- package/src/modes/controllers/mcp-command-controller.ts +1940 -0
- package/src/modes/controllers/selector-controller.ts +1292 -0
- package/src/modes/controllers/ssh-command-controller.ts +421 -0
- package/src/modes/index.ts +34 -0
- package/src/modes/interactive-mode.ts +1781 -0
- package/src/modes/oauth-manual-input.ts +42 -0
- package/src/modes/print-mode.ts +196 -0
- package/src/modes/prompt-action-autocomplete.ts +231 -0
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +762 -0
- package/src/modes/rpc/rpc-mode.ts +877 -0
- package/src/modes/rpc/rpc-types.ts +322 -0
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +30 -0
- package/src/modes/theme/defaults/index.ts +7 -0
- package/src/modes/theme/defaults/xcsh-dark.json +113 -0
- package/src/modes/theme/defaults/xcsh-light.json +115 -0
- package/src/modes/theme/mermaid-cache.ts +151 -0
- package/src/modes/theme/mermaid-palette.ts +39 -0
- package/src/modes/theme/theme-schema.json +553 -0
- package/src/modes/theme/theme.ts +2563 -0
- package/src/modes/types.ts +273 -0
- package/src/modes/utils/hotkeys-markdown.ts +60 -0
- package/src/modes/utils/keybinding-matchers.ts +30 -0
- package/src/modes/utils/read-group-outcome-aggregator.ts +55 -0
- package/src/modes/utils/sanitize-error-message.ts +60 -0
- package/src/modes/utils/tools-markdown.ts +28 -0
- package/src/modes/utils/ui-helpers.ts +734 -0
- package/src/plan-mode/approved-plan.ts +55 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +37 -0
- package/src/prompts/agents/designer.md +67 -0
- package/src/prompts/agents/explore.md +61 -0
- package/src/prompts/agents/frontmatter.md +10 -0
- package/src/prompts/agents/init.md +36 -0
- package/src/prompts/agents/librarian.md +121 -0
- package/src/prompts/agents/plan.md +49 -0
- package/src/prompts/agents/reviewer.md +127 -0
- package/src/prompts/agents/task.md +16 -0
- package/src/prompts/ci-green-request.md +35 -0
- package/src/prompts/compaction/branch-summary-context.md +5 -0
- package/src/prompts/compaction/branch-summary-preamble.md +2 -0
- package/src/prompts/compaction/branch-summary.md +30 -0
- package/src/prompts/compaction/compaction-short-summary.md +9 -0
- package/src/prompts/compaction/compaction-summary-context.md +5 -0
- package/src/prompts/compaction/compaction-summary.md +38 -0
- package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
- package/src/prompts/compaction/compaction-update-summary.md +45 -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-request.md +70 -0
- package/src/prompts/system/agent-creation-architect.md +65 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-handoff-threshold-focus.md +1 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +2 -0
- package/src/prompts/system/custom-system-prompt.md +82 -0
- package/src/prompts/system/eager-todo.md +13 -0
- package/src/prompts/system/file-operations.md +10 -0
- package/src/prompts/system/handoff-document.md +48 -0
- package/src/prompts/system/plan-mode-active.md +114 -0
- package/src/prompts/system/plan-mode-approved.md +23 -0
- package/src/prompts/system/plan-mode-reference.md +14 -0
- package/src/prompts/system/plan-mode-subagent.md +34 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/subagent-submit-reminder.md +7 -0
- package/src/prompts/system/subagent-system-prompt.md +36 -0
- package/src/prompts/system/subagent-user-prompt.md +12 -0
- package/src/prompts/system/summarization-system.md +3 -0
- package/src/prompts/system/system-prompt.md +698 -0
- package/src/prompts/system/title-system.md +2 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/web-search.md +28 -0
- package/src/prompts/tools/ask.md +28 -0
- package/src/prompts/tools/ast-edit.md +48 -0
- package/src/prompts/tools/ast-grep.md +54 -0
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +67 -0
- package/src/prompts/tools/browser.md +26 -0
- package/src/prompts/tools/calculator.md +10 -0
- package/src/prompts/tools/cancel-job.md +5 -0
- package/src/prompts/tools/catalog-workflow-runner.md +13 -0
- package/src/prompts/tools/checkpoint.md +16 -0
- package/src/prompts/tools/chunk-edit.md +279 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/display-image.md +23 -0
- package/src/prompts/tools/exit-plan-mode.md +41 -0
- package/src/prompts/tools/find.md +26 -0
- package/src/prompts/tools/gemini-image.md +7 -0
- package/src/prompts/tools/grep.md +29 -0
- package/src/prompts/tools/hashline.md +131 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +32 -0
- package/src/prompts/tools/lsp.md +33 -0
- package/src/prompts/tools/patch.md +77 -0
- package/src/prompts/tools/poll.md +5 -0
- package/src/prompts/tools/python.md +60 -0
- package/src/prompts/tools/read-chunk.md +35 -0
- package/src/prompts/tools/read.md +63 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/prompts/tools/replace.md +36 -0
- package/src/prompts/tools/resolve.md +8 -0
- package/src/prompts/tools/rewind.md +13 -0
- package/src/prompts/tools/search-tool-bm25.md +34 -0
- package/src/prompts/tools/ssh.md +44 -0
- package/src/prompts/tools/task-summary.md +28 -0
- package/src/prompts/tools/task.md +131 -0
- package/src/prompts/tools/todo-write.md +89 -0
- package/src/prompts/tools/vim.md +98 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/prompts/tools/xcsh-api.md +283 -0
- package/src/resource-management/index.ts +42 -0
- package/src/sdk.ts +2123 -0
- package/src/secrets/index.ts +146 -0
- package/src/secrets/obfuscator.ts +290 -0
- package/src/secrets/regex.ts +21 -0
- package/src/services/context-env.ts +102 -0
- package/src/services/xcsh-api-client.ts +230 -0
- package/src/services/xcsh-context-command.ts +803 -0
- package/src/services/xcsh-context-display.ts +49 -0
- package/src/services/xcsh-context-indicators.ts +17 -0
- package/src/services/xcsh-context-segment.ts +39 -0
- package/src/services/xcsh-context.ts +1410 -0
- package/src/services/xcsh-env.ts +107 -0
- package/src/services/xcsh-knowledge.ts +177 -0
- package/src/services/xcsh-table.ts +164 -0
- package/src/session/agent-session.ts +6690 -0
- package/src/session/agent-storage.ts +451 -0
- package/src/session/artifacts.ts +132 -0
- package/src/session/auth-storage.ts +17 -0
- package/src/session/blob-store.ts +135 -0
- package/src/session/compaction/branch-summarization.ts +322 -0
- package/src/session/compaction/compaction.ts +1388 -0
- package/src/session/compaction/index.ts +7 -0
- package/src/session/compaction/pruning.ts +91 -0
- package/src/session/compaction/utils.ts +184 -0
- package/src/session/history-storage.ts +253 -0
- package/src/session/messages.ts +597 -0
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +2816 -0
- package/src/session/session-storage.ts +370 -0
- package/src/session/streaming-output.ts +770 -0
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +1614 -0
- package/src/slash-commands/export-command.ts +178 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/resource-commands.ts +250 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +445 -0
- package/src/ssh/ssh-executor.ts +131 -0
- package/src/ssh/sshfs-mount.ts +137 -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/system-prompt.ts +688 -0
- package/src/task/agents.ts +166 -0
- package/src/task/commands.ts +131 -0
- package/src/task/discovery.ts +126 -0
- package/src/task/executor.ts +1324 -0
- package/src/task/index.ts +1197 -0
- package/src/task/isolation-backend.ts +72 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/output-manager.ts +107 -0
- package/src/task/parallel.ts +116 -0
- package/src/task/render.ts +1035 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/template.ts +33 -0
- package/src/task/types.ts +230 -0
- package/src/task/worktree.ts +580 -0
- package/src/task/xcsh-command.ts +26 -0
- package/src/thinking.ts +87 -0
- package/src/tools/action-renderer.ts +198 -0
- package/src/tools/archive-reader.ts +315 -0
- package/src/tools/ask.ts +791 -0
- package/src/tools/ast-edit.ts +492 -0
- package/src/tools/ast-grep.ts +453 -0
- package/src/tools/auto-generated-guard.ts +305 -0
- package/src/tools/bash-interactive.ts +383 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-normalize.ts +107 -0
- package/src/tools/bash-skill-urls.ts +246 -0
- package/src/tools/bash.ts +878 -0
- package/src/tools/browser-renderer.ts +129 -0
- package/src/tools/browser.ts +1366 -0
- package/src/tools/calculator.ts +543 -0
- package/src/tools/cancel-job.ts +100 -0
- package/src/tools/catalog-workflow-runner.ts +718 -0
- package/src/tools/checkpoint.ts +133 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/debug.ts +1004 -0
- package/src/tools/display-image-renderer.ts +96 -0
- package/src/tools/display-image.ts +109 -0
- package/src/tools/exit-plan-mode.ts +101 -0
- package/src/tools/fetch.ts +1462 -0
- package/src/tools/find.ts +437 -0
- package/src/tools/fs-cache-invalidation.ts +33 -0
- package/src/tools/gemini-image.ts +1004 -0
- package/src/tools/grep.ts +705 -0
- package/src/tools/index.ts +442 -0
- package/src/tools/inspect-image-renderer.ts +101 -0
- package/src/tools/inspect-image.ts +176 -0
- package/src/tools/json-tree.ts +266 -0
- package/src/tools/jtd-to-json-schema.ts +199 -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/mermaid-renderer.ts +127 -0
- package/src/tools/notebook.ts +288 -0
- package/src/tools/output-meta.ts +588 -0
- package/src/tools/path-utils.ts +560 -0
- package/src/tools/plan-mode-guard.ts +41 -0
- package/src/tools/poll-tool.ts +177 -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/python.ts +1184 -0
- package/src/tools/read.ts +1591 -0
- package/src/tools/render-mermaid.ts +93 -0
- package/src/tools/render-utils.ts +677 -0
- package/src/tools/renderers.ts +82 -0
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +196 -0
- package/src/tools/review.ts +238 -0
- package/src/tools/search-tool-bm25.ts +288 -0
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/ssh.ts +322 -0
- package/src/tools/submit-result.ts +229 -0
- package/src/tools/todo-render.ts +33 -0
- package/src/tools/todo-write.ts +440 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +93 -0
- package/src/tools/tool-timeouts.ts +30 -0
- package/src/tools/vim.ts +981 -0
- package/src/tools/write.ts +671 -0
- package/src/tools/xcsh-api-renderer.ts +402 -0
- package/src/tools/xcsh-api.ts +887 -0
- package/src/tui/code-cell.ts +101 -0
- package/src/tui/file-list.ts +47 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +175 -0
- package/src/tui/status-line.ts +39 -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/changelog.ts +98 -0
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/command-args.ts +76 -0
- package/src/utils/commit-message-generator.ts +137 -0
- package/src/utils/edit-mode.ts +50 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +65 -0
- package/src/utils/file-display-mode.ts +37 -0
- package/src/utils/file-mentions.ts +376 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/git.ts +1495 -0
- package/src/utils/gitstatus.ts +140 -0
- package/src/utils/image-convert.ts +27 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/image-passthrough.ts +118 -0
- package/src/utils/image-resize.ts +272 -0
- package/src/utils/image-viewer.ts +17 -0
- package/src/utils/lang-from-path.ts +239 -0
- package/src/utils/markit.ts +81 -0
- package/src/utils/open.ts +20 -0
- package/src/utils/session-color.ts +55 -0
- package/src/utils/shell-snapshot.ts +188 -0
- package/src/utils/title-generator.ts +193 -0
- package/src/utils/tool-choice.ts +28 -0
- package/src/utils/tools-manager.ts +356 -0
- package/src/vim/buffer.ts +309 -0
- package/src/vim/commands.ts +382 -0
- package/src/vim/engine.ts +2426 -0
- package/src/vim/parser.ts +151 -0
- package/src/vim/render.ts +252 -0
- package/src/vim/types.ts +197 -0
- package/src/web/kagi.ts +174 -0
- package/src/web/parallel.ts +346 -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 +110 -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 +452 -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 +169 -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 +190 -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 +93 -0
- package/src/web/scrapers/types.ts +267 -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 +313 -0
- package/src/web/search/errors.ts +120 -0
- package/src/web/search/index.ts +346 -0
- package/src/web/search/params.ts +129 -0
- package/src/web/search/provider.ts +97 -0
- package/src/web/search/providers/anthropic.ts +339 -0
- package/src/web/search/providers/base.ts +35 -0
- package/src/web/search/providers/brave.ts +150 -0
- package/src/web/search/providers/codex.ts +402 -0
- package/src/web/search/providers/exa.ts +264 -0
- package/src/web/search/providers/firecrawl.ts +115 -0
- package/src/web/search/providers/gemini.ts +566 -0
- package/src/web/search/providers/jina.ts +99 -0
- package/src/web/search/providers/kagi.ts +70 -0
- package/src/web/search/providers/kimi.ts +157 -0
- package/src/web/search/providers/parallel.ts +63 -0
- package/src/web/search/providers/perplexity.ts +546 -0
- package/src/web/search/providers/synthetic.ts +113 -0
- package/src/web/search/providers/tavily.ts +162 -0
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +311 -0
- package/src/web/search/render.ts +348 -0
- package/src/web/search/types.ts +438 -0
- package/src/web/search/utils.ts +17 -0
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import type { ThinkingLevel } from "@f5xc-salesdemos/pi-agent-core";
|
|
5
|
+
import { FileType, glob } from "@f5xc-salesdemos/pi-natives";
|
|
6
|
+
import {
|
|
7
|
+
CONFIG_DIR_NAME,
|
|
8
|
+
getConfigDirName,
|
|
9
|
+
getProjectDir,
|
|
10
|
+
parseFrontmatter,
|
|
11
|
+
tryParseJson,
|
|
12
|
+
} from "@f5xc-salesdemos/pi-utils";
|
|
13
|
+
import { readDirEntries, readFile } from "../capability/fs";
|
|
14
|
+
import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../capability/rule";
|
|
15
|
+
import type { Skill, SkillFrontmatter } from "../capability/skill";
|
|
16
|
+
import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
|
|
17
|
+
import { parseThinkingLevel } from "../thinking";
|
|
18
|
+
|
|
19
|
+
import { buildPluginDirRoot } from "./plugin-dir-roots";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Standard paths for each config source.
|
|
23
|
+
*/
|
|
24
|
+
export const SOURCE_PATHS = {
|
|
25
|
+
native: {
|
|
26
|
+
get userBase() {
|
|
27
|
+
return getConfigDirName();
|
|
28
|
+
},
|
|
29
|
+
get userAgent() {
|
|
30
|
+
return `${getConfigDirName()}/agent`;
|
|
31
|
+
},
|
|
32
|
+
projectDir: CONFIG_DIR_NAME,
|
|
33
|
+
},
|
|
34
|
+
codex: {
|
|
35
|
+
userBase: ".codex",
|
|
36
|
+
userAgent: ".codex",
|
|
37
|
+
projectDir: ".codex",
|
|
38
|
+
},
|
|
39
|
+
gemini: {
|
|
40
|
+
userBase: ".gemini",
|
|
41
|
+
userAgent: ".gemini",
|
|
42
|
+
projectDir: ".gemini",
|
|
43
|
+
},
|
|
44
|
+
opencode: {
|
|
45
|
+
userBase: ".config/opencode",
|
|
46
|
+
userAgent: ".config/opencode",
|
|
47
|
+
projectDir: ".opencode",
|
|
48
|
+
},
|
|
49
|
+
cursor: {
|
|
50
|
+
userBase: ".cursor",
|
|
51
|
+
userAgent: ".cursor",
|
|
52
|
+
projectDir: ".cursor",
|
|
53
|
+
},
|
|
54
|
+
windsurf: {
|
|
55
|
+
userBase: ".codeium/windsurf",
|
|
56
|
+
userAgent: ".codeium/windsurf",
|
|
57
|
+
projectDir: ".windsurf",
|
|
58
|
+
},
|
|
59
|
+
cline: {
|
|
60
|
+
userBase: ".cline",
|
|
61
|
+
userAgent: ".cline",
|
|
62
|
+
projectDir: null, // Cline uses root-level .clinerules
|
|
63
|
+
},
|
|
64
|
+
github: {
|
|
65
|
+
userBase: null,
|
|
66
|
+
userAgent: null,
|
|
67
|
+
projectDir: ".github",
|
|
68
|
+
},
|
|
69
|
+
vscode: {
|
|
70
|
+
userBase: ".vscode",
|
|
71
|
+
userAgent: ".vscode",
|
|
72
|
+
projectDir: ".vscode",
|
|
73
|
+
},
|
|
74
|
+
} as const;
|
|
75
|
+
|
|
76
|
+
export type SourceId = keyof typeof SOURCE_PATHS;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get user-level path for a source.
|
|
80
|
+
*/
|
|
81
|
+
export function getUserPath(ctx: LoadContext, source: SourceId, subpath: string): string | null {
|
|
82
|
+
const paths = SOURCE_PATHS[source];
|
|
83
|
+
if (!paths.userAgent) return null;
|
|
84
|
+
return path.join(ctx.home, paths.userAgent, subpath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get project-level path for a source (cwd only).
|
|
89
|
+
*/
|
|
90
|
+
export function getProjectPath(ctx: LoadContext, source: SourceId, subpath: string): string | null {
|
|
91
|
+
const paths = SOURCE_PATHS[source];
|
|
92
|
+
if (!paths.projectDir) return null;
|
|
93
|
+
|
|
94
|
+
return path.join(ctx.cwd, paths.projectDir, subpath);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create source metadata for an item.
|
|
99
|
+
*/
|
|
100
|
+
export function createSourceMeta(provider: string, filePath: string, level: "user" | "project"): SourceMeta {
|
|
101
|
+
return {
|
|
102
|
+
provider,
|
|
103
|
+
providerName: "", // Filled in by registry
|
|
104
|
+
path: path.resolve(filePath),
|
|
105
|
+
level,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function parseBoolean(value: unknown): boolean | undefined {
|
|
110
|
+
if (typeof value === "boolean") return value;
|
|
111
|
+
if (typeof value === "string") {
|
|
112
|
+
const normalized = value.trim().toLowerCase();
|
|
113
|
+
if (normalized === "true") return true;
|
|
114
|
+
if (normalized === "false") return false;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Parse a comma-separated string into an array of trimmed, non-empty strings.
|
|
121
|
+
*/
|
|
122
|
+
export function parseCSV(value: string): string[] {
|
|
123
|
+
return value
|
|
124
|
+
.split(",")
|
|
125
|
+
.map(s => s.trim())
|
|
126
|
+
.filter(Boolean);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Parse a value that may be an array of strings or a comma-separated string.
|
|
131
|
+
* Returns undefined if the result would be empty.
|
|
132
|
+
*/
|
|
133
|
+
export function parseArrayOrCSV(value: unknown): string[] | undefined {
|
|
134
|
+
if (Array.isArray(value)) {
|
|
135
|
+
const filtered = value.filter((item): item is string => typeof item === "string");
|
|
136
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
137
|
+
}
|
|
138
|
+
if (typeof value === "string") {
|
|
139
|
+
const parsed = parseCSV(value);
|
|
140
|
+
return parsed.length > 0 ? parsed : undefined;
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Build a canonical rule item from a markdown/markdown-frontmatter document.
|
|
147
|
+
*/
|
|
148
|
+
export function buildRuleFromMarkdown(
|
|
149
|
+
name: string,
|
|
150
|
+
content: string,
|
|
151
|
+
filePath: string,
|
|
152
|
+
source: SourceMeta,
|
|
153
|
+
options?: {
|
|
154
|
+
ruleName?: string;
|
|
155
|
+
stripNamePattern?: RegExp;
|
|
156
|
+
},
|
|
157
|
+
): Rule {
|
|
158
|
+
const { frontmatter, body } = parseFrontmatter(content, { source: filePath });
|
|
159
|
+
const { condition, scope } = parseRuleConditionAndScope(frontmatter as RuleFrontmatter);
|
|
160
|
+
|
|
161
|
+
let globs: string[] | undefined;
|
|
162
|
+
if (Array.isArray(frontmatter.globs)) {
|
|
163
|
+
globs = frontmatter.globs.filter((item): item is string => typeof item === "string");
|
|
164
|
+
} else if (typeof frontmatter.globs === "string") {
|
|
165
|
+
globs = [frontmatter.globs];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const resolvedName = options?.ruleName ?? name.replace(options?.stripNamePattern ?? /\.(md|mdc)$/, "");
|
|
169
|
+
const rawMode = frontmatter.interruptMode;
|
|
170
|
+
const interruptMode: Rule["interruptMode"] =
|
|
171
|
+
rawMode === "never" || rawMode === "prose-only" || rawMode === "tool-only" || rawMode === "always"
|
|
172
|
+
? rawMode
|
|
173
|
+
: undefined;
|
|
174
|
+
return {
|
|
175
|
+
name: resolvedName,
|
|
176
|
+
path: filePath,
|
|
177
|
+
content: body,
|
|
178
|
+
globs,
|
|
179
|
+
alwaysApply: frontmatter.alwaysApply === true,
|
|
180
|
+
description: typeof frontmatter.description === "string" ? frontmatter.description : undefined,
|
|
181
|
+
condition,
|
|
182
|
+
scope,
|
|
183
|
+
interruptMode,
|
|
184
|
+
_source: source,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Parse model field into a prioritized list.
|
|
190
|
+
*/
|
|
191
|
+
export function parseModelList(value: unknown): string[] | undefined {
|
|
192
|
+
const parsed = parseArrayOrCSV(value);
|
|
193
|
+
if (!parsed) return undefined;
|
|
194
|
+
const normalized = parsed.map(entry => entry.trim()).filter(Boolean);
|
|
195
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Parsed agent fields from frontmatter (excludes source/filePath/systemPrompt) */
|
|
199
|
+
export interface ParsedAgentFields {
|
|
200
|
+
name: string;
|
|
201
|
+
description: string;
|
|
202
|
+
tools?: string[];
|
|
203
|
+
spawns?: string[] | "*";
|
|
204
|
+
model?: string[];
|
|
205
|
+
output?: unknown;
|
|
206
|
+
thinkingLevel?: ThinkingLevel;
|
|
207
|
+
blocking?: boolean;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Parse agent fields from frontmatter.
|
|
212
|
+
* Returns null if required fields (name, description) are missing.
|
|
213
|
+
*/
|
|
214
|
+
export function parseAgentFields(frontmatter: Record<string, unknown>): ParsedAgentFields | null {
|
|
215
|
+
const name = typeof frontmatter.name === "string" ? frontmatter.name : undefined;
|
|
216
|
+
const description = typeof frontmatter.description === "string" ? frontmatter.description : undefined;
|
|
217
|
+
|
|
218
|
+
if (!name || !description) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let tools = parseArrayOrCSV(frontmatter.tools)?.map(tool => tool.toLowerCase());
|
|
223
|
+
|
|
224
|
+
// Subagents with explicit tool lists always need submit_result
|
|
225
|
+
if (tools && !tools.includes("submit_result")) {
|
|
226
|
+
tools = [...tools, "submit_result"];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Parse spawns field (array, "*", or CSV)
|
|
230
|
+
let spawns: string[] | "*" | undefined;
|
|
231
|
+
if (frontmatter.spawns === "*") {
|
|
232
|
+
spawns = "*";
|
|
233
|
+
} else if (typeof frontmatter.spawns === "string") {
|
|
234
|
+
const trimmed = frontmatter.spawns.trim();
|
|
235
|
+
if (trimmed === "*") {
|
|
236
|
+
spawns = "*";
|
|
237
|
+
} else {
|
|
238
|
+
spawns = parseArrayOrCSV(trimmed);
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
spawns = parseArrayOrCSV(frontmatter.spawns);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Backward compat: infer spawns: "*" when tools includes "task"
|
|
245
|
+
if (spawns === undefined && tools?.includes("task")) {
|
|
246
|
+
spawns = "*";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const output = frontmatter.output !== undefined ? frontmatter.output : undefined;
|
|
250
|
+
const rawThinkingLevel =
|
|
251
|
+
typeof frontmatter.thinkingLevel === "string"
|
|
252
|
+
? frontmatter.thinkingLevel
|
|
253
|
+
: typeof frontmatter.thinking === "string"
|
|
254
|
+
? frontmatter.thinking
|
|
255
|
+
: undefined;
|
|
256
|
+
|
|
257
|
+
const thinkingLevel = parseThinkingLevel(rawThinkingLevel);
|
|
258
|
+
const model = parseModelList(frontmatter.model);
|
|
259
|
+
const blocking = parseBoolean(frontmatter.blocking);
|
|
260
|
+
return { name, description, tools, spawns, model, output, thinkingLevel, blocking };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function globIf(
|
|
264
|
+
dir: string,
|
|
265
|
+
pattern: string,
|
|
266
|
+
fileType: FileType,
|
|
267
|
+
recursive: boolean = true,
|
|
268
|
+
): Promise<Array<{ path: string }>> {
|
|
269
|
+
try {
|
|
270
|
+
const result = await glob({ pattern, path: dir, gitignore: true, hidden: false, fileType, recursive });
|
|
271
|
+
return result.matches;
|
|
272
|
+
} catch {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface ScanSkillsFromDirOptions {
|
|
278
|
+
dir: string;
|
|
279
|
+
providerId: string;
|
|
280
|
+
level: "user" | "project";
|
|
281
|
+
requireDescription?: boolean;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Stable ordering used for skill lists in prompts: name (case-insensitive), then name, then path.
|
|
285
|
+
export function compareSkillOrder(aName: string, aPath: string, bName: string, bPath: string): number {
|
|
286
|
+
const cmp = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);
|
|
287
|
+
const lowerCompare = cmp(aName.toLowerCase(), bName.toLowerCase());
|
|
288
|
+
if (lowerCompare !== 0) return lowerCompare;
|
|
289
|
+
const nameCompare = cmp(aName, bName);
|
|
290
|
+
if (nameCompare !== 0) return nameCompare;
|
|
291
|
+
return cmp(aPath, bPath);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function scanSkillsFromDir(
|
|
295
|
+
_ctx: LoadContext,
|
|
296
|
+
options: ScanSkillsFromDirOptions,
|
|
297
|
+
): Promise<LoadResult<Skill>> {
|
|
298
|
+
const items: Skill[] = [];
|
|
299
|
+
const warnings: string[] = [];
|
|
300
|
+
const { dir, level, providerId, requireDescription = false } = options;
|
|
301
|
+
|
|
302
|
+
let entries: fs.Dirent[];
|
|
303
|
+
try {
|
|
304
|
+
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
307
|
+
warnings.push(`Failed to read skills directory: ${dir} (${String(error)})`);
|
|
308
|
+
}
|
|
309
|
+
return { items, warnings };
|
|
310
|
+
}
|
|
311
|
+
const loadSkill = async (skillPath: string) => {
|
|
312
|
+
try {
|
|
313
|
+
const content = await readFile(skillPath);
|
|
314
|
+
if (!content) return;
|
|
315
|
+
const { frontmatter, body } = parseFrontmatter(content, { source: skillPath });
|
|
316
|
+
if (frontmatter.enabled === false) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (requireDescription && !frontmatter.description) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const skillDirName = path.basename(path.dirname(skillPath));
|
|
323
|
+
const rawName = frontmatter.name;
|
|
324
|
+
const name = typeof rawName === "string" ? rawName.trim() || skillDirName : skillDirName;
|
|
325
|
+
items.push({
|
|
326
|
+
name,
|
|
327
|
+
path: skillPath,
|
|
328
|
+
content: body,
|
|
329
|
+
frontmatter: frontmatter as SkillFrontmatter,
|
|
330
|
+
level,
|
|
331
|
+
_source: createSourceMeta(providerId, skillPath, level),
|
|
332
|
+
});
|
|
333
|
+
} catch {
|
|
334
|
+
warnings.push(`Failed to read skill file: ${skillPath}`);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const work = [];
|
|
339
|
+
for (const entry of entries) {
|
|
340
|
+
if (entry.name.startsWith(".")) continue;
|
|
341
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
342
|
+
const skillPath = path.join(dir, entry.name, "SKILL.md");
|
|
343
|
+
if (fs.existsSync(skillPath)) {
|
|
344
|
+
work.push(loadSkill(skillPath));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
await Promise.all(work);
|
|
348
|
+
|
|
349
|
+
// Deterministic ordering: async file reads complete nondeterministically, so sort after loading.
|
|
350
|
+
items.sort((a, b) => compareSkillOrder(a.name, a.path, b.name, b.path));
|
|
351
|
+
|
|
352
|
+
return { items, warnings };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Expand environment variables in a string.
|
|
357
|
+
* Supports ${VAR} and ${VAR:-default} syntax.
|
|
358
|
+
*/
|
|
359
|
+
function expandEnvVars(value: string, extraEnv?: Record<string, string>): string {
|
|
360
|
+
return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (_, varName: string, defaultValue?: string) => {
|
|
361
|
+
const envValue = extraEnv?.[varName] ?? Bun.env[varName];
|
|
362
|
+
if (envValue !== undefined) return envValue;
|
|
363
|
+
if (defaultValue !== undefined) return defaultValue;
|
|
364
|
+
return `\${${varName}}`;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Build plugin-specific environment variables for variable substitution.
|
|
370
|
+
* These are injected as extraEnv when loading plugin configs (hooks, MCP, LSP).
|
|
371
|
+
*/
|
|
372
|
+
export function buildPluginEnvVars(pluginRoot: string, pluginId: string, projectDir?: string): Record<string, string> {
|
|
373
|
+
const dataDir = path.join(os.homedir(), getConfigDirName(), "plugins", "data", pluginId.replace("@", "__"));
|
|
374
|
+
const vars: Record<string, string> = {
|
|
375
|
+
XCSH_PLUGIN_ROOT: pluginRoot,
|
|
376
|
+
XCSH_PLUGIN_DATA: dataDir,
|
|
377
|
+
};
|
|
378
|
+
if (projectDir) {
|
|
379
|
+
vars.XCSH_PROJECT_DIR = projectDir;
|
|
380
|
+
}
|
|
381
|
+
return vars;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Recursively expand environment variables in an object.
|
|
386
|
+
*/
|
|
387
|
+
export function expandEnvVarsDeep<T>(obj: T, extraEnv?: Record<string, string>): T {
|
|
388
|
+
if (typeof obj === "string") {
|
|
389
|
+
return expandEnvVars(obj, extraEnv) as T;
|
|
390
|
+
}
|
|
391
|
+
if (Array.isArray(obj)) {
|
|
392
|
+
return obj.map(item => expandEnvVarsDeep(item, extraEnv)) as T;
|
|
393
|
+
}
|
|
394
|
+
if (obj !== null && typeof obj === "object") {
|
|
395
|
+
const result: Record<string, unknown> = {};
|
|
396
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
397
|
+
result[key] = expandEnvVarsDeep(value, extraEnv);
|
|
398
|
+
}
|
|
399
|
+
return result as T;
|
|
400
|
+
}
|
|
401
|
+
return obj;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Load files from a directory matching extensions.
|
|
406
|
+
* Uses native glob for fast filesystem scanning with gitignore support.
|
|
407
|
+
*/
|
|
408
|
+
export async function loadFilesFromDir<T>(
|
|
409
|
+
_ctx: LoadContext,
|
|
410
|
+
dir: string,
|
|
411
|
+
provider: string,
|
|
412
|
+
level: "user" | "project",
|
|
413
|
+
options: {
|
|
414
|
+
/** File extensions to match (without dot) */
|
|
415
|
+
extensions?: string[];
|
|
416
|
+
/** Transform file to item (return null to skip) */
|
|
417
|
+
transform: (name: string, content: string, path: string, source: SourceMeta) => T | null;
|
|
418
|
+
/** Whether to recurse into subdirectories (default: false) */
|
|
419
|
+
recursive?: boolean;
|
|
420
|
+
},
|
|
421
|
+
): Promise<LoadResult<T>> {
|
|
422
|
+
const items: T[] = [];
|
|
423
|
+
const warnings: string[] = [];
|
|
424
|
+
// Build glob pattern based on extensions and recursion
|
|
425
|
+
const { extensions, recursive = false } = options;
|
|
426
|
+
|
|
427
|
+
let pattern: string;
|
|
428
|
+
if (extensions && extensions.length > 0) {
|
|
429
|
+
const extPattern = extensions.length === 1 ? extensions[0] : `{${extensions.join(",")}}`;
|
|
430
|
+
pattern = recursive ? `**/*.${extPattern}` : `*.${extPattern}`;
|
|
431
|
+
} else {
|
|
432
|
+
pattern = recursive ? "**/*" : "*";
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Use native glob for fast scanning with gitignore support
|
|
436
|
+
let matches: Array<{ path: string }>;
|
|
437
|
+
try {
|
|
438
|
+
const result = await glob({
|
|
439
|
+
pattern,
|
|
440
|
+
path: dir,
|
|
441
|
+
gitignore: true,
|
|
442
|
+
hidden: false,
|
|
443
|
+
fileType: FileType.File,
|
|
444
|
+
});
|
|
445
|
+
matches = result.matches;
|
|
446
|
+
} catch {
|
|
447
|
+
// Directory doesn't exist or isn't readable
|
|
448
|
+
return { items, warnings };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Read all matching files in parallel
|
|
452
|
+
const fileResults = await Promise.all(
|
|
453
|
+
matches.map(async match => {
|
|
454
|
+
const filePath = path.join(dir, match.path);
|
|
455
|
+
const content = await readFile(filePath);
|
|
456
|
+
return { filePath, content };
|
|
457
|
+
}),
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
for (const { filePath, content } of fileResults) {
|
|
461
|
+
if (content === null) {
|
|
462
|
+
warnings.push(`Failed to read file: ${filePath}`);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const name = path.basename(filePath);
|
|
467
|
+
const source = createSourceMeta(provider, filePath, level);
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
const item = options.transform(name, content, filePath, source);
|
|
471
|
+
if (item !== null) {
|
|
472
|
+
items.push(item);
|
|
473
|
+
}
|
|
474
|
+
} catch (err) {
|
|
475
|
+
warnings.push(`Failed to parse ${filePath}: ${err}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return { items, warnings };
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Calculate depth of target directory relative to current working directory.
|
|
483
|
+
* Depth is the number of directory levels from cwd to target.
|
|
484
|
+
* - Positive depth: target is above cwd (parent/ancestor)
|
|
485
|
+
* - Zero depth: target is cwd
|
|
486
|
+
* - This uses path splitting to count directory levels
|
|
487
|
+
*/
|
|
488
|
+
export function calculateDepth(cwd: string, targetDir: string, separator: string): number {
|
|
489
|
+
return cwd.split(separator).length - targetDir.split(separator).length;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
interface ExtensionModuleManifest {
|
|
493
|
+
extensions?: string[];
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function readExtensionModuleManifest(
|
|
497
|
+
_ctx: LoadContext,
|
|
498
|
+
packageJsonPath: string,
|
|
499
|
+
): Promise<ExtensionModuleManifest | null> {
|
|
500
|
+
const content = await readFile(packageJsonPath);
|
|
501
|
+
if (!content) return null;
|
|
502
|
+
|
|
503
|
+
const pkg = tryParseJson<{ xcsh?: ExtensionModuleManifest; pi?: ExtensionModuleManifest }>(content);
|
|
504
|
+
const manifest = pkg?.xcsh ?? pkg?.pi;
|
|
505
|
+
if (manifest && typeof manifest === "object") {
|
|
506
|
+
return manifest;
|
|
507
|
+
}
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Discover extension module entry points in a directory.
|
|
513
|
+
*
|
|
514
|
+
* Discovery rules:
|
|
515
|
+
* 1. Direct files: `extensions/*.ts` or `*.js` → load
|
|
516
|
+
* 2. Subdirectory with index: `extensions/<ext>/index.ts` or `index.js` → load
|
|
517
|
+
* 3. Subdirectory with package.json: `extensions/<ext>/package.json` with "xcsh"/"pi" field → load declared paths
|
|
518
|
+
*
|
|
519
|
+
* No recursion beyond one level. Complex packages must use package.json manifest.
|
|
520
|
+
* Uses native glob for fast filesystem scanning with gitignore support.
|
|
521
|
+
*/
|
|
522
|
+
export async function discoverExtensionModulePaths(_ctx: LoadContext, dir: string): Promise<string[]> {
|
|
523
|
+
const discovered = new Set<string>();
|
|
524
|
+
// Find all candidate files in parallel using glob
|
|
525
|
+
const [directFiles, indexFiles, packageJsonFiles] = await Promise.all([
|
|
526
|
+
// 1. Direct *.ts or *.js files
|
|
527
|
+
globIf(dir, "*.{ts,js}", FileType.File, false),
|
|
528
|
+
// 2. Subdirectory index files
|
|
529
|
+
globIf(dir, "*/index.{ts,js}", FileType.File, false),
|
|
530
|
+
// 3. Subdirectory package.json files
|
|
531
|
+
globIf(dir, "*/package.json", FileType.File, false),
|
|
532
|
+
]);
|
|
533
|
+
|
|
534
|
+
// Process direct files
|
|
535
|
+
for (const match of directFiles) {
|
|
536
|
+
if (match.path.includes("/")) continue;
|
|
537
|
+
discovered.add(path.join(dir, match.path));
|
|
538
|
+
}
|
|
539
|
+
// Track which subdirectories have package.json manifests with declared extensions
|
|
540
|
+
const subdirsWithDeclaredExtensions = new Set<string>();
|
|
541
|
+
for (const match of packageJsonFiles) {
|
|
542
|
+
const subdir = path.dirname(match.path); // e.g., "my-extension"
|
|
543
|
+
const packageJsonPath = path.join(dir, match.path);
|
|
544
|
+
const manifest = await readExtensionModuleManifest(_ctx, packageJsonPath);
|
|
545
|
+
const declaredExtensions =
|
|
546
|
+
manifest?.extensions?.filter((extPath): extPath is string => typeof extPath === "string") ?? [];
|
|
547
|
+
if (declaredExtensions.length === 0) continue;
|
|
548
|
+
subdirsWithDeclaredExtensions.add(subdir);
|
|
549
|
+
const subdirPath = path.join(dir, subdir);
|
|
550
|
+
for (const extPath of declaredExtensions) {
|
|
551
|
+
let resolvedExtPath = path.resolve(subdirPath, extPath);
|
|
552
|
+
const entries = await readDirEntries(resolvedExtPath);
|
|
553
|
+
if (entries.length !== 0) {
|
|
554
|
+
const pluginFilePath = entries.find(
|
|
555
|
+
e => e.isFile() && (e.name === "index.ts" || e.name === "index.js"),
|
|
556
|
+
)?.name;
|
|
557
|
+
resolvedExtPath = pluginFilePath ? path.join(resolvedExtPath, pluginFilePath) : resolvedExtPath;
|
|
558
|
+
}
|
|
559
|
+
const content = await readFile(resolvedExtPath);
|
|
560
|
+
if (content !== null) {
|
|
561
|
+
discovered.add(resolvedExtPath);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const preferredIndexBySubdir = new Map<string, string>();
|
|
566
|
+
for (const match of indexFiles) {
|
|
567
|
+
if (match.path.split("/").length !== 2) continue;
|
|
568
|
+
const subdir = path.dirname(match.path);
|
|
569
|
+
if (subdirsWithDeclaredExtensions.has(subdir)) continue;
|
|
570
|
+
const existing = preferredIndexBySubdir.get(subdir);
|
|
571
|
+
if (!existing || (existing.endsWith("index.js") && match.path.endsWith("index.ts"))) {
|
|
572
|
+
preferredIndexBySubdir.set(subdir, match.path);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
for (const preferredPath of preferredIndexBySubdir.values()) {
|
|
576
|
+
discovered.add(path.join(dir, preferredPath));
|
|
577
|
+
}
|
|
578
|
+
return [...discovered];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Derive a stable extension name from a path.
|
|
583
|
+
*/
|
|
584
|
+
export function getExtensionNameFromPath(extensionPath: string): string {
|
|
585
|
+
const base = extensionPath.replace(/\\/g, "/").split("/").pop() ?? extensionPath;
|
|
586
|
+
|
|
587
|
+
if (base === "index.ts" || base === "index.js") {
|
|
588
|
+
const parts = extensionPath.replace(/\\/g, "/").split("/");
|
|
589
|
+
const parent = parts[parts.length - 2];
|
|
590
|
+
return parent ?? base;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const dot = base.lastIndexOf(".");
|
|
594
|
+
if (dot > 0) {
|
|
595
|
+
return base.slice(0, dot);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return base;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// =============================================================================
|
|
602
|
+
// xcsh Plugin Cache Helpers
|
|
603
|
+
// =============================================================================
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Entry for an installed xcsh plugin.
|
|
607
|
+
*/
|
|
608
|
+
export interface XcshPluginEntry {
|
|
609
|
+
scope: "user" | "project";
|
|
610
|
+
installPath: string;
|
|
611
|
+
version: string;
|
|
612
|
+
installedAt: string;
|
|
613
|
+
lastUpdated: string;
|
|
614
|
+
gitCommitSha?: string;
|
|
615
|
+
enabled?: boolean;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* xcsh installed_plugins.json registry format.
|
|
620
|
+
*/
|
|
621
|
+
export interface XcshPluginsRegistry {
|
|
622
|
+
version: number;
|
|
623
|
+
plugins: Record<string, XcshPluginEntry[]>;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Resolved plugin root for loading.
|
|
628
|
+
*/
|
|
629
|
+
export interface XcshPluginRoot {
|
|
630
|
+
/** Plugin ID (e.g., "simpleclaude-core@simpleclaude") */
|
|
631
|
+
id: string;
|
|
632
|
+
/** Marketplace name */
|
|
633
|
+
marketplace: string;
|
|
634
|
+
/** Plugin name */
|
|
635
|
+
plugin: string;
|
|
636
|
+
/** Version string */
|
|
637
|
+
version: string;
|
|
638
|
+
/** Absolute path to plugin root */
|
|
639
|
+
path: string;
|
|
640
|
+
/** Whether this is a user, project, or local scope plugin */
|
|
641
|
+
scope: "user" | "project" | "local";
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** Map plugin scope to the loading level — "local" behaves as "project" for capability loading. */
|
|
645
|
+
export function scopeToLevel(scope: "user" | "project" | "local"): "user" | "project" {
|
|
646
|
+
return scope === "local" ? "project" : scope;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Parse xcsh installed_plugins.json content.
|
|
651
|
+
*/
|
|
652
|
+
export function parseXcshPluginsRegistry(content: string): XcshPluginsRegistry | null {
|
|
653
|
+
const data = tryParseJson<XcshPluginsRegistry>(content);
|
|
654
|
+
if (!data || typeof data !== "object") return null;
|
|
655
|
+
if (
|
|
656
|
+
typeof data.version !== "number" ||
|
|
657
|
+
!data.plugins ||
|
|
658
|
+
typeof data.plugins !== "object" ||
|
|
659
|
+
Array.isArray(data.plugins)
|
|
660
|
+
)
|
|
661
|
+
return null;
|
|
662
|
+
return data;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Resolve the active project registry path by walking up from `cwd`.
|
|
667
|
+
*
|
|
668
|
+
* Walk order:
|
|
669
|
+
* 1. Walk up from `cwd` looking for the nearest directory containing `.xcsh/`.
|
|
670
|
+
* The first match returns `<dir>/.xcsh/plugins/installed_plugins.json`.
|
|
671
|
+
* 2. If no `.xcsh/` is found, rescan from `cwd` upward looking for `.git`.
|
|
672
|
+
* The git root is used as an anchor: `<gitRoot>/.xcsh/plugins/installed_plugins.json`.
|
|
673
|
+
* 3. If neither is found, return `null` — no project context is active.
|
|
674
|
+
*
|
|
675
|
+
* This is the single source of truth for "active project root" used by install,
|
|
676
|
+
* uninstall, list, upgrade, discovery, and doctor. Deterministic for a given `cwd`.
|
|
677
|
+
*/
|
|
678
|
+
export async function resolveActiveProjectRegistryPath(cwd: string): Promise<string | null> {
|
|
679
|
+
// Pass 1: walk up looking for an existing .xcsh/ directory (nearest wins).
|
|
680
|
+
// Stop before os.homedir() — ~/.xcsh/ is the user-level config dir, not a project root.
|
|
681
|
+
const homeDir = os.homedir();
|
|
682
|
+
let dir = path.resolve(cwd);
|
|
683
|
+
while (dir !== homeDir) {
|
|
684
|
+
try {
|
|
685
|
+
const stat = await fs.promises.stat(path.join(dir, getConfigDirName()));
|
|
686
|
+
if (stat.isDirectory()) {
|
|
687
|
+
return path.join(dir, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
688
|
+
}
|
|
689
|
+
} catch {
|
|
690
|
+
// not found at this level — continue up
|
|
691
|
+
}
|
|
692
|
+
const parent = path.dirname(dir);
|
|
693
|
+
if (parent === dir) break; // filesystem root
|
|
694
|
+
dir = parent;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Pass 2: walk up looking for .git as a fallback anchor.
|
|
698
|
+
dir = path.resolve(cwd);
|
|
699
|
+
while (dir !== homeDir) {
|
|
700
|
+
try {
|
|
701
|
+
await fs.promises.stat(path.join(dir, ".git"));
|
|
702
|
+
return path.join(dir, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
703
|
+
} catch {
|
|
704
|
+
// not found at this level — continue up
|
|
705
|
+
}
|
|
706
|
+
const parent = path.dirname(dir);
|
|
707
|
+
if (parent === dir) break; // filesystem root
|
|
708
|
+
dir = parent;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return null; // not inside any project
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Like resolveActiveProjectRegistryPath, but falls back to `<cwd>/.xcsh/plugins/installed_plugins.json`
|
|
716
|
+
* when no project anchor (.xcsh/ or .git/) is found.
|
|
717
|
+
*
|
|
718
|
+
* Use this when the caller accepts an explicit --scope project so that installing into a freshly
|
|
719
|
+
* bootstrapped directory (no .xcsh/ or .git/ yet) works: writeInstalledPluginsRegistry auto-creates
|
|
720
|
+
* the directory tree on first write.
|
|
721
|
+
*
|
|
722
|
+
* Returns undefined when cwd is os.homedir() — that path is already the user registry and must
|
|
723
|
+
* never alias as the project registry.
|
|
724
|
+
*/
|
|
725
|
+
export async function resolveOrDefaultProjectRegistryPath(cwd: string): Promise<string | undefined> {
|
|
726
|
+
const resolved = await resolveActiveProjectRegistryPath(cwd);
|
|
727
|
+
if (resolved) return resolved;
|
|
728
|
+
// Home directory must not be treated as a project root: the fallback path would alias
|
|
729
|
+
// getInstalledPluginsRegistryPath(), causing MarketplaceManager to load the same file
|
|
730
|
+
// as both user and project registry and producing duplicates / disambiguation errors.
|
|
731
|
+
if (path.resolve(cwd) === os.homedir()) return undefined;
|
|
732
|
+
return path.join(cwd, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const pluginRootsCache = new Map<string, { roots: XcshPluginRoot[]; warnings: string[] }>();
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* List all installed marketplace plugin roots from the plugin cache.
|
|
739
|
+
* Reads ~/.xcsh/plugins/installed_plugins.json and optionally the nearest
|
|
740
|
+
* project-scoped registry resolved from `cwd`.
|
|
741
|
+
*
|
|
742
|
+
* Results are cached per `home:resolvedProjectPath` key to avoid repeated parsing.
|
|
743
|
+
*/
|
|
744
|
+
export async function listXcshPluginRoots(
|
|
745
|
+
home: string,
|
|
746
|
+
cwd?: string,
|
|
747
|
+
): Promise<{ roots: XcshPluginRoot[]; warnings: string[] }> {
|
|
748
|
+
const resolvedProjectPath = cwd ? await resolveActiveProjectRegistryPath(cwd) : null;
|
|
749
|
+
const cacheKey = `${home}:${resolvedProjectPath ?? ""}`;
|
|
750
|
+
const cached = pluginRootsCache.get(cacheKey);
|
|
751
|
+
if (cached) return cached;
|
|
752
|
+
|
|
753
|
+
const roots: XcshPluginRoot[] = [];
|
|
754
|
+
const warnings: string[] = [];
|
|
755
|
+
const projectRoots: XcshPluginRoot[] = [];
|
|
756
|
+
|
|
757
|
+
// ── Installed plugins registry ───────────────────────────────────────────
|
|
758
|
+
// Path derived from `home` (not os.homedir()) so test isolation works when home is overridden.
|
|
759
|
+
const registryPath = path.join(home, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
760
|
+
const content = await readFile(registryPath);
|
|
761
|
+
if (content) {
|
|
762
|
+
const registry = parseXcshPluginsRegistry(content);
|
|
763
|
+
if (registry) {
|
|
764
|
+
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
765
|
+
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
766
|
+
|
|
767
|
+
const atIndex = pluginId.lastIndexOf("@");
|
|
768
|
+
if (atIndex === -1) {
|
|
769
|
+
warnings.push(`Invalid plugin ID format (missing @marketplace): ${pluginId}`);
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const pluginName = pluginId.slice(0, atIndex);
|
|
773
|
+
const marketplace = pluginId.slice(atIndex + 1);
|
|
774
|
+
|
|
775
|
+
for (const entry of entries) {
|
|
776
|
+
if (!entry.installPath || typeof entry.installPath !== "string") {
|
|
777
|
+
warnings.push(`Plugin ${pluginId} entry has no installPath`);
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (entry.enabled === false) continue;
|
|
781
|
+
if (roots.some(r => r.id === pluginId && r.path === entry.installPath)) continue;
|
|
782
|
+
|
|
783
|
+
roots.push({
|
|
784
|
+
id: pluginId,
|
|
785
|
+
marketplace,
|
|
786
|
+
plugin: pluginName,
|
|
787
|
+
version: entry.version || "unknown",
|
|
788
|
+
path: entry.installPath,
|
|
789
|
+
scope: entry.scope || "user",
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
} else {
|
|
794
|
+
warnings.push(`Failed to parse plugin registry: ${registryPath}`);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// ── Project-scoped registry ──────────────────────────────────────────
|
|
799
|
+
// Loaded from the nearest .xcsh/plugins/installed_plugins.json relative to cwd.
|
|
800
|
+
// Project entries take precedence over user entries for the same plugin ID.
|
|
801
|
+
// Skip if the project registry is the same file as the user registry (home === cwd).
|
|
802
|
+
if (resolvedProjectPath && resolvedProjectPath !== registryPath) {
|
|
803
|
+
const projectContent = await readFile(resolvedProjectPath);
|
|
804
|
+
if (projectContent) {
|
|
805
|
+
const projectRegistry = parseXcshPluginsRegistry(projectContent);
|
|
806
|
+
if (projectRegistry) {
|
|
807
|
+
for (const [pluginId, entries] of Object.entries(projectRegistry.plugins)) {
|
|
808
|
+
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
809
|
+
const atIndex = pluginId.lastIndexOf("@");
|
|
810
|
+
if (atIndex === -1) {
|
|
811
|
+
warnings.push(`Invalid plugin ID format (missing @marketplace): ${pluginId}`);
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const pluginName = pluginId.slice(0, atIndex);
|
|
815
|
+
const marketplace = pluginId.slice(atIndex + 1);
|
|
816
|
+
for (const entry of entries) {
|
|
817
|
+
if (!entry.installPath || typeof entry.installPath !== "string") {
|
|
818
|
+
warnings.push(`Plugin ${pluginId} entry has no installPath`);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (entry.enabled === false) continue;
|
|
822
|
+
projectRoots.push({
|
|
823
|
+
id: pluginId,
|
|
824
|
+
marketplace,
|
|
825
|
+
plugin: pluginName,
|
|
826
|
+
version: entry.version || "unknown",
|
|
827
|
+
path: entry.installPath,
|
|
828
|
+
scope: "project",
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
} else {
|
|
833
|
+
warnings.push(`Failed to parse project plugin registry: ${resolvedProjectPath}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Project entries shadow user entries for the same plugin ID.
|
|
839
|
+
if (projectRoots.length > 0) {
|
|
840
|
+
const projectIds = new Set(projectRoots.map(r => r.id));
|
|
841
|
+
const deduped = roots.filter(r => !projectIds.has(r.id));
|
|
842
|
+
roots.length = 0;
|
|
843
|
+
roots.push(...projectRoots, ...deduped);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// Merge --plugin-dir roots (highest precedence) on every fresh load
|
|
847
|
+
if (injectedPluginDirRoots.length > 0) {
|
|
848
|
+
const injectedIds = new Set(injectedPluginDirRoots.map(r => r.id));
|
|
849
|
+
const filtered = roots.filter(r => !injectedIds.has(r.id));
|
|
850
|
+
roots.length = 0;
|
|
851
|
+
roots.push(...injectedPluginDirRoots, ...filtered);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const result = { roots, warnings };
|
|
855
|
+
pluginRootsCache.set(cacheKey, result);
|
|
856
|
+
return result;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Clear the plugin roots cache (useful for testing or when plugins change).
|
|
861
|
+
*/
|
|
862
|
+
export function clearXcshPluginRootsCache(): void {
|
|
863
|
+
pluginRootsCache.clear();
|
|
864
|
+
preloadedPluginRoots = [...injectedPluginDirRoots];
|
|
865
|
+
// Re-warm preloaded roots asynchronously so sync LSP config reads stay valid
|
|
866
|
+
if (lastPreloadHome) {
|
|
867
|
+
void preloadPluginRoots(lastPreloadHome, getProjectDir());
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// ── Preloaded plugin roots (for sync consumers like LSP config) ─────────────
|
|
872
|
+
// Populated at startup by preloadPluginRoots(). Read synchronously by
|
|
873
|
+
// getPreloadedPluginRoots(). Safe degradation: empty array if not warmed.
|
|
874
|
+
|
|
875
|
+
let preloadedPluginRoots: XcshPluginRoot[] = [];
|
|
876
|
+
let injectedPluginDirRoots: XcshPluginRoot[] = [];
|
|
877
|
+
let lastPreloadHome: string | undefined;
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Populate the module-level plugin roots cache for sync consumers.
|
|
881
|
+
* Call during session initialization, after dir resolution completes
|
|
882
|
+
* but before any LSP config is read.
|
|
883
|
+
*/
|
|
884
|
+
export async function preloadPluginRoots(home: string, cwd?: string): Promise<void> {
|
|
885
|
+
lastPreloadHome = home;
|
|
886
|
+
const { roots } = await listXcshPluginRoots(home, cwd);
|
|
887
|
+
preloadedPluginRoots = roots;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Get pre-loaded plugin roots synchronously.
|
|
892
|
+
* Returns empty array if preloadPluginRoots() hasn't been called.
|
|
893
|
+
*/
|
|
894
|
+
export function getPreloadedPluginRoots(): readonly XcshPluginRoot[] {
|
|
895
|
+
return preloadedPluginRoots;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// ── --plugin-dir injection ──────────────────────────────────────────────────
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Inject synthetic plugin roots from --plugin-dir paths.
|
|
902
|
+
* These are prepended to the cache with highest precedence (before OMP/xcsh entries).
|
|
903
|
+
* Must be called before any listXcshPluginRoots() access.
|
|
904
|
+
*/
|
|
905
|
+
export async function injectPluginDirRoots(home: string, dirs: string[], cwd?: string): Promise<void> {
|
|
906
|
+
const injected: XcshPluginRoot[] = [];
|
|
907
|
+
for (const dir of dirs) {
|
|
908
|
+
const resolved = path.resolve(dir);
|
|
909
|
+
// Read plugin name from manifest
|
|
910
|
+
let pluginName = path.basename(resolved);
|
|
911
|
+
try {
|
|
912
|
+
const manifestPath = path.join(resolved, ".xcsh-plugin", "plugin.json");
|
|
913
|
+
const content = await Bun.file(manifestPath).text();
|
|
914
|
+
const manifest = JSON.parse(content);
|
|
915
|
+
if (typeof manifest.name === "string" && manifest.name) {
|
|
916
|
+
pluginName = manifest.name;
|
|
917
|
+
}
|
|
918
|
+
} catch {
|
|
919
|
+
// No manifest or invalid — use directory name
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
injected.push(buildPluginDirRoot(resolved, pluginName));
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// Set injected roots BEFORE populating cache so listXcshPluginRoots merges them.
|
|
926
|
+
injectedPluginDirRoots = injected;
|
|
927
|
+
lastPreloadHome = home; // ensure cache-clear re-warm fires even when injectPluginDirRoots was the startup path
|
|
928
|
+
// Clear any stale cache entries (populated before injected roots were set).
|
|
929
|
+
pluginRootsCache.clear();
|
|
930
|
+
// Rebuild — cache miss triggers fresh load that includes both user+project registries
|
|
931
|
+
// and prepends injectedPluginDirRoots at highest precedence.
|
|
932
|
+
const { roots } = await listXcshPluginRoots(home, cwd);
|
|
933
|
+
preloadedPluginRoots = roots;
|
|
934
|
+
}
|