@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,1115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy matching utilities for the edit tool.
|
|
3
|
+
*
|
|
4
|
+
* Provides both character-level and line-level fuzzy matching with progressive
|
|
5
|
+
* fallback strategies for finding text in files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AgentToolResult } from "@f5xc-salesdemos/pi-agent-core";
|
|
9
|
+
import { isEnoent } from "@f5xc-salesdemos/pi-utils";
|
|
10
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
11
|
+
import type { WritethroughCallback, WritethroughDeferredHandle } from "../../lsp";
|
|
12
|
+
import type { ToolSession } from "../../tools";
|
|
13
|
+
import { invalidateFsScanAfterWrite } from "../../tools/fs-cache-invalidation";
|
|
14
|
+
import { outputMeta } from "../../tools/output-meta";
|
|
15
|
+
import { enforcePlanModeWrite, resolvePlanPath } from "../../tools/plan-mode-guard";
|
|
16
|
+
import { generateDiffString, replaceText } from "../diff";
|
|
17
|
+
import {
|
|
18
|
+
countLeadingWhitespace,
|
|
19
|
+
detectLineEnding,
|
|
20
|
+
normalizeForFuzzy,
|
|
21
|
+
normalizeToLF,
|
|
22
|
+
normalizeUnicode,
|
|
23
|
+
restoreLineEndings,
|
|
24
|
+
stripBom,
|
|
25
|
+
} from "../normalize";
|
|
26
|
+
import type { EditToolDetails, LspBatchRequest } from "../renderer";
|
|
27
|
+
|
|
28
|
+
export interface FuzzyMatch {
|
|
29
|
+
actualText: string;
|
|
30
|
+
startIndex: number;
|
|
31
|
+
startLine: number;
|
|
32
|
+
confidence: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MatchOutcome {
|
|
36
|
+
match?: FuzzyMatch;
|
|
37
|
+
closest?: FuzzyMatch;
|
|
38
|
+
occurrences?: number;
|
|
39
|
+
occurrenceLines?: number[];
|
|
40
|
+
occurrencePreviews?: string[];
|
|
41
|
+
fuzzyMatches?: number;
|
|
42
|
+
dominantFuzzy?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type SequenceMatchStrategy =
|
|
46
|
+
| "exact"
|
|
47
|
+
| "trim-trailing"
|
|
48
|
+
| "trim"
|
|
49
|
+
| "comment-prefix"
|
|
50
|
+
| "unicode"
|
|
51
|
+
| "prefix"
|
|
52
|
+
| "substring"
|
|
53
|
+
| "fuzzy"
|
|
54
|
+
| "fuzzy-dominant"
|
|
55
|
+
| "character";
|
|
56
|
+
|
|
57
|
+
export interface SequenceSearchResult {
|
|
58
|
+
index: number | undefined;
|
|
59
|
+
confidence: number;
|
|
60
|
+
matchCount?: number;
|
|
61
|
+
matchIndices?: number[];
|
|
62
|
+
strategy?: SequenceMatchStrategy;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ContextMatchStrategy = "exact" | "trim" | "unicode" | "prefix" | "substring" | "fuzzy";
|
|
66
|
+
|
|
67
|
+
export interface ContextLineResult {
|
|
68
|
+
index: number | undefined;
|
|
69
|
+
confidence: number;
|
|
70
|
+
matchCount?: number;
|
|
71
|
+
matchIndices?: number[];
|
|
72
|
+
strategy?: ContextMatchStrategy;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class EditMatchError extends Error {
|
|
76
|
+
constructor(
|
|
77
|
+
readonly path: string,
|
|
78
|
+
readonly searchText: string,
|
|
79
|
+
readonly closest: FuzzyMatch | undefined,
|
|
80
|
+
readonly options: { allowFuzzy: boolean; threshold: number; fuzzyMatches?: number },
|
|
81
|
+
) {
|
|
82
|
+
super(EditMatchError.formatMessage(path, searchText, closest, options));
|
|
83
|
+
this.name = "EditMatchError";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static formatMessage(
|
|
87
|
+
path: string,
|
|
88
|
+
searchText: string,
|
|
89
|
+
closest: FuzzyMatch | undefined,
|
|
90
|
+
options: { allowFuzzy: boolean; threshold: number; fuzzyMatches?: number },
|
|
91
|
+
): string {
|
|
92
|
+
if (!closest) {
|
|
93
|
+
return options.allowFuzzy
|
|
94
|
+
? `Could not find a close enough match in ${path}.`
|
|
95
|
+
: `Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const similarity = Math.round(closest.confidence * 100);
|
|
99
|
+
const searchLines = searchText.split("\n");
|
|
100
|
+
const actualLines = closest.actualText.split("\n");
|
|
101
|
+
const { oldLine, newLine } = findFirstDifferentLine(searchLines, actualLines);
|
|
102
|
+
const thresholdPercent = Math.round(options.threshold * 100);
|
|
103
|
+
|
|
104
|
+
const hint = options.allowFuzzy
|
|
105
|
+
? options.fuzzyMatches && options.fuzzyMatches > 1
|
|
106
|
+
? `Found ${options.fuzzyMatches} high-confidence matches. Provide more context to make it unique.`
|
|
107
|
+
: `Closest match was below the ${thresholdPercent}% similarity threshold.`
|
|
108
|
+
: "Fuzzy matching is disabled. Enable 'Edit fuzzy match' in settings to accept high-confidence matches.";
|
|
109
|
+
|
|
110
|
+
return [
|
|
111
|
+
options.allowFuzzy
|
|
112
|
+
? `Could not find a close enough match in ${path}.`
|
|
113
|
+
: `Could not find the exact text in ${path}.`,
|
|
114
|
+
``,
|
|
115
|
+
`Closest match (${similarity}% similar) at line ${closest.startLine}:`,
|
|
116
|
+
` - ${oldLine}`,
|
|
117
|
+
` + ${newLine}`,
|
|
118
|
+
hint,
|
|
119
|
+
].join("\n");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function findFirstDifferentLine(oldLines: string[], newLines: string[]): { oldLine: string; newLine: string } {
|
|
124
|
+
const max = Math.max(oldLines.length, newLines.length);
|
|
125
|
+
for (let i = 0; i < max; i++) {
|
|
126
|
+
const oldLine = oldLines[i] ?? "";
|
|
127
|
+
const newLine = newLines[i] ?? "";
|
|
128
|
+
if (oldLine !== newLine) {
|
|
129
|
+
return { oldLine, newLine };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { oldLine: oldLines[0] ?? "", newLine: newLines[0] ?? "" };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function formatOccurrenceError(path: string, matchOutcome: MatchOutcome): string {
|
|
136
|
+
const previews = matchOutcome.occurrencePreviews?.join("\n\n") ?? "";
|
|
137
|
+
const moreMsg =
|
|
138
|
+
matchOutcome.occurrences && matchOutcome.occurrences > MAX_RECORDED_MATCHES
|
|
139
|
+
? ` (showing first ${MAX_RECORDED_MATCHES} of ${matchOutcome.occurrences})`
|
|
140
|
+
: "";
|
|
141
|
+
return `Found ${matchOutcome.occurrences} occurrences in ${path}${moreMsg}:\n\n${previews}\n\nAdd more context lines to disambiguate.`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function readReplaceFileContent(absolutePath: string, path: string): Promise<string> {
|
|
145
|
+
try {
|
|
146
|
+
return await Bun.file(absolutePath).text();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (isEnoent(error)) {
|
|
149
|
+
throw new Error(`File not found: ${path}`);
|
|
150
|
+
}
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
156
|
+
// Constants
|
|
157
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
158
|
+
|
|
159
|
+
/** Default similarity threshold for fuzzy matching */
|
|
160
|
+
export const DEFAULT_FUZZY_THRESHOLD = 0.95;
|
|
161
|
+
|
|
162
|
+
/** Threshold for sequence-based fuzzy matching */
|
|
163
|
+
const SEQUENCE_FUZZY_THRESHOLD = 0.92;
|
|
164
|
+
|
|
165
|
+
/** Fallback threshold for line-based matching */
|
|
166
|
+
const FALLBACK_THRESHOLD = 0.8;
|
|
167
|
+
|
|
168
|
+
/** Threshold for context line matching */
|
|
169
|
+
const CONTEXT_FUZZY_THRESHOLD = 0.8;
|
|
170
|
+
|
|
171
|
+
/** Minimum length for partial/substring matching */
|
|
172
|
+
const PARTIAL_MATCH_MIN_LENGTH = 6;
|
|
173
|
+
|
|
174
|
+
/** Minimum ratio of pattern to line length for substring match */
|
|
175
|
+
const PARTIAL_MATCH_MIN_RATIO = 0.3;
|
|
176
|
+
|
|
177
|
+
/** Context lines to show before/after an ambiguous match preview */
|
|
178
|
+
const OCCURRENCE_PREVIEW_CONTEXT = 5;
|
|
179
|
+
|
|
180
|
+
/** Maximum line length for ambiguous match previews */
|
|
181
|
+
const OCCURRENCE_PREVIEW_MAX_LEN = 80;
|
|
182
|
+
|
|
183
|
+
/** Maximum number of match indices or previews to retain for diagnostics */
|
|
184
|
+
const MAX_RECORDED_MATCHES = 5;
|
|
185
|
+
|
|
186
|
+
/** Minimum confidence for a dominant fuzzy match to be auto-selected */
|
|
187
|
+
const DOMINANT_FUZZY_MIN_CONFIDENCE = 0.97;
|
|
188
|
+
|
|
189
|
+
/** Minimum score gap between the best and second-best fuzzy matches */
|
|
190
|
+
const DOMINANT_FUZZY_DELTA = 0.08;
|
|
191
|
+
|
|
192
|
+
interface IndexedMatches {
|
|
193
|
+
firstMatch: number | undefined;
|
|
194
|
+
matchCount: number;
|
|
195
|
+
matchIndices: number[];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface PreviewWindowOptions {
|
|
199
|
+
context: number;
|
|
200
|
+
maxLen: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function collectIndexedMatches(
|
|
204
|
+
start: number,
|
|
205
|
+
endInclusive: number,
|
|
206
|
+
predicate: (index: number) => boolean,
|
|
207
|
+
): IndexedMatches {
|
|
208
|
+
let firstMatch: number | undefined;
|
|
209
|
+
let matchCount = 0;
|
|
210
|
+
const matchIndices: number[] = [];
|
|
211
|
+
|
|
212
|
+
for (let index = start; index <= endInclusive; index++) {
|
|
213
|
+
if (!predicate(index)) continue;
|
|
214
|
+
if (firstMatch === undefined) {
|
|
215
|
+
firstMatch = index;
|
|
216
|
+
}
|
|
217
|
+
matchCount++;
|
|
218
|
+
if (matchIndices.length < MAX_RECORDED_MATCHES) {
|
|
219
|
+
matchIndices.push(index);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { firstMatch, matchCount, matchIndices };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function toSingleMatchResult<TStrategy extends SequenceMatchStrategy | ContextMatchStrategy>(
|
|
227
|
+
matches: IndexedMatches,
|
|
228
|
+
confidence: number,
|
|
229
|
+
strategy: TStrategy,
|
|
230
|
+
): { index: number; confidence: number; strategy: TStrategy } | undefined {
|
|
231
|
+
if (matches.firstMatch === undefined) {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
index: matches.firstMatch,
|
|
236
|
+
confidence,
|
|
237
|
+
strategy,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function toAmbiguousMatchResult<TStrategy extends SequenceMatchStrategy | ContextMatchStrategy>(
|
|
242
|
+
matches: IndexedMatches,
|
|
243
|
+
confidence: number,
|
|
244
|
+
strategy: TStrategy,
|
|
245
|
+
): { index: number; confidence: number; matchCount: number; matchIndices: number[]; strategy: TStrategy } | undefined {
|
|
246
|
+
if (matches.firstMatch === undefined) {
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
index: matches.firstMatch,
|
|
251
|
+
confidence,
|
|
252
|
+
matchCount: matches.matchCount,
|
|
253
|
+
matchIndices: matches.matchIndices,
|
|
254
|
+
strategy,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function formatPreviewWindow(lines: string[], centerIndex: number, options: PreviewWindowOptions): string {
|
|
259
|
+
const start = Math.max(0, centerIndex - options.context);
|
|
260
|
+
const end = Math.min(lines.length, centerIndex + options.context + 1);
|
|
261
|
+
return lines
|
|
262
|
+
.slice(start, end)
|
|
263
|
+
.map((line, index) => {
|
|
264
|
+
const num = start + index + 1;
|
|
265
|
+
const truncated = line.length > options.maxLen ? `${line.slice(0, options.maxLen - 1)}…` : line;
|
|
266
|
+
return ` ${num} | ${truncated}`;
|
|
267
|
+
})
|
|
268
|
+
.join("\n");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function findExactMatchOutcome(content: string, target: string): MatchOutcome | undefined {
|
|
272
|
+
const exactIndex = content.indexOf(target);
|
|
273
|
+
if (exactIndex === -1) {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const occurrences = content.split(target).length - 1;
|
|
278
|
+
if (occurrences > 1) {
|
|
279
|
+
const contentLines = content.split("\n");
|
|
280
|
+
const occurrenceLines: number[] = [];
|
|
281
|
+
const occurrencePreviews: string[] = [];
|
|
282
|
+
let searchStart = 0;
|
|
283
|
+
|
|
284
|
+
for (let i = 0; i < MAX_RECORDED_MATCHES; i++) {
|
|
285
|
+
const idx = content.indexOf(target, searchStart);
|
|
286
|
+
if (idx === -1) break;
|
|
287
|
+
const lineNumber = content.slice(0, idx).split("\n").length;
|
|
288
|
+
occurrenceLines.push(lineNumber);
|
|
289
|
+
occurrencePreviews.push(
|
|
290
|
+
formatPreviewWindow(contentLines, lineNumber - 1, {
|
|
291
|
+
context: OCCURRENCE_PREVIEW_CONTEXT,
|
|
292
|
+
maxLen: OCCURRENCE_PREVIEW_MAX_LEN,
|
|
293
|
+
}),
|
|
294
|
+
);
|
|
295
|
+
searchStart = idx + 1;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return { occurrences, occurrenceLines, occurrencePreviews };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const startLine = content.slice(0, exactIndex).split("\n").length;
|
|
302
|
+
return {
|
|
303
|
+
match: {
|
|
304
|
+
actualText: target,
|
|
305
|
+
startIndex: exactIndex,
|
|
306
|
+
startLine,
|
|
307
|
+
confidence: 1,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
313
|
+
// Core Algorithms
|
|
314
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
315
|
+
|
|
316
|
+
/** Compute Levenshtein distance between two strings */
|
|
317
|
+
export function levenshteinDistance(a: string, b: string): number {
|
|
318
|
+
if (a === b) return 0;
|
|
319
|
+
const aLen = a.length;
|
|
320
|
+
const bLen = b.length;
|
|
321
|
+
if (aLen === 0) return bLen;
|
|
322
|
+
if (bLen === 0) return aLen;
|
|
323
|
+
|
|
324
|
+
let prev = new Array<number>(bLen + 1);
|
|
325
|
+
let curr = new Array<number>(bLen + 1);
|
|
326
|
+
for (let j = 0; j <= bLen; j++) {
|
|
327
|
+
prev[j] = j;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
for (let i = 1; i <= aLen; i++) {
|
|
331
|
+
curr[0] = i;
|
|
332
|
+
const aCode = a.charCodeAt(i - 1);
|
|
333
|
+
for (let j = 1; j <= bLen; j++) {
|
|
334
|
+
const cost = aCode === b.charCodeAt(j - 1) ? 0 : 1;
|
|
335
|
+
const deletion = prev[j] + 1;
|
|
336
|
+
const insertion = curr[j - 1] + 1;
|
|
337
|
+
const substitution = prev[j - 1] + cost;
|
|
338
|
+
curr[j] = Math.min(deletion, insertion, substitution);
|
|
339
|
+
}
|
|
340
|
+
const tmp = prev;
|
|
341
|
+
prev = curr;
|
|
342
|
+
curr = tmp;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return prev[bLen];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Compute similarity score between two strings (0 to 1) */
|
|
349
|
+
export function similarity(a: string, b: string): number {
|
|
350
|
+
if (a.length === 0 && b.length === 0) return 1;
|
|
351
|
+
const maxLen = Math.max(a.length, b.length);
|
|
352
|
+
if (maxLen === 0) return 1;
|
|
353
|
+
const distance = levenshteinDistance(a, b);
|
|
354
|
+
return 1 - distance / maxLen;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
358
|
+
// Line-Based Utilities
|
|
359
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
360
|
+
|
|
361
|
+
/** Compute relative indent depths for lines */
|
|
362
|
+
function computeRelativeIndentDepths(lines: string[]): number[] {
|
|
363
|
+
const indents = lines.map(countLeadingWhitespace);
|
|
364
|
+
const nonEmptyIndents: number[] = [];
|
|
365
|
+
for (let i = 0; i < lines.length; i++) {
|
|
366
|
+
if (lines[i].trim().length > 0) {
|
|
367
|
+
nonEmptyIndents.push(indents[i]);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const minIndent = nonEmptyIndents.length > 0 ? Math.min(...nonEmptyIndents) : 0;
|
|
371
|
+
const indentSteps = nonEmptyIndents.map(indent => indent - minIndent).filter(step => step > 0);
|
|
372
|
+
const indentUnit = indentSteps.length > 0 ? Math.min(...indentSteps) : 1;
|
|
373
|
+
|
|
374
|
+
return lines.map((line, index) => {
|
|
375
|
+
if (line.trim().length === 0) return 0;
|
|
376
|
+
if (indentUnit <= 0) return 0;
|
|
377
|
+
const relativeIndent = indents[index] - minIndent;
|
|
378
|
+
return Math.round(relativeIndent / indentUnit);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Normalize lines for matching, optionally including indent depth */
|
|
383
|
+
function normalizeLines(lines: string[], includeDepth = true): string[] {
|
|
384
|
+
const indentDepths = includeDepth ? computeRelativeIndentDepths(lines) : null;
|
|
385
|
+
return lines.map((line, index) => {
|
|
386
|
+
const trimmed = line.trim();
|
|
387
|
+
const prefix = indentDepths ? `${indentDepths[index]}|` : "|";
|
|
388
|
+
if (trimmed.length === 0) return prefix;
|
|
389
|
+
return `${prefix}${normalizeForFuzzy(trimmed)}`;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** Compute character offsets for each line in content */
|
|
394
|
+
function computeLineOffsets(lines: string[]): number[] {
|
|
395
|
+
const offsets: number[] = [];
|
|
396
|
+
let offset = 0;
|
|
397
|
+
for (let i = 0; i < lines.length; i++) {
|
|
398
|
+
offsets.push(offset);
|
|
399
|
+
offset += lines[i].length;
|
|
400
|
+
if (i < lines.length - 1) offset += 1; // newline
|
|
401
|
+
}
|
|
402
|
+
return offsets;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
406
|
+
// Character-Level Fuzzy Match (for replace mode)
|
|
407
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
408
|
+
|
|
409
|
+
interface BestFuzzyMatchResult {
|
|
410
|
+
best?: FuzzyMatch;
|
|
411
|
+
aboveThresholdCount: number;
|
|
412
|
+
secondBestScore: number;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function findBestFuzzyMatchCore(
|
|
416
|
+
contentLines: string[],
|
|
417
|
+
targetLines: string[],
|
|
418
|
+
offsets: number[],
|
|
419
|
+
threshold: number,
|
|
420
|
+
includeDepth: boolean,
|
|
421
|
+
): BestFuzzyMatchResult {
|
|
422
|
+
const targetNormalized = normalizeLines(targetLines, includeDepth);
|
|
423
|
+
|
|
424
|
+
let best: FuzzyMatch | undefined;
|
|
425
|
+
let bestScore = -1;
|
|
426
|
+
let secondBestScore = -1;
|
|
427
|
+
let aboveThresholdCount = 0;
|
|
428
|
+
|
|
429
|
+
for (let start = 0; start <= contentLines.length - targetLines.length; start++) {
|
|
430
|
+
const windowLines = contentLines.slice(start, start + targetLines.length);
|
|
431
|
+
const windowNormalized = normalizeLines(windowLines, includeDepth);
|
|
432
|
+
let score = 0;
|
|
433
|
+
for (let i = 0; i < targetLines.length; i++) {
|
|
434
|
+
score += similarity(targetNormalized[i], windowNormalized[i]);
|
|
435
|
+
}
|
|
436
|
+
score = score / targetLines.length;
|
|
437
|
+
|
|
438
|
+
if (score >= threshold) {
|
|
439
|
+
aboveThresholdCount++;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (score > bestScore) {
|
|
443
|
+
secondBestScore = bestScore;
|
|
444
|
+
bestScore = score;
|
|
445
|
+
best = {
|
|
446
|
+
actualText: windowLines.join("\n"),
|
|
447
|
+
startIndex: offsets[start],
|
|
448
|
+
startLine: start + 1,
|
|
449
|
+
confidence: score,
|
|
450
|
+
};
|
|
451
|
+
} else if (score > secondBestScore) {
|
|
452
|
+
secondBestScore = score;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return { best, aboveThresholdCount, secondBestScore };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function findBestFuzzyMatch(content: string, target: string, threshold: number): BestFuzzyMatchResult {
|
|
460
|
+
const contentLines = content.split("\n");
|
|
461
|
+
const targetLines = target.split("\n");
|
|
462
|
+
|
|
463
|
+
if (targetLines.length === 0 || target.length === 0) {
|
|
464
|
+
return { aboveThresholdCount: 0, secondBestScore: 0 };
|
|
465
|
+
}
|
|
466
|
+
if (targetLines.length > contentLines.length) {
|
|
467
|
+
return { aboveThresholdCount: 0, secondBestScore: 0 };
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const offsets = computeLineOffsets(contentLines);
|
|
471
|
+
let result = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, true);
|
|
472
|
+
|
|
473
|
+
// Retry without indent depth if match is close but below threshold
|
|
474
|
+
if (result.best && result.best.confidence < threshold && result.best.confidence >= FALLBACK_THRESHOLD) {
|
|
475
|
+
const noDepthResult = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, false);
|
|
476
|
+
if (noDepthResult.best && noDepthResult.best.confidence > result.best.confidence) {
|
|
477
|
+
result = noDepthResult;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Find a match for target text within content.
|
|
486
|
+
* Used primarily for replace-mode edits.
|
|
487
|
+
*/
|
|
488
|
+
export function findMatch(
|
|
489
|
+
content: string,
|
|
490
|
+
target: string,
|
|
491
|
+
options: { allowFuzzy: boolean; threshold?: number },
|
|
492
|
+
): MatchOutcome {
|
|
493
|
+
if (target.length === 0) {
|
|
494
|
+
return {};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const exactMatch = findExactMatchOutcome(content, target);
|
|
498
|
+
if (exactMatch) {
|
|
499
|
+
return exactMatch;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Try fuzzy match
|
|
503
|
+
const threshold = options.threshold ?? DEFAULT_FUZZY_THRESHOLD;
|
|
504
|
+
const { best, aboveThresholdCount, secondBestScore } = findBestFuzzyMatch(content, target, threshold);
|
|
505
|
+
|
|
506
|
+
if (!best) {
|
|
507
|
+
return {};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (options.allowFuzzy && best.confidence >= threshold) {
|
|
511
|
+
if (aboveThresholdCount === 1) {
|
|
512
|
+
return { match: best, closest: best };
|
|
513
|
+
}
|
|
514
|
+
if (
|
|
515
|
+
aboveThresholdCount > 1 &&
|
|
516
|
+
best.confidence >= DOMINANT_FUZZY_MIN_CONFIDENCE &&
|
|
517
|
+
best.confidence - secondBestScore >= DOMINANT_FUZZY_DELTA
|
|
518
|
+
) {
|
|
519
|
+
return { match: best, closest: best, fuzzyMatches: aboveThresholdCount, dominantFuzzy: true };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return { closest: best, fuzzyMatches: aboveThresholdCount };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
527
|
+
// Line-Based Sequence Match (for patch mode)
|
|
528
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
529
|
+
|
|
530
|
+
/** Check if pattern matches lines starting at index using comparison function */
|
|
531
|
+
function matchesAt(lines: string[], pattern: string[], i: number, compare: (a: string, b: string) => boolean): boolean {
|
|
532
|
+
for (let j = 0; j < pattern.length; j++) {
|
|
533
|
+
if (!compare(lines[i + j], pattern[j])) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** Compute average similarity score for pattern at position */
|
|
541
|
+
function fuzzyScoreAt(lines: string[], pattern: string[], i: number): number {
|
|
542
|
+
let totalScore = 0;
|
|
543
|
+
for (let j = 0; j < pattern.length; j++) {
|
|
544
|
+
const lineNorm = normalizeForFuzzy(lines[i + j]);
|
|
545
|
+
const patternNorm = normalizeForFuzzy(pattern[j]);
|
|
546
|
+
totalScore += similarity(lineNorm, patternNorm);
|
|
547
|
+
}
|
|
548
|
+
return totalScore / pattern.length;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** Check if line starts with pattern (normalized) */
|
|
552
|
+
function lineStartsWithPattern(line: string, pattern: string): boolean {
|
|
553
|
+
const lineNorm = normalizeForFuzzy(line);
|
|
554
|
+
const patternNorm = normalizeForFuzzy(pattern);
|
|
555
|
+
if (patternNorm.length === 0) return lineNorm.length === 0;
|
|
556
|
+
return lineNorm.startsWith(patternNorm);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** Check if line contains pattern as significant substring */
|
|
560
|
+
function lineIncludesPattern(line: string, pattern: string): boolean {
|
|
561
|
+
const lineNorm = normalizeForFuzzy(line);
|
|
562
|
+
const patternNorm = normalizeForFuzzy(pattern);
|
|
563
|
+
if (patternNorm.length === 0) return lineNorm.length === 0;
|
|
564
|
+
if (patternNorm.length < PARTIAL_MATCH_MIN_LENGTH) return false;
|
|
565
|
+
if (!lineNorm.includes(patternNorm)) return false;
|
|
566
|
+
return patternNorm.length / Math.max(1, lineNorm.length) >= PARTIAL_MATCH_MIN_RATIO;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function stripCommentPrefix(line: string): string {
|
|
570
|
+
let trimmed = line.trimStart();
|
|
571
|
+
if (trimmed.startsWith("/*")) {
|
|
572
|
+
trimmed = trimmed.slice(2);
|
|
573
|
+
} else if (trimmed.startsWith("*/")) {
|
|
574
|
+
trimmed = trimmed.slice(2);
|
|
575
|
+
} else if (trimmed.startsWith("//")) {
|
|
576
|
+
trimmed = trimmed.slice(2);
|
|
577
|
+
} else if (trimmed.startsWith("*")) {
|
|
578
|
+
trimmed = trimmed.slice(1);
|
|
579
|
+
} else if (trimmed.startsWith("#")) {
|
|
580
|
+
trimmed = trimmed.slice(1);
|
|
581
|
+
} else if (trimmed.startsWith(";")) {
|
|
582
|
+
trimmed = trimmed.slice(1);
|
|
583
|
+
} else if (trimmed.startsWith("/") && trimmed[1] === " ") {
|
|
584
|
+
trimmed = trimmed.slice(1);
|
|
585
|
+
}
|
|
586
|
+
return trimmed.trimStart();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Find a sequence of pattern lines within content lines.
|
|
591
|
+
*
|
|
592
|
+
* Attempts matches with decreasing strictness:
|
|
593
|
+
* 1. Exact match
|
|
594
|
+
* 2. Trailing whitespace ignored
|
|
595
|
+
* 3. All whitespace trimmed
|
|
596
|
+
* 4. Unicode punctuation normalized
|
|
597
|
+
* 5. Prefix match (pattern is prefix of line)
|
|
598
|
+
* 6. Substring match (pattern is substring of line)
|
|
599
|
+
* 7. Fuzzy similarity match
|
|
600
|
+
*
|
|
601
|
+
* @param lines - The lines of the file content
|
|
602
|
+
* @param pattern - The lines to search for
|
|
603
|
+
* @param start - Starting index for the search
|
|
604
|
+
* @param eof - If true, prefer matching at end of file first
|
|
605
|
+
*/
|
|
606
|
+
export function seekSequence(
|
|
607
|
+
lines: string[],
|
|
608
|
+
pattern: string[],
|
|
609
|
+
start: number,
|
|
610
|
+
eof: boolean,
|
|
611
|
+
options?: { allowFuzzy?: boolean },
|
|
612
|
+
): SequenceSearchResult {
|
|
613
|
+
const allowFuzzy = options?.allowFuzzy ?? true;
|
|
614
|
+
// Empty pattern matches immediately
|
|
615
|
+
if (pattern.length === 0) {
|
|
616
|
+
return { index: start, confidence: 1.0, strategy: "exact" };
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Pattern longer than available content cannot match
|
|
620
|
+
if (pattern.length > lines.length) {
|
|
621
|
+
return { index: undefined, confidence: 0 };
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Determine search start position
|
|
625
|
+
const searchStart = eof && lines.length >= pattern.length ? lines.length - pattern.length : start;
|
|
626
|
+
const maxStart = lines.length - pattern.length;
|
|
627
|
+
|
|
628
|
+
const runExactPasses = (from: number, to: number): SequenceSearchResult | undefined => {
|
|
629
|
+
const comparisonPasses: Array<{
|
|
630
|
+
compare: (a: string, b: string) => boolean;
|
|
631
|
+
confidence: number;
|
|
632
|
+
strategy: SequenceMatchStrategy;
|
|
633
|
+
}> = [
|
|
634
|
+
{ compare: (a, b) => a === b, confidence: 1.0, strategy: "exact" },
|
|
635
|
+
{ compare: (a, b) => a.trimEnd() === b.trimEnd(), confidence: 0.99, strategy: "trim-trailing" },
|
|
636
|
+
{ compare: (a, b) => a.trim() === b.trim(), confidence: 0.98, strategy: "trim" },
|
|
637
|
+
{
|
|
638
|
+
compare: (a, b) => stripCommentPrefix(a) === stripCommentPrefix(b),
|
|
639
|
+
confidence: 0.975,
|
|
640
|
+
strategy: "comment-prefix",
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
compare: (a, b) => normalizeUnicode(a) === normalizeUnicode(b),
|
|
644
|
+
confidence: 0.97,
|
|
645
|
+
strategy: "unicode",
|
|
646
|
+
},
|
|
647
|
+
];
|
|
648
|
+
|
|
649
|
+
for (const pass of comparisonPasses) {
|
|
650
|
+
const matches = collectIndexedMatches(from, to, i => matchesAt(lines, pattern, i, pass.compare));
|
|
651
|
+
const result = toSingleMatchResult(matches, pass.confidence, pass.strategy);
|
|
652
|
+
if (result) {
|
|
653
|
+
return result;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (!allowFuzzy) {
|
|
658
|
+
return undefined;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const partialPasses: Array<{
|
|
662
|
+
compare: (line: string, patternLine: string) => boolean;
|
|
663
|
+
confidence: number;
|
|
664
|
+
strategy: SequenceMatchStrategy;
|
|
665
|
+
}> = [
|
|
666
|
+
{ compare: lineStartsWithPattern, confidence: 0.965, strategy: "prefix" },
|
|
667
|
+
{ compare: lineIncludesPattern, confidence: 0.94, strategy: "substring" },
|
|
668
|
+
];
|
|
669
|
+
|
|
670
|
+
for (const pass of partialPasses) {
|
|
671
|
+
const matches = collectIndexedMatches(from, to, i => matchesAt(lines, pattern, i, pass.compare));
|
|
672
|
+
const result = toAmbiguousMatchResult(matches, pass.confidence, pass.strategy);
|
|
673
|
+
if (result) {
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return undefined;
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
const primaryPassResult = runExactPasses(searchStart, maxStart);
|
|
682
|
+
if (primaryPassResult) {
|
|
683
|
+
return primaryPassResult;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (eof && searchStart > start) {
|
|
687
|
+
const fromStartResult = runExactPasses(start, maxStart);
|
|
688
|
+
if (fromStartResult) {
|
|
689
|
+
return fromStartResult;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (!allowFuzzy) {
|
|
694
|
+
return { index: undefined, confidence: 0 };
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Pass 7: Fuzzy matching - find best match above threshold
|
|
698
|
+
let bestScore = 0;
|
|
699
|
+
let secondBestScore = 0;
|
|
700
|
+
let bestIndex: number | undefined;
|
|
701
|
+
const fuzzyMatches: IndexedMatches = {
|
|
702
|
+
firstMatch: undefined,
|
|
703
|
+
matchCount: 0,
|
|
704
|
+
matchIndices: [],
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
const scoreFuzzyRange = (from: number, to: number): void => {
|
|
708
|
+
for (let i = from; i <= to; i++) {
|
|
709
|
+
const score = fuzzyScoreAt(lines, pattern, i);
|
|
710
|
+
if (score >= SEQUENCE_FUZZY_THRESHOLD) {
|
|
711
|
+
if (fuzzyMatches.firstMatch === undefined) {
|
|
712
|
+
fuzzyMatches.firstMatch = i;
|
|
713
|
+
}
|
|
714
|
+
fuzzyMatches.matchCount++;
|
|
715
|
+
if (fuzzyMatches.matchIndices.length < MAX_RECORDED_MATCHES) {
|
|
716
|
+
fuzzyMatches.matchIndices.push(i);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (score > bestScore) {
|
|
720
|
+
secondBestScore = bestScore;
|
|
721
|
+
bestScore = score;
|
|
722
|
+
bestIndex = i;
|
|
723
|
+
} else if (score > secondBestScore) {
|
|
724
|
+
secondBestScore = score;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
scoreFuzzyRange(searchStart, maxStart);
|
|
730
|
+
|
|
731
|
+
// Also search from start if eof mode started from end
|
|
732
|
+
if (eof && searchStart > start) {
|
|
733
|
+
scoreFuzzyRange(start, searchStart - 1);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (bestIndex !== undefined && bestScore >= SEQUENCE_FUZZY_THRESHOLD) {
|
|
737
|
+
if (
|
|
738
|
+
fuzzyMatches.matchCount > 1 &&
|
|
739
|
+
bestScore >= DOMINANT_FUZZY_MIN_CONFIDENCE &&
|
|
740
|
+
bestScore - secondBestScore >= DOMINANT_FUZZY_DELTA
|
|
741
|
+
) {
|
|
742
|
+
return {
|
|
743
|
+
index: bestIndex,
|
|
744
|
+
confidence: bestScore,
|
|
745
|
+
matchCount: 1,
|
|
746
|
+
matchIndices: fuzzyMatches.matchIndices,
|
|
747
|
+
strategy: "fuzzy-dominant",
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
index: bestIndex,
|
|
752
|
+
confidence: bestScore,
|
|
753
|
+
matchCount: fuzzyMatches.matchCount,
|
|
754
|
+
matchIndices: fuzzyMatches.matchIndices,
|
|
755
|
+
strategy: "fuzzy",
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Pass 8: Character-based fuzzy matching via findMatch
|
|
760
|
+
// This is the final fallback for when line-based matching fails
|
|
761
|
+
const CHARACTER_MATCH_THRESHOLD = 0.92;
|
|
762
|
+
const patternText = pattern.join("\n");
|
|
763
|
+
const contentText = lines.slice(start).join("\n");
|
|
764
|
+
const matchOutcome = findMatch(contentText, patternText, {
|
|
765
|
+
allowFuzzy: true,
|
|
766
|
+
threshold: CHARACTER_MATCH_THRESHOLD,
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
if (matchOutcome.match) {
|
|
770
|
+
// Convert character index back to line index
|
|
771
|
+
const matchedContent = contentText.substring(0, matchOutcome.match.startIndex);
|
|
772
|
+
const lineIndex = start + matchedContent.split("\n").length - 1;
|
|
773
|
+
const fallbackMatchCount = matchOutcome.occurrences ?? matchOutcome.fuzzyMatches ?? 1;
|
|
774
|
+
return {
|
|
775
|
+
index: lineIndex,
|
|
776
|
+
confidence: matchOutcome.match.confidence,
|
|
777
|
+
matchCount: fallbackMatchCount,
|
|
778
|
+
strategy: "character",
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const fallbackMatchCount = matchOutcome.occurrences ?? matchOutcome.fuzzyMatches;
|
|
783
|
+
return { index: undefined, confidence: bestScore, matchCount: fallbackMatchCount };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export function findClosestSequenceMatch(
|
|
787
|
+
lines: string[],
|
|
788
|
+
pattern: string[],
|
|
789
|
+
options?: { start?: number; eof?: boolean },
|
|
790
|
+
): { index: number | undefined; confidence: number; strategy: SequenceMatchStrategy } {
|
|
791
|
+
if (pattern.length === 0) {
|
|
792
|
+
return { index: options?.start ?? 0, confidence: 1, strategy: "exact" };
|
|
793
|
+
}
|
|
794
|
+
if (pattern.length > lines.length) {
|
|
795
|
+
return { index: undefined, confidence: 0, strategy: "fuzzy" };
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const start = options?.start ?? 0;
|
|
799
|
+
const eof = options?.eof ?? false;
|
|
800
|
+
const maxStart = lines.length - pattern.length;
|
|
801
|
+
const searchStart = eof && lines.length >= pattern.length ? maxStart : start;
|
|
802
|
+
|
|
803
|
+
let bestIndex: number | undefined;
|
|
804
|
+
let bestScore = 0;
|
|
805
|
+
|
|
806
|
+
for (let i = searchStart; i <= maxStart; i++) {
|
|
807
|
+
const score = fuzzyScoreAt(lines, pattern, i);
|
|
808
|
+
if (score > bestScore) {
|
|
809
|
+
bestScore = score;
|
|
810
|
+
bestIndex = i;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (eof && searchStart > start) {
|
|
815
|
+
for (let i = start; i < searchStart; i++) {
|
|
816
|
+
const score = fuzzyScoreAt(lines, pattern, i);
|
|
817
|
+
if (score > bestScore) {
|
|
818
|
+
bestScore = score;
|
|
819
|
+
bestIndex = i;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return { index: bestIndex, confidence: bestScore, strategy: "fuzzy" };
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Find a context line in the file using progressive matching strategies.
|
|
829
|
+
*
|
|
830
|
+
* @param lines - The lines of the file content
|
|
831
|
+
* @param context - The context line to search for
|
|
832
|
+
* @param startFrom - Starting index for the search
|
|
833
|
+
*/
|
|
834
|
+
export function findContextLine(
|
|
835
|
+
lines: string[],
|
|
836
|
+
context: string,
|
|
837
|
+
startFrom: number,
|
|
838
|
+
options?: { allowFuzzy?: boolean; skipFunctionFallback?: boolean },
|
|
839
|
+
): ContextLineResult {
|
|
840
|
+
const allowFuzzy = options?.allowFuzzy ?? true;
|
|
841
|
+
const trimmedContext = context.trim();
|
|
842
|
+
|
|
843
|
+
const endIndex = lines.length - 1;
|
|
844
|
+
const exactPasses: Array<{
|
|
845
|
+
confidence: number;
|
|
846
|
+
strategy: ContextMatchStrategy;
|
|
847
|
+
predicate: (index: number) => boolean;
|
|
848
|
+
}> = [
|
|
849
|
+
{ confidence: 1.0, strategy: "exact", predicate: i => lines[i] === context },
|
|
850
|
+
{ confidence: 0.99, strategy: "trim", predicate: i => lines[i].trim() === trimmedContext },
|
|
851
|
+
];
|
|
852
|
+
|
|
853
|
+
for (const pass of exactPasses) {
|
|
854
|
+
const matches = collectIndexedMatches(startFrom, endIndex, pass.predicate);
|
|
855
|
+
const result = toAmbiguousMatchResult(matches, pass.confidence, pass.strategy);
|
|
856
|
+
if (result) {
|
|
857
|
+
return result;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Pass 3: Unicode normalization match
|
|
862
|
+
const normalizedContext = normalizeUnicode(context);
|
|
863
|
+
const unicodeMatches = collectIndexedMatches(
|
|
864
|
+
startFrom,
|
|
865
|
+
endIndex,
|
|
866
|
+
i => normalizeUnicode(lines[i]) === normalizedContext,
|
|
867
|
+
);
|
|
868
|
+
const unicodeResult = toAmbiguousMatchResult(unicodeMatches, 0.98, "unicode");
|
|
869
|
+
if (unicodeResult) {
|
|
870
|
+
return unicodeResult;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (!allowFuzzy) {
|
|
874
|
+
return { index: undefined, confidence: 0 };
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// Pass 4: Prefix match (file line starts with context)
|
|
878
|
+
const contextNorm = normalizeForFuzzy(context);
|
|
879
|
+
if (contextNorm.length > 0) {
|
|
880
|
+
const prefixMatches = collectIndexedMatches(startFrom, endIndex, i =>
|
|
881
|
+
normalizeForFuzzy(lines[i]).startsWith(contextNorm),
|
|
882
|
+
);
|
|
883
|
+
const prefixResult = toAmbiguousMatchResult(prefixMatches, 0.96, "prefix");
|
|
884
|
+
if (prefixResult) {
|
|
885
|
+
return prefixResult;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Pass 5: Substring match (file line contains context)
|
|
890
|
+
// First pass: find all substring matches (ignoring ratio)
|
|
891
|
+
// If exactly one match exists, accept it (uniqueness is sufficient)
|
|
892
|
+
// If multiple matches, apply ratio filter to disambiguate
|
|
893
|
+
if (contextNorm.length >= PARTIAL_MATCH_MIN_LENGTH) {
|
|
894
|
+
const allSubstringMatches: Array<{ index: number; ratio: number }> = [];
|
|
895
|
+
for (let i = startFrom; i < lines.length; i++) {
|
|
896
|
+
const lineNorm = normalizeForFuzzy(lines[i]);
|
|
897
|
+
if (lineNorm.includes(contextNorm)) {
|
|
898
|
+
const ratio = contextNorm.length / Math.max(1, lineNorm.length);
|
|
899
|
+
allSubstringMatches.push({ index: i, ratio });
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
const matchIndices = allSubstringMatches.slice(0, 5).map(match => match.index);
|
|
903
|
+
|
|
904
|
+
// If exactly one substring match, accept it regardless of ratio
|
|
905
|
+
if (allSubstringMatches.length === 1) {
|
|
906
|
+
return {
|
|
907
|
+
index: allSubstringMatches[0].index,
|
|
908
|
+
confidence: 0.94,
|
|
909
|
+
matchCount: 1,
|
|
910
|
+
matchIndices,
|
|
911
|
+
strategy: "substring",
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Multiple matches: filter by ratio to disambiguate
|
|
916
|
+
let firstMatch: number | undefined;
|
|
917
|
+
let matchCount = 0;
|
|
918
|
+
for (const match of allSubstringMatches) {
|
|
919
|
+
if (match.ratio >= PARTIAL_MATCH_MIN_RATIO) {
|
|
920
|
+
if (firstMatch === undefined) firstMatch = match.index;
|
|
921
|
+
matchCount++;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (matchCount > 0) {
|
|
925
|
+
return { index: firstMatch, confidence: 0.94, matchCount, matchIndices, strategy: "substring" };
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// If we had substring matches but none passed ratio filter,
|
|
929
|
+
// return ambiguous result so caller knows matches exist
|
|
930
|
+
if (allSubstringMatches.length > 1) {
|
|
931
|
+
return {
|
|
932
|
+
index: allSubstringMatches[0].index,
|
|
933
|
+
confidence: 0.94,
|
|
934
|
+
matchCount: allSubstringMatches.length,
|
|
935
|
+
matchIndices,
|
|
936
|
+
strategy: "substring",
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Pass 6: Fuzzy match using similarity
|
|
942
|
+
let bestIndex: number | undefined;
|
|
943
|
+
let bestScore = 0;
|
|
944
|
+
const fuzzyMatches: IndexedMatches = {
|
|
945
|
+
firstMatch: undefined,
|
|
946
|
+
matchCount: 0,
|
|
947
|
+
matchIndices: [],
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
for (let i = startFrom; i < lines.length; i++) {
|
|
951
|
+
const lineNorm = normalizeForFuzzy(lines[i]);
|
|
952
|
+
const score = similarity(lineNorm, contextNorm);
|
|
953
|
+
if (score >= CONTEXT_FUZZY_THRESHOLD) {
|
|
954
|
+
if (fuzzyMatches.firstMatch === undefined) {
|
|
955
|
+
fuzzyMatches.firstMatch = i;
|
|
956
|
+
}
|
|
957
|
+
fuzzyMatches.matchCount++;
|
|
958
|
+
if (fuzzyMatches.matchIndices.length < MAX_RECORDED_MATCHES) {
|
|
959
|
+
fuzzyMatches.matchIndices.push(i);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (score > bestScore) {
|
|
963
|
+
bestScore = score;
|
|
964
|
+
bestIndex = i;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
if (bestIndex !== undefined && bestScore >= CONTEXT_FUZZY_THRESHOLD) {
|
|
969
|
+
return {
|
|
970
|
+
index: bestIndex,
|
|
971
|
+
confidence: bestScore,
|
|
972
|
+
matchCount: fuzzyMatches.matchCount,
|
|
973
|
+
matchIndices: fuzzyMatches.matchIndices,
|
|
974
|
+
strategy: "fuzzy",
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (!options?.skipFunctionFallback && trimmedContext.endsWith("()")) {
|
|
979
|
+
const withParen = trimmedContext.replace(/\(\)\s*$/u, "(");
|
|
980
|
+
const withoutParen = trimmedContext.replace(/\(\)\s*$/u, "");
|
|
981
|
+
const parenResult = findContextLine(lines, withParen, startFrom, { allowFuzzy, skipFunctionFallback: true });
|
|
982
|
+
if (parenResult.index !== undefined || (parenResult.matchCount ?? 0) > 0) {
|
|
983
|
+
return parenResult;
|
|
984
|
+
}
|
|
985
|
+
return findContextLine(lines, withoutParen, startFrom, { allowFuzzy, skipFunctionFallback: true });
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
return { index: undefined, confidence: bestScore };
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
export const replaceEditEntrySchema = Type.Object({
|
|
992
|
+
path: Type.String({ description: "File path (relative or absolute)" }),
|
|
993
|
+
old_text: Type.String({ description: "Text to find (fuzzy whitespace matching enabled)" }),
|
|
994
|
+
new_text: Type.String({ description: "Replacement text" }),
|
|
995
|
+
all: Type.Optional(Type.Boolean({ description: "Replace all occurrences (default: unique match required)" })),
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
export const replaceEditSchema = Type.Object({
|
|
999
|
+
edits: Type.Array(replaceEditEntrySchema, { description: "Replacements", minItems: 1 }),
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
export type ReplaceEditEntry = Static<typeof replaceEditEntrySchema>;
|
|
1003
|
+
export type ReplaceParams = Static<typeof replaceEditSchema>;
|
|
1004
|
+
|
|
1005
|
+
export interface ExecuteReplaceSingleOptions {
|
|
1006
|
+
session: ToolSession;
|
|
1007
|
+
params: ReplaceEditEntry;
|
|
1008
|
+
signal?: AbortSignal;
|
|
1009
|
+
batchRequest?: LspBatchRequest;
|
|
1010
|
+
allowFuzzy: boolean;
|
|
1011
|
+
fuzzyThreshold: number;
|
|
1012
|
+
writethrough: WritethroughCallback;
|
|
1013
|
+
beginDeferredDiagnosticsForPath: (path: string) => WritethroughDeferredHandle;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
export function isReplaceParams(params: unknown): params is ReplaceParams {
|
|
1017
|
+
if (typeof params !== "object" || params === null) return false;
|
|
1018
|
+
if (!("edits" in params) || !Array.isArray((params as any).edits)) return false;
|
|
1019
|
+
const first = (params as any).edits[0];
|
|
1020
|
+
return first && typeof first === "object" && "old_text" in first && "new_text" in first;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
export async function executeReplaceSingle(
|
|
1024
|
+
options: ExecuteReplaceSingleOptions,
|
|
1025
|
+
): Promise<AgentToolResult<EditToolDetails, typeof replaceEditEntrySchema>> {
|
|
1026
|
+
const {
|
|
1027
|
+
session,
|
|
1028
|
+
params,
|
|
1029
|
+
signal,
|
|
1030
|
+
batchRequest,
|
|
1031
|
+
allowFuzzy,
|
|
1032
|
+
fuzzyThreshold,
|
|
1033
|
+
writethrough,
|
|
1034
|
+
beginDeferredDiagnosticsForPath,
|
|
1035
|
+
} = options;
|
|
1036
|
+
const { path, old_text, new_text, all } = params;
|
|
1037
|
+
|
|
1038
|
+
enforcePlanModeWrite(session, path);
|
|
1039
|
+
|
|
1040
|
+
if (path.endsWith(".ipynb")) {
|
|
1041
|
+
throw new Error("Cannot edit Jupyter notebooks with the Edit tool. Use the NotebookEdit tool instead.");
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (old_text.length === 0) {
|
|
1045
|
+
throw new Error("old_text must not be empty.");
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const absolutePath = resolvePlanPath(session, path);
|
|
1049
|
+
const rawContent = await readReplaceFileContent(absolutePath, path);
|
|
1050
|
+
const { bom, text: content } = stripBom(rawContent);
|
|
1051
|
+
const originalEnding = detectLineEnding(content);
|
|
1052
|
+
const normalizedContent = normalizeToLF(content);
|
|
1053
|
+
const normalizedOldText = normalizeToLF(old_text);
|
|
1054
|
+
const normalizedNewText = normalizeToLF(new_text);
|
|
1055
|
+
|
|
1056
|
+
const result = replaceText(normalizedContent, normalizedOldText, normalizedNewText, {
|
|
1057
|
+
fuzzy: allowFuzzy,
|
|
1058
|
+
all: all ?? false,
|
|
1059
|
+
threshold: fuzzyThreshold,
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
if (result.count === 0) {
|
|
1063
|
+
const matchOutcome = findMatch(normalizedContent, normalizedOldText, {
|
|
1064
|
+
allowFuzzy,
|
|
1065
|
+
threshold: fuzzyThreshold,
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
if (matchOutcome.occurrences && matchOutcome.occurrences > 1) {
|
|
1069
|
+
throw new Error(formatOccurrenceError(path, matchOutcome));
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
throw new EditMatchError(path, normalizedOldText, matchOutcome.closest, {
|
|
1073
|
+
allowFuzzy,
|
|
1074
|
+
threshold: fuzzyThreshold,
|
|
1075
|
+
fuzzyMatches: matchOutcome.fuzzyMatches,
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if (normalizedContent === result.content) {
|
|
1080
|
+
throw new Error(
|
|
1081
|
+
`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const finalContent = bom + restoreLineEndings(result.content, originalEnding);
|
|
1086
|
+
const diagnostics = await writethrough(
|
|
1087
|
+
absolutePath,
|
|
1088
|
+
finalContent,
|
|
1089
|
+
signal,
|
|
1090
|
+
Bun.file(absolutePath),
|
|
1091
|
+
batchRequest,
|
|
1092
|
+
dst => (dst === absolutePath ? beginDeferredDiagnosticsForPath(absolutePath) : undefined),
|
|
1093
|
+
);
|
|
1094
|
+
invalidateFsScanAfterWrite(absolutePath);
|
|
1095
|
+
|
|
1096
|
+
const diffResult = generateDiffString(normalizedContent, result.content);
|
|
1097
|
+
const resultText =
|
|
1098
|
+
result.count > 1
|
|
1099
|
+
? `Successfully replaced ${result.count} occurrences in ${path}.`
|
|
1100
|
+
: `Successfully replaced text in ${path}.`;
|
|
1101
|
+
|
|
1102
|
+
const meta = outputMeta()
|
|
1103
|
+
.diagnostics(diagnostics?.summary ?? "", diagnostics?.messages ?? [])
|
|
1104
|
+
.get();
|
|
1105
|
+
|
|
1106
|
+
return {
|
|
1107
|
+
content: [{ type: "text", text: resultText }],
|
|
1108
|
+
details: {
|
|
1109
|
+
diff: diffResult.diff,
|
|
1110
|
+
firstChangedLine: diffResult.firstChangedLine,
|
|
1111
|
+
diagnostics,
|
|
1112
|
+
meta,
|
|
1113
|
+
},
|
|
1114
|
+
};
|
|
1115
|
+
}
|