@nghyane/arcane 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/README.md +12 -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 +550 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/todo.ts +296 -0
- package/examples/extensions/tools.ts +144 -0
- package/examples/extensions/with-deps/index.ts +35 -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 +116 -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 +150 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +119 -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 +36 -0
- package/examples/sdk/08-slash-commands.ts +41 -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 +150 -0
- package/package.json +464 -0
- package/scripts/format-prompts.ts +184 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +32 -0
- package/src/bun-imports.d.ts +22 -0
- package/src/capability/context-file.ts +39 -0
- package/src/capability/extension-module.ts +33 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +89 -0
- package/src/capability/hook.ts +39 -0
- package/src/capability/index.ts +432 -0
- package/src/capability/instruction.ts +36 -0
- package/src/capability/mcp.ts +60 -0
- package/src/capability/prompt.ts +34 -0
- package/src/capability/rule.ts +223 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +48 -0
- package/src/capability/slash-command.ts +39 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +37 -0
- package/src/capability/types.ts +156 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/config-cli.ts +357 -0
- package/src/cli/file-processor.ts +124 -0
- package/src/cli/grep-cli.ts +152 -0
- package/src/cli/jupyter-cli.ts +106 -0
- package/src/cli/list-models.ts +103 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +42 -0
- package/src/cli/setup-cli.ts +376 -0
- package/src/cli/shell-cli.ts +174 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/stats-cli.ts +197 -0
- package/src/cli/update-cli.ts +286 -0
- package/src/cli/web-search-cli.ts +143 -0
- package/src/cli.ts +65 -0
- package/src/commands/commit.ts +36 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/grep.ts +41 -0
- package/src/commands/jupyter.ts +32 -0
- package/src/commands/launch.ts +139 -0
- package/src/commands/plugin.ts +70 -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 +42 -0
- package/src/commit/agentic/agent.ts +311 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +359 -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 +69 -0
- package/src/commit/agentic/tools/analyze-file.ts +118 -0
- package/src/commit/agentic/tools/git-file-diff.ts +194 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +84 -0
- package/src/commit/agentic/tools/index.ts +56 -0
- package/src/commit/agentic/tools/propose-changelog.ts +128 -0
- package/src/commit/agentic/tools/propose-commit.ts +154 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/split-commit.ts +280 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +200 -0
- package/src/commit/analysis/conventional.ts +165 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +112 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +37 -0
- package/src/commit/changelog/generate.ts +110 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +93 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/git/errors.ts +9 -0
- package/src/commit/git/index.ts +211 -0
- package/src/commit/git/operations.ts +54 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +64 -0
- package/src/commit/map-reduce/map-phase.ts +178 -0
- package/src/commit/map-reduce/reduce-phase.ts +145 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +69 -0
- package/src/commit/pipeline.ts +243 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/types.ts +109 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/config/file-lock.ts +121 -0
- package/src/config/keybindings.ts +280 -0
- package/src/config/model-registry.ts +1140 -0
- package/src/config/model-resolver.ts +812 -0
- package/src/config/prompt-templates.ts +526 -0
- package/src/config/resolve-config-value.ts +92 -0
- package/src/config/settings-schema.ts +1236 -0
- package/src/config/settings.ts +706 -0
- package/src/config.ts +414 -0
- package/src/cursor.ts +239 -0
- package/src/debug/index.ts +431 -0
- package/src/debug/log-formatting.ts +60 -0
- package/src/debug/log-viewer.ts +903 -0
- package/src/debug/profiler.ts +158 -0
- package/src/debug/report-bundle.ts +366 -0
- package/src/debug/system-info.ts +112 -0
- package/src/discovery/agents-md.ts +68 -0
- package/src/discovery/agents.ts +199 -0
- package/src/discovery/builtin.ts +815 -0
- package/src/discovery/claude-plugins.ts +205 -0
- package/src/discovery/claude.ts +506 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +532 -0
- package/src/discovery/cursor.ts +218 -0
- package/src/discovery/gemini.ts +395 -0
- package/src/discovery/github.ts +117 -0
- package/src/discovery/helpers.ts +698 -0
- package/src/discovery/index.ts +89 -0
- package/src/discovery/mcp-json.ts +156 -0
- package/src/discovery/opencode.ts +394 -0
- package/src/discovery/ssh.ts +160 -0
- package/src/discovery/vscode.ts +103 -0
- package/src/discovery/windsurf.ts +145 -0
- package/src/exa/company.ts +57 -0
- package/src/exa/index.ts +62 -0
- package/src/exa/linkedin.ts +57 -0
- package/src/exa/mcp-client.ts +289 -0
- package/src/exa/render.ts +244 -0
- package/src/exa/researcher.ts +89 -0
- package/src/exa/search.ts +330 -0
- package/src/exa/types.ts +166 -0
- package/src/exa/websets.ts +247 -0
- package/src/exec/bash-executor.ts +184 -0
- package/src/exec/exec.ts +53 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +162 -0
- package/src/export/html/template.css +889 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +45 -0
- package/src/export/html/template.js +1329 -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/review/index.ts +433 -0
- package/src/extensibility/custom-commands/index.ts +15 -0
- package/src/extensibility/custom-commands/loader.ts +231 -0
- package/src/extensibility/custom-commands/types.ts +111 -0
- package/src/extensibility/custom-tools/index.ts +22 -0
- package/src/extensibility/custom-tools/loader.ts +235 -0
- package/src/extensibility/custom-tools/types.ts +226 -0
- package/src/extensibility/custom-tools/wrapper.ts +45 -0
- package/src/extensibility/extensions/index.ts +136 -0
- package/src/extensibility/extensions/loader.ts +520 -0
- package/src/extensibility/extensions/runner.ts +774 -0
- package/src/extensibility/extensions/types.ts +1293 -0
- package/src/extensibility/extensions/wrapper.ts +188 -0
- package/src/extensibility/hooks/index.ts +16 -0
- package/src/extensibility/hooks/loader.ts +273 -0
- package/src/extensibility/hooks/runner.ts +441 -0
- package/src/extensibility/hooks/tool-wrapper.ts +106 -0
- package/src/extensibility/hooks/types.ts +817 -0
- package/src/extensibility/plugins/doctor.ts +65 -0
- package/src/extensibility/plugins/git-url.ts +281 -0
- package/src/extensibility/plugins/index.ts +33 -0
- package/src/extensibility/plugins/installer.ts +192 -0
- package/src/extensibility/plugins/loader.ts +338 -0
- package/src/extensibility/plugins/manager.ts +716 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +190 -0
- package/src/extensibility/skills.ts +385 -0
- package/src/extensibility/slash-commands.ts +287 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/index.ts +275 -0
- package/src/internal-urls/agent-protocol.ts +136 -0
- package/src/internal-urls/artifact-protocol.ts +97 -0
- package/src/internal-urls/docs-index.generated.ts +54 -0
- package/src/internal-urls/docs-protocol.ts +84 -0
- package/src/internal-urls/index.ts +31 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/memory-protocol.ts +133 -0
- package/src/internal-urls/router.ts +70 -0
- package/src/internal-urls/rule-protocol.ts +55 -0
- package/src/internal-urls/skill-protocol.ts +111 -0
- package/src/internal-urls/types.ts +52 -0
- package/src/ipy/executor.ts +556 -0
- package/src/ipy/gateway-coordinator.ts +426 -0
- package/src/ipy/kernel.ts +892 -0
- package/src/ipy/modules.ts +109 -0
- package/src/ipy/prelude.py +831 -0
- package/src/ipy/prelude.ts +3 -0
- package/src/ipy/runtime.ts +222 -0
- package/src/lsp/client.ts +867 -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 +397 -0
- package/src/lsp/defaults.json +464 -0
- package/src/lsp/edits.ts +109 -0
- package/src/lsp/index.ts +1268 -0
- package/src/lsp/lspmux.ts +250 -0
- package/src/lsp/render.ts +689 -0
- package/src/lsp/types.ts +414 -0
- package/src/lsp/utils.ts +549 -0
- package/src/main.ts +773 -0
- package/src/mcp/client.ts +239 -0
- package/src/mcp/config-writer.ts +215 -0
- package/src/mcp/config.ts +363 -0
- package/src/mcp/index.ts +55 -0
- package/src/mcp/json-rpc.ts +84 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +490 -0
- package/src/mcp/oauth-discovery.ts +274 -0
- package/src/mcp/oauth-flow.ts +229 -0
- package/src/mcp/render.ts +123 -0
- package/src/mcp/tool-bridge.ts +372 -0
- package/src/mcp/tool-cache.ts +121 -0
- package/src/mcp/transports/http.ts +332 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +281 -0
- package/src/mcp/types.ts +248 -0
- package/src/memories/index.ts +1099 -0
- package/src/memories/storage.ts +563 -0
- package/src/modes/components/agent-dashboard.ts +1130 -0
- package/src/modes/components/assistant-message.ts +144 -0
- package/src/modes/components/bash-execution.ts +218 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/branch-summary-message.ts +45 -0
- package/src/modes/components/codemode-group.ts +369 -0
- package/src/modes/components/compaction-summary-message.ts +51 -0
- package/src/modes/components/countdown-timer.ts +46 -0
- package/src/modes/components/custom-editor.ts +181 -0
- package/src/modes/components/custom-message.ts +91 -0
- package/src/modes/components/diff.ts +186 -0
- package/src/modes/components/dynamic-border.ts +25 -0
- package/src/modes/components/extensions/extension-dashboard.ts +325 -0
- package/src/modes/components/extensions/extension-list.ts +484 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +321 -0
- package/src/modes/components/extensions/state-manager.ts +586 -0
- package/src/modes/components/extensions/types.ts +191 -0
- package/src/modes/components/footer.ts +315 -0
- package/src/modes/components/history-search.ts +157 -0
- package/src/modes/components/hook-editor.ts +101 -0
- package/src/modes/components/hook-input.ts +72 -0
- package/src/modes/components/hook-message.ts +100 -0
- package/src/modes/components/hook-selector.ts +155 -0
- package/src/modes/components/index.ts +41 -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 +1295 -0
- package/src/modes/components/model-selector.ts +625 -0
- package/src/modes/components/oauth-selector.ts +210 -0
- package/src/modes/components/plugin-settings.ts +477 -0
- package/src/modes/components/python-execution.ts +196 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +119 -0
- package/src/modes/components/session-selector.ts +242 -0
- package/src/modes/components/settings-defs.ts +340 -0
- package/src/modes/components/settings-selector.ts +529 -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/index.ts +4 -0
- package/src/modes/components/status-line/presets.ts +94 -0
- package/src/modes/components/status-line/segments.ts +352 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/types.ts +75 -0
- package/src/modes/components/status-line-segment-editor.ts +354 -0
- package/src/modes/components/status-line.ts +421 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +64 -0
- package/src/modes/components/todo-display.ts +115 -0
- package/src/modes/components/todo-reminder.ts +40 -0
- package/src/modes/components/tool-execution.ts +703 -0
- package/src/modes/components/tree-selector.ts +904 -0
- package/src/modes/components/ttsr-notification.ts +80 -0
- package/src/modes/components/user-message-selector.ts +146 -0
- package/src/modes/components/user-message.ts +22 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome.ts +247 -0
- package/src/modes/controllers/command-controller.ts +1120 -0
- package/src/modes/controllers/event-controller.ts +479 -0
- package/src/modes/controllers/extension-ui-controller.ts +778 -0
- package/src/modes/controllers/input-controller.ts +671 -0
- package/src/modes/controllers/mcp-command-controller.ts +1315 -0
- package/src/modes/controllers/selector-controller.ts +712 -0
- package/src/modes/controllers/ssh-command-controller.ts +452 -0
- package/src/modes/index.ts +15 -0
- package/src/modes/interactive-mode.ts +1027 -0
- package/src/modes/print-mode.ts +191 -0
- package/src/modes/rpc/rpc-client.ts +583 -0
- package/src/modes/rpc/rpc-mode.ts +700 -0
- package/src/modes/rpc/rpc-types.ts +236 -0
- package/src/modes/theme/dark.json +95 -0
- package/src/modes/theme/defaults/alabaster.json +93 -0
- package/src/modes/theme/defaults/amethyst.json +96 -0
- package/src/modes/theme/defaults/anthracite.json +93 -0
- package/src/modes/theme/defaults/basalt.json +91 -0
- package/src/modes/theme/defaults/birch.json +95 -0
- package/src/modes/theme/defaults/dark-abyss.json +91 -0
- package/src/modes/theme/defaults/dark-arctic.json +104 -0
- package/src/modes/theme/defaults/dark-aurora.json +95 -0
- package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
- package/src/modes/theme/defaults/dark-cavern.json +91 -0
- package/src/modes/theme/defaults/dark-copper.json +95 -0
- package/src/modes/theme/defaults/dark-cosmos.json +90 -0
- package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
- package/src/modes/theme/defaults/dark-dracula.json +98 -0
- package/src/modes/theme/defaults/dark-eclipse.json +91 -0
- package/src/modes/theme/defaults/dark-ember.json +95 -0
- package/src/modes/theme/defaults/dark-equinox.json +90 -0
- package/src/modes/theme/defaults/dark-forest.json +96 -0
- package/src/modes/theme/defaults/dark-github.json +105 -0
- package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
- package/src/modes/theme/defaults/dark-lavender.json +95 -0
- package/src/modes/theme/defaults/dark-lunar.json +89 -0
- package/src/modes/theme/defaults/dark-midnight.json +95 -0
- package/src/modes/theme/defaults/dark-monochrome.json +94 -0
- package/src/modes/theme/defaults/dark-monokai.json +98 -0
- package/src/modes/theme/defaults/dark-nebula.json +90 -0
- package/src/modes/theme/defaults/dark-nord.json +97 -0
- package/src/modes/theme/defaults/dark-ocean.json +101 -0
- package/src/modes/theme/defaults/dark-one.json +100 -0
- package/src/modes/theme/defaults/dark-rainforest.json +91 -0
- package/src/modes/theme/defaults/dark-reef.json +91 -0
- package/src/modes/theme/defaults/dark-retro.json +92 -0
- package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
- package/src/modes/theme/defaults/dark-sakura.json +95 -0
- package/src/modes/theme/defaults/dark-slate.json +95 -0
- package/src/modes/theme/defaults/dark-solarized.json +97 -0
- package/src/modes/theme/defaults/dark-solstice.json +90 -0
- package/src/modes/theme/defaults/dark-starfall.json +91 -0
- package/src/modes/theme/defaults/dark-sunset.json +99 -0
- package/src/modes/theme/defaults/dark-swamp.json +90 -0
- package/src/modes/theme/defaults/dark-synthwave.json +103 -0
- package/src/modes/theme/defaults/dark-taiga.json +91 -0
- package/src/modes/theme/defaults/dark-terminal.json +95 -0
- package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
- package/src/modes/theme/defaults/dark-tundra.json +91 -0
- package/src/modes/theme/defaults/dark-twilight.json +91 -0
- package/src/modes/theme/defaults/dark-volcanic.json +91 -0
- package/src/modes/theme/defaults/graphite.json +92 -0
- package/src/modes/theme/defaults/index.ts +195 -0
- package/src/modes/theme/defaults/light-arctic.json +107 -0
- package/src/modes/theme/defaults/light-aurora-day.json +91 -0
- package/src/modes/theme/defaults/light-canyon.json +91 -0
- package/src/modes/theme/defaults/light-catppuccin.json +106 -0
- package/src/modes/theme/defaults/light-cirrus.json +90 -0
- package/src/modes/theme/defaults/light-coral.json +95 -0
- package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
- package/src/modes/theme/defaults/light-dawn.json +90 -0
- package/src/modes/theme/defaults/light-dunes.json +91 -0
- package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
- package/src/modes/theme/defaults/light-forest.json +100 -0
- package/src/modes/theme/defaults/light-frost.json +95 -0
- package/src/modes/theme/defaults/light-github.json +115 -0
- package/src/modes/theme/defaults/light-glacier.json +91 -0
- package/src/modes/theme/defaults/light-gruvbox.json +108 -0
- package/src/modes/theme/defaults/light-haze.json +90 -0
- package/src/modes/theme/defaults/light-honeycomb.json +95 -0
- package/src/modes/theme/defaults/light-lagoon.json +91 -0
- package/src/modes/theme/defaults/light-lavender.json +95 -0
- package/src/modes/theme/defaults/light-meadow.json +91 -0
- package/src/modes/theme/defaults/light-mint.json +95 -0
- package/src/modes/theme/defaults/light-monochrome.json +101 -0
- package/src/modes/theme/defaults/light-ocean.json +99 -0
- package/src/modes/theme/defaults/light-one.json +99 -0
- package/src/modes/theme/defaults/light-opal.json +91 -0
- package/src/modes/theme/defaults/light-orchard.json +91 -0
- package/src/modes/theme/defaults/light-paper.json +95 -0
- package/src/modes/theme/defaults/light-prism.json +90 -0
- package/src/modes/theme/defaults/light-retro.json +98 -0
- package/src/modes/theme/defaults/light-sand.json +95 -0
- package/src/modes/theme/defaults/light-savanna.json +91 -0
- package/src/modes/theme/defaults/light-solarized.json +102 -0
- package/src/modes/theme/defaults/light-soleil.json +90 -0
- package/src/modes/theme/defaults/light-sunset.json +99 -0
- package/src/modes/theme/defaults/light-synthwave.json +98 -0
- package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
- package/src/modes/theme/defaults/light-wetland.json +91 -0
- package/src/modes/theme/defaults/light-zenith.json +89 -0
- package/src/modes/theme/defaults/limestone.json +94 -0
- package/src/modes/theme/defaults/mahogany.json +97 -0
- package/src/modes/theme/defaults/marble.json +93 -0
- package/src/modes/theme/defaults/obsidian.json +91 -0
- package/src/modes/theme/defaults/onyx.json +91 -0
- package/src/modes/theme/defaults/pearl.json +93 -0
- package/src/modes/theme/defaults/porcelain.json +91 -0
- package/src/modes/theme/defaults/quartz.json +96 -0
- package/src/modes/theme/defaults/sandstone.json +95 -0
- package/src/modes/theme/defaults/titanium.json +90 -0
- package/src/modes/theme/light.json +93 -0
- package/src/modes/theme/mermaid-cache.ts +111 -0
- package/src/modes/theme/theme-schema.json +429 -0
- package/src/modes/theme/theme.ts +2333 -0
- package/src/modes/types.ts +216 -0
- package/src/modes/utils/ui-helpers.ts +529 -0
- package/src/patch/applicator.ts +1482 -0
- package/src/patch/diff.ts +425 -0
- package/src/patch/fuzzy.ts +784 -0
- package/src/patch/hashline.ts +972 -0
- package/src/patch/index.ts +964 -0
- package/src/patch/normalize.ts +397 -0
- package/src/patch/normative.ts +72 -0
- package/src/patch/parser.ts +532 -0
- package/src/patch/shared.ts +400 -0
- package/src/patch/types.ts +292 -0
- package/src/priority.json +35 -0
- package/src/prompts/agents/explore.md +48 -0
- package/src/prompts/agents/frontmatter.md +9 -0
- package/src/prompts/agents/init.md +36 -0
- package/src/prompts/agents/librarian.md +53 -0
- package/src/prompts/agents/oracle.md +51 -0
- package/src/prompts/agents/reviewer.md +70 -0
- package/src/prompts/agents/task.md +14 -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 +64 -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/custom-system-prompt.md +68 -0
- package/src/prompts/system/file-operations.md +10 -0
- package/src/prompts/system/subagent-submit-reminder.md +11 -0
- package/src/prompts/system/subagent-system-prompt.md +31 -0
- package/src/prompts/system/subagent-user-prompt.md +8 -0
- package/src/prompts/system/summarization-system.md +3 -0
- package/src/prompts/system/system-prompt.md +300 -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 +44 -0
- package/src/prompts/tools/bash.md +24 -0
- package/src/prompts/tools/browser.md +33 -0
- package/src/prompts/tools/calculator.md +12 -0
- package/src/prompts/tools/explore.md +29 -0
- package/src/prompts/tools/fetch.md +16 -0
- package/src/prompts/tools/find.md +18 -0
- package/src/prompts/tools/gemini-image.md +23 -0
- package/src/prompts/tools/grep.md +28 -0
- package/src/prompts/tools/hashline.md +232 -0
- package/src/prompts/tools/librarian.md +24 -0
- package/src/prompts/tools/lsp.md +28 -0
- package/src/prompts/tools/oracle.md +26 -0
- package/src/prompts/tools/patch.md +74 -0
- package/src/prompts/tools/python.md +66 -0
- package/src/prompts/tools/read.md +36 -0
- package/src/prompts/tools/replace.md +38 -0
- package/src/prompts/tools/reviewer.md +41 -0
- package/src/prompts/tools/ssh.md +51 -0
- package/src/prompts/tools/task-summary.md +28 -0
- package/src/prompts/tools/task.md +275 -0
- package/src/prompts/tools/todo-write.md +65 -0
- package/src/prompts/tools/undo-edit.md +7 -0
- package/src/prompts/tools/web-search.md +19 -0
- package/src/prompts/tools/write.md +18 -0
- package/src/sdk.ts +1287 -0
- package/src/secrets/index.ts +116 -0
- package/src/secrets/obfuscator.ts +269 -0
- package/src/secrets/regex.ts +21 -0
- package/src/session/agent-session.ts +4669 -0
- package/src/session/agent-storage.ts +621 -0
- package/src/session/artifacts.ts +132 -0
- package/src/session/auth-storage.ts +1433 -0
- package/src/session/blob-store.ts +103 -0
- package/src/session/compaction/branch-summarization.ts +315 -0
- package/src/session/compaction/compaction.ts +864 -0
- package/src/session/compaction/index.ts +7 -0
- package/src/session/compaction/pruning.ts +91 -0
- package/src/session/compaction/utils.ts +171 -0
- package/src/session/history-storage.ts +170 -0
- package/src/session/messages.ts +317 -0
- package/src/session/session-manager.ts +2276 -0
- package/src/session/session-storage.ts +342 -0
- package/src/session/streaming-output.ts +565 -0
- package/src/slash-commands/builtin-registry.ts +439 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +444 -0
- package/src/ssh/ssh-executor.ts +127 -0
- package/src/ssh/sshfs-mount.ts +135 -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 +685 -0
- package/src/task/agents.ts +155 -0
- package/src/task/batch.ts +102 -0
- package/src/task/commands.ts +134 -0
- package/src/task/discovery.ts +126 -0
- package/src/task/executor.ts +908 -0
- package/src/task/index.ts +223 -0
- package/src/task/output-manager.ts +107 -0
- package/src/task/parallel.ts +84 -0
- package/src/task/render.ts +326 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/template.ts +32 -0
- package/src/task/types.ts +144 -0
- package/src/tools/ask.ts +523 -0
- package/src/tools/bash-interactive.ts +419 -0
- package/src/tools/bash-interceptor.ts +105 -0
- package/src/tools/bash-normalize.ts +107 -0
- package/src/tools/bash-skill-urls.ts +177 -0
- package/src/tools/bash.ts +347 -0
- package/src/tools/browser.ts +1374 -0
- package/src/tools/calculator.ts +537 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/explore.ts +23 -0
- package/src/tools/fetch.ts +1091 -0
- package/src/tools/find.ts +540 -0
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/gemini-image.ts +907 -0
- package/src/tools/grep.ts +489 -0
- package/src/tools/index.ts +337 -0
- package/src/tools/json-tree.ts +231 -0
- package/src/tools/jtd-to-json-schema.ts +247 -0
- package/src/tools/jtd-to-typescript.ts +198 -0
- package/src/tools/librarian.ts +33 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/notebook.ts +287 -0
- package/src/tools/oracle.ts +40 -0
- package/src/tools/output-meta.ts +459 -0
- package/src/tools/output-utils.ts +63 -0
- package/src/tools/path-utils.ts +116 -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 +1118 -0
- package/src/tools/read.ts +1193 -0
- package/src/tools/render-utils.ts +680 -0
- package/src/tools/renderers.ts +60 -0
- package/src/tools/reviewer-tool.ts +41 -0
- package/src/tools/ssh.ts +326 -0
- package/src/tools/subagent-tool.ts +169 -0
- package/src/tools/submit-result.ts +152 -0
- package/src/tools/todo-write.ts +255 -0
- package/src/tools/tool-errors.ts +92 -0
- package/src/tools/tool-result.ts +86 -0
- package/src/tools/undo-edit.ts +145 -0
- package/src/tools/undo-history.ts +22 -0
- package/src/tools/write.ts +274 -0
- package/src/tui/code-cell.ts +108 -0
- package/src/tui/file-list.ts +47 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +144 -0
- package/src/tui/status-line.ts +39 -0
- package/src/tui/tree-list.ts +53 -0
- package/src/tui/types.ts +16 -0
- package/src/tui/utils.ts +116 -0
- package/src/utils/changelog.ts +98 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +59 -0
- package/src/utils/file-display-mode.ts +36 -0
- package/src/utils/file-mentions.ts +384 -0
- package/src/utils/frontmatter.ts +101 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/ignore-files.ts +119 -0
- package/src/utils/image-convert.ts +27 -0
- package/src/utils/image-resize.ts +236 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/open.ts +20 -0
- package/src/utils/shell-snapshot.ts +199 -0
- package/src/utils/timings.ts +26 -0
- package/src/utils/title-generator.ts +167 -0
- package/src/utils/tools-manager.ts +362 -0
- package/src/web/scrapers/artifacthub.ts +215 -0
- package/src/web/scrapers/arxiv.ts +88 -0
- package/src/web/scrapers/aur.ts +175 -0
- package/src/web/scrapers/biorxiv.ts +141 -0
- package/src/web/scrapers/bluesky.ts +284 -0
- package/src/web/scrapers/brew.ts +177 -0
- package/src/web/scrapers/cheatsh.ts +78 -0
- package/src/web/scrapers/chocolatey.ts +158 -0
- package/src/web/scrapers/choosealicense.ts +110 -0
- package/src/web/scrapers/cisa-kev.ts +100 -0
- package/src/web/scrapers/clojars.ts +180 -0
- package/src/web/scrapers/coingecko.ts +184 -0
- package/src/web/scrapers/crates-io.ts +128 -0
- package/src/web/scrapers/crossref.ts +149 -0
- package/src/web/scrapers/devto.ts +177 -0
- package/src/web/scrapers/discogs.ts +307 -0
- package/src/web/scrapers/discourse.ts +221 -0
- package/src/web/scrapers/dockerhub.ts +160 -0
- package/src/web/scrapers/fdroid.ts +158 -0
- package/src/web/scrapers/firefox-addons.ts +214 -0
- package/src/web/scrapers/flathub.ts +239 -0
- package/src/web/scrapers/github-gist.ts +68 -0
- package/src/web/scrapers/github.ts +490 -0
- package/src/web/scrapers/gitlab.ts +456 -0
- package/src/web/scrapers/go-pkg.ts +275 -0
- package/src/web/scrapers/hackage.ts +94 -0
- package/src/web/scrapers/hackernews.ts +208 -0
- package/src/web/scrapers/hex.ts +121 -0
- package/src/web/scrapers/huggingface.ts +385 -0
- package/src/web/scrapers/iacr.ts +86 -0
- package/src/web/scrapers/index.ts +249 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +169 -0
- package/src/web/scrapers/lemmy.ts +220 -0
- package/src/web/scrapers/lobsters.ts +186 -0
- package/src/web/scrapers/mastodon.ts +310 -0
- package/src/web/scrapers/maven.ts +152 -0
- package/src/web/scrapers/mdn.ts +172 -0
- package/src/web/scrapers/metacpan.ts +253 -0
- package/src/web/scrapers/musicbrainz.ts +272 -0
- package/src/web/scrapers/npm.ts +114 -0
- package/src/web/scrapers/nuget.ts +205 -0
- package/src/web/scrapers/nvd.ts +243 -0
- package/src/web/scrapers/ollama.ts +265 -0
- package/src/web/scrapers/open-vsx.ts +119 -0
- package/src/web/scrapers/opencorporates.ts +275 -0
- package/src/web/scrapers/openlibrary.ts +319 -0
- package/src/web/scrapers/orcid.ts +298 -0
- package/src/web/scrapers/osv.ts +192 -0
- package/src/web/scrapers/packagist.ts +174 -0
- package/src/web/scrapers/pub-dev.ts +185 -0
- package/src/web/scrapers/pubmed.ts +177 -0
- package/src/web/scrapers/pypi.ts +129 -0
- package/src/web/scrapers/rawg.ts +124 -0
- package/src/web/scrapers/readthedocs.ts +125 -0
- package/src/web/scrapers/reddit.ts +104 -0
- package/src/web/scrapers/repology.ts +262 -0
- package/src/web/scrapers/rfc.ts +209 -0
- package/src/web/scrapers/rubygems.ts +117 -0
- package/src/web/scrapers/searchcode.ts +217 -0
- package/src/web/scrapers/sec-edgar.ts +274 -0
- package/src/web/scrapers/semantic-scholar.ts +190 -0
- package/src/web/scrapers/snapcraft.ts +200 -0
- package/src/web/scrapers/sourcegraph.ts +373 -0
- package/src/web/scrapers/spdx.ts +121 -0
- package/src/web/scrapers/spotify.ts +217 -0
- package/src/web/scrapers/stackoverflow.ts +124 -0
- package/src/web/scrapers/terraform.ts +304 -0
- package/src/web/scrapers/tldr.ts +51 -0
- package/src/web/scrapers/twitter.ts +97 -0
- package/src/web/scrapers/types.ts +200 -0
- package/src/web/scrapers/utils.ts +142 -0
- package/src/web/scrapers/vimeo.ts +152 -0
- package/src/web/scrapers/vscode-marketplace.ts +195 -0
- package/src/web/scrapers/w3c.ts +163 -0
- package/src/web/scrapers/wikidata.ts +357 -0
- package/src/web/scrapers/wikipedia.ts +95 -0
- package/src/web/scrapers/youtube.ts +312 -0
- package/src/web/search/auth.ts +178 -0
- package/src/web/search/index.ts +598 -0
- package/src/web/search/provider.ts +77 -0
- package/src/web/search/providers/anthropic.ts +284 -0
- package/src/web/search/providers/base.ts +22 -0
- package/src/web/search/providers/brave.ts +165 -0
- package/src/web/search/providers/codex.ts +377 -0
- package/src/web/search/providers/exa.ts +158 -0
- package/src/web/search/providers/gemini.ts +437 -0
- package/src/web/search/providers/jina.ts +99 -0
- package/src/web/search/providers/kimi.ts +196 -0
- package/src/web/search/providers/perplexity.ts +546 -0
- package/src/web/search/providers/synthetic.ts +136 -0
- package/src/web/search/providers/zai.ts +352 -0
- package/src/web/search/render.ts +299 -0
- package/src/web/search/types.ts +437 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-read file mentions from user prompts.
|
|
3
|
+
*
|
|
4
|
+
* When users reference files with @path syntax (e.g., "@src/foo.ts"),
|
|
5
|
+
* we automatically inject the file contents as a FileMentionMessage
|
|
6
|
+
* so the agent doesn't need to read them manually.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "node:fs/promises";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import type { AgentMessage } from "@nghyane/arcane-agent";
|
|
11
|
+
import { glob } from "@nghyane/arcane-natives";
|
|
12
|
+
import { formatHashLines } from "../patch/hashline";
|
|
13
|
+
import type { FileMentionMessage } from "../session/messages";
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_MAX_BYTES,
|
|
16
|
+
formatBytes,
|
|
17
|
+
truncateHead,
|
|
18
|
+
truncateStringToBytesFromStart,
|
|
19
|
+
} from "../session/streaming-output";
|
|
20
|
+
import { resolveReadPath } from "../tools/path-utils";
|
|
21
|
+
import { formatAge } from "../tools/render-utils";
|
|
22
|
+
import { fuzzyMatch } from "./fuzzy";
|
|
23
|
+
import { formatDimensionNote, resizeImage } from "./image-resize";
|
|
24
|
+
import { detectSupportedImageMimeTypeFromFile } from "./mime";
|
|
25
|
+
|
|
26
|
+
/** Regex to match @filepath patterns in text */
|
|
27
|
+
const FILE_MENTION_REGEX = /@([^\s@]+)/g;
|
|
28
|
+
const LEADING_PUNCTUATION_REGEX = /^[`"'([{<]+/;
|
|
29
|
+
const TRAILING_PUNCTUATION_REGEX = /[)\]}>.,;:!?"'`]+$/;
|
|
30
|
+
const MENTION_BOUNDARY_REGEX = /[\s([{<"'`]/;
|
|
31
|
+
const DEFAULT_DIR_LIMIT = 500;
|
|
32
|
+
const MIN_FUZZY_QUERY_LENGTH = 5;
|
|
33
|
+
const MAX_RESOLUTION_CANDIDATES = 20_000;
|
|
34
|
+
const PATH_SEPARATOR_REGEX = /[/._\-\s]+/g;
|
|
35
|
+
|
|
36
|
+
type MentionDiscoveryProfile = {
|
|
37
|
+
hidden: boolean;
|
|
38
|
+
gitignore: boolean;
|
|
39
|
+
includeNodeModules: boolean;
|
|
40
|
+
maxResults: number;
|
|
41
|
+
cache: boolean;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function getMentionCandidateDiscoveryProfile(): MentionDiscoveryProfile {
|
|
45
|
+
return {
|
|
46
|
+
hidden: true,
|
|
47
|
+
gitignore: true,
|
|
48
|
+
cache: true,
|
|
49
|
+
includeNodeModules: true,
|
|
50
|
+
maxResults: MAX_RESOLUTION_CANDIDATES,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Avoid OOM when users @mention very large files. Above these limits we skip
|
|
55
|
+
// auto-reading and only include the path in the message.
|
|
56
|
+
const MAX_AUTO_READ_TEXT_BYTES = 5 * 1024 * 1024; // 5MB
|
|
57
|
+
const MAX_AUTO_READ_IMAGE_BYTES = 25 * 1024 * 1024; // 25MB
|
|
58
|
+
|
|
59
|
+
function isMentionBoundary(text: string, index: number): boolean {
|
|
60
|
+
if (index === 0) return true;
|
|
61
|
+
return MENTION_BOUNDARY_REGEX.test(text[index - 1]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function sanitizeMentionPath(rawPath: string): string | null {
|
|
65
|
+
let cleaned = rawPath.trim();
|
|
66
|
+
cleaned = cleaned.replace(LEADING_PUNCTUATION_REGEX, "");
|
|
67
|
+
cleaned = cleaned.replace(TRAILING_PUNCTUATION_REGEX, "");
|
|
68
|
+
cleaned = cleaned.trim();
|
|
69
|
+
return cleaned.length > 0 ? cleaned : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type MentionCandidate = {
|
|
73
|
+
path: string;
|
|
74
|
+
pathLower: string;
|
|
75
|
+
normalizedPath: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
function normalizeMentionQuery(query: string): string {
|
|
79
|
+
return query.toLowerCase().replace(PATH_SEPARATOR_REGEX, "");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function pathExists(filePath: string): Promise<boolean> {
|
|
83
|
+
try {
|
|
84
|
+
await Bun.file(filePath).stat();
|
|
85
|
+
return true;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function listMentionCandidates(cwd: string): Promise<MentionCandidate[]> {
|
|
92
|
+
let entries: string[];
|
|
93
|
+
try {
|
|
94
|
+
const discoveryProfile = getMentionCandidateDiscoveryProfile();
|
|
95
|
+
const result = await glob({
|
|
96
|
+
pattern: "**/*",
|
|
97
|
+
path: cwd,
|
|
98
|
+
...discoveryProfile,
|
|
99
|
+
});
|
|
100
|
+
entries = result.matches.map(match => match.path);
|
|
101
|
+
} catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
106
|
+
const candidates: MentionCandidate[] = [];
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
const pathLower = entry.toLowerCase();
|
|
109
|
+
const normalizedPath = normalizeMentionQuery(entry);
|
|
110
|
+
if (normalizedPath.length === 0) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
candidates.push({ path: entry, pathLower, normalizedPath });
|
|
114
|
+
}
|
|
115
|
+
return candidates;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function resolveMentionPath(
|
|
119
|
+
filePath: string,
|
|
120
|
+
cwd: string,
|
|
121
|
+
getMentionCandidates: () => Promise<MentionCandidate[]>,
|
|
122
|
+
): Promise<string | null> {
|
|
123
|
+
const absolutePath = resolveReadPath(filePath, cwd);
|
|
124
|
+
if (await pathExists(absolutePath)) {
|
|
125
|
+
return filePath;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const queryLower = filePath.toLowerCase();
|
|
129
|
+
const candidates = await getMentionCandidates();
|
|
130
|
+
const prefixMatches = candidates.filter(candidate => candidate.pathLower.startsWith(queryLower));
|
|
131
|
+
if (prefixMatches.length === 1) {
|
|
132
|
+
return prefixMatches[0]?.path ?? null;
|
|
133
|
+
}
|
|
134
|
+
if (prefixMatches.length > 1) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const normalizedQuery = normalizeMentionQuery(filePath);
|
|
139
|
+
if (normalizedQuery.length < MIN_FUZZY_QUERY_LENGTH) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const scored = candidates
|
|
144
|
+
.map(candidate => ({ candidate, match: fuzzyMatch(normalizedQuery, candidate.normalizedPath) }))
|
|
145
|
+
.filter(entry => entry.match.matches)
|
|
146
|
+
.sort((a, b) => {
|
|
147
|
+
if (a.match.score !== b.match.score) {
|
|
148
|
+
return a.match.score - b.match.score;
|
|
149
|
+
}
|
|
150
|
+
return a.candidate.path.localeCompare(b.candidate.path);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (scored.length === 0) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const best = scored[0];
|
|
158
|
+
|
|
159
|
+
return best?.candidate.path ?? null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function buildTextOutput(textContent: string): { output: string; lineCount: number } {
|
|
163
|
+
const allLines = textContent.split("\n");
|
|
164
|
+
const totalFileLines = allLines.length;
|
|
165
|
+
const truncation = truncateHead(textContent);
|
|
166
|
+
|
|
167
|
+
if (truncation.firstLineExceedsLimit) {
|
|
168
|
+
const firstLine = allLines[0] ?? "";
|
|
169
|
+
const firstLineBytes = Buffer.byteLength(firstLine, "utf-8");
|
|
170
|
+
const snippet = truncateStringToBytesFromStart(firstLine, DEFAULT_MAX_BYTES);
|
|
171
|
+
let outputText = snippet.text;
|
|
172
|
+
|
|
173
|
+
if (outputText.length > 0) {
|
|
174
|
+
outputText += `\n\n[Line 1 is ${formatBytes(firstLineBytes)}, exceeds ${formatBytes(
|
|
175
|
+
DEFAULT_MAX_BYTES,
|
|
176
|
+
)} limit. Showing first ${formatBytes(snippet.bytes)} of the line.]`;
|
|
177
|
+
} else {
|
|
178
|
+
outputText = `[Line 1 is ${formatBytes(firstLineBytes)}, exceeds ${formatBytes(
|
|
179
|
+
DEFAULT_MAX_BYTES,
|
|
180
|
+
)} limit. Unable to display a valid UTF-8 snippet.]`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { output: outputText, lineCount: totalFileLines };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let outputText = truncation.content;
|
|
187
|
+
|
|
188
|
+
if (truncation.truncated) {
|
|
189
|
+
const endLineDisplay = truncation.outputLines;
|
|
190
|
+
const nextOffset = endLineDisplay + 1;
|
|
191
|
+
|
|
192
|
+
if (truncation.truncatedBy === "lines") {
|
|
193
|
+
outputText += `\n\n[Showing lines 1-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue]`;
|
|
194
|
+
} else {
|
|
195
|
+
outputText += `\n\n[Showing lines 1-${endLineDisplay} of ${totalFileLines} (${formatBytes(
|
|
196
|
+
DEFAULT_MAX_BYTES,
|
|
197
|
+
)} limit). Use offset=${nextOffset} to continue]`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { output: outputText, lineCount: totalFileLines };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function buildDirectoryListing(absolutePath: string): Promise<{ output: string; lineCount: number }> {
|
|
205
|
+
let entries: string[];
|
|
206
|
+
try {
|
|
207
|
+
entries = await Array.fromAsync(new Bun.Glob("*").scan({ cwd: absolutePath, dot: true, onlyFiles: false }));
|
|
208
|
+
} catch {
|
|
209
|
+
return { output: "(empty directory)", lineCount: 1 };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
213
|
+
|
|
214
|
+
const results: string[] = [];
|
|
215
|
+
let entryLimitReached = false;
|
|
216
|
+
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
if (results.length >= DEFAULT_DIR_LIMIT) {
|
|
219
|
+
entryLimitReached = true;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const fullPath = path.join(absolutePath, entry);
|
|
224
|
+
let suffix = "";
|
|
225
|
+
let age = "";
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const stat = await Bun.file(fullPath).stat();
|
|
229
|
+
if (stat.isDirectory()) {
|
|
230
|
+
suffix = "/";
|
|
231
|
+
}
|
|
232
|
+
const ageSeconds = Math.floor((Date.now() - stat.mtimeMs) / 1000);
|
|
233
|
+
age = formatAge(ageSeconds);
|
|
234
|
+
} catch {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const line = age ? `${entry}${suffix} (${age})` : `${entry}${suffix}`;
|
|
239
|
+
results.push(line);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (results.length === 0) {
|
|
243
|
+
return { output: "(empty directory)", lineCount: 1 };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const rawOutput = results.join("\n");
|
|
247
|
+
const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
248
|
+
let output = truncation.content;
|
|
249
|
+
|
|
250
|
+
const notices: string[] = [];
|
|
251
|
+
if (entryLimitReached) {
|
|
252
|
+
notices.push(`${DEFAULT_DIR_LIMIT} entries limit reached. Use limit=${DEFAULT_DIR_LIMIT * 2} for more`);
|
|
253
|
+
}
|
|
254
|
+
if (truncation.truncated) {
|
|
255
|
+
notices.push(`${formatBytes(DEFAULT_MAX_BYTES)} limit reached`);
|
|
256
|
+
}
|
|
257
|
+
if (notices.length > 0) {
|
|
258
|
+
output += `\n\n[${notices.join(". ")}]`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return { output, lineCount: output.split("\n").length };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Extract all @filepath mentions from text */
|
|
265
|
+
export function extractFileMentions(text: string): string[] {
|
|
266
|
+
const matches = [...text.matchAll(FILE_MENTION_REGEX)];
|
|
267
|
+
const mentions: string[] = [];
|
|
268
|
+
|
|
269
|
+
for (const match of matches) {
|
|
270
|
+
const index = match.index ?? 0;
|
|
271
|
+
if (!isMentionBoundary(text, index)) continue;
|
|
272
|
+
|
|
273
|
+
const cleaned = sanitizeMentionPath(match[1]);
|
|
274
|
+
if (!cleaned) continue;
|
|
275
|
+
|
|
276
|
+
mentions.push(cleaned);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return [...new Set(mentions)];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Generate a FileMentionMessage containing the contents of mentioned files.
|
|
284
|
+
* Returns empty array if no files could be read.
|
|
285
|
+
*/
|
|
286
|
+
export async function generateFileMentionMessages(
|
|
287
|
+
filePaths: string[],
|
|
288
|
+
cwd: string,
|
|
289
|
+
options?: { autoResizeImages?: boolean; useHashLines?: boolean },
|
|
290
|
+
): Promise<AgentMessage[]> {
|
|
291
|
+
if (filePaths.length === 0) return [];
|
|
292
|
+
|
|
293
|
+
const autoResizeImages = options?.autoResizeImages ?? true;
|
|
294
|
+
|
|
295
|
+
const files: FileMentionMessage["files"] = [];
|
|
296
|
+
let mentionCandidatesPromise: Promise<MentionCandidate[]> | null = null;
|
|
297
|
+
const getMentionCandidates = (): Promise<MentionCandidate[]> => {
|
|
298
|
+
mentionCandidatesPromise ??= listMentionCandidates(cwd);
|
|
299
|
+
return mentionCandidatesPromise;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
for (const filePath of filePaths) {
|
|
303
|
+
const resolvedPath = await resolveMentionPath(filePath, cwd, getMentionCandidates);
|
|
304
|
+
if (!resolvedPath) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const absolutePath = resolveReadPath(resolvedPath, cwd);
|
|
308
|
+
try {
|
|
309
|
+
const stat = await Bun.file(absolutePath).stat();
|
|
310
|
+
if (stat.isDirectory()) {
|
|
311
|
+
const { output, lineCount } = await buildDirectoryListing(absolutePath);
|
|
312
|
+
files.push({ path: resolvedPath, content: output, lineCount });
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);
|
|
317
|
+
if (mimeType) {
|
|
318
|
+
if (stat.size > MAX_AUTO_READ_IMAGE_BYTES) {
|
|
319
|
+
files.push({
|
|
320
|
+
path: resolvedPath,
|
|
321
|
+
content: `(skipped auto-read: too large, ${formatBytes(stat.size)})`,
|
|
322
|
+
byteSize: stat.size,
|
|
323
|
+
skippedReason: "tooLarge",
|
|
324
|
+
});
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const buffer = await fs.readFile(absolutePath);
|
|
328
|
+
if (buffer.length === 0) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const base64Content = buffer.toBase64();
|
|
333
|
+
let image = { type: "image" as const, mimeType, data: base64Content };
|
|
334
|
+
let dimensionNote: string | undefined;
|
|
335
|
+
|
|
336
|
+
if (autoResizeImages) {
|
|
337
|
+
try {
|
|
338
|
+
const resized = await resizeImage({ type: "image", data: base64Content, mimeType });
|
|
339
|
+
dimensionNote = formatDimensionNote(resized);
|
|
340
|
+
image = {
|
|
341
|
+
type: "image" as const,
|
|
342
|
+
mimeType: resized.mimeType,
|
|
343
|
+
data: resized.data,
|
|
344
|
+
};
|
|
345
|
+
} catch {
|
|
346
|
+
image = { type: "image" as const, mimeType, data: base64Content };
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
files.push({ path: resolvedPath, content: dimensionNote ?? "", image });
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (stat.size > MAX_AUTO_READ_TEXT_BYTES) {
|
|
355
|
+
files.push({
|
|
356
|
+
path: resolvedPath,
|
|
357
|
+
content: `(skipped auto-read: too large, ${formatBytes(stat.size)})`,
|
|
358
|
+
byteSize: stat.size,
|
|
359
|
+
skippedReason: "tooLarge",
|
|
360
|
+
});
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const content = await Bun.file(absolutePath).text();
|
|
365
|
+
let { output, lineCount } = buildTextOutput(content);
|
|
366
|
+
if (options?.useHashLines) {
|
|
367
|
+
output = formatHashLines(output);
|
|
368
|
+
}
|
|
369
|
+
files.push({ path: resolvedPath, content: output, lineCount });
|
|
370
|
+
} catch {
|
|
371
|
+
// File doesn't exist or isn't readable - skip silently
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (files.length === 0) return [];
|
|
376
|
+
|
|
377
|
+
const message: FileMentionMessage = {
|
|
378
|
+
role: "fileMention",
|
|
379
|
+
files,
|
|
380
|
+
timestamp: Date.now(),
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
return [message];
|
|
384
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { logger } from "@nghyane/arcane-utils";
|
|
2
|
+
import { YAML } from "bun";
|
|
3
|
+
|
|
4
|
+
function stripHtmlComments(content: string): string {
|
|
5
|
+
return content.replace(/<!--[\s\S]*?-->/g, "");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function toError(value: unknown): Error {
|
|
9
|
+
return value instanceof Error ? value : new Error(String(`YAML: ${value}`));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function truncate(content: string, maxLength: number): string {
|
|
13
|
+
return content.length > maxLength ? `${content.slice(0, maxLength)}…` : content;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class FrontmatterError extends Error {
|
|
17
|
+
constructor(
|
|
18
|
+
error: Error,
|
|
19
|
+
readonly source?: unknown,
|
|
20
|
+
) {
|
|
21
|
+
super(`Failed to parse YAML frontmatter (${source}): ${error.message}`, { cause: error });
|
|
22
|
+
this.name = "FrontmatterError";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
toString(): string {
|
|
26
|
+
// Format the error with stack and detail, including the error message, stack, and source if present
|
|
27
|
+
const details: string[] = [this.message];
|
|
28
|
+
if (this.source !== undefined) {
|
|
29
|
+
details.push(`Source: ${JSON.stringify(this.source)}`);
|
|
30
|
+
}
|
|
31
|
+
if (this.cause && typeof this.cause === "object" && "stack" in this.cause && this.cause.stack) {
|
|
32
|
+
details.push(`Stack:\n${this.cause.stack}`);
|
|
33
|
+
} else if (this.stack) {
|
|
34
|
+
details.push(`Stack:\n${this.stack}`);
|
|
35
|
+
}
|
|
36
|
+
return details.join("\n\n");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface FrontmatterOptions {
|
|
41
|
+
/** Source of the content (alias: source) */
|
|
42
|
+
location?: unknown;
|
|
43
|
+
/** Source of the content (alias for location) */
|
|
44
|
+
source?: unknown;
|
|
45
|
+
/** Fallback frontmatter values */
|
|
46
|
+
fallback?: Record<string, unknown>;
|
|
47
|
+
/** Normalize the content */
|
|
48
|
+
normalize?: boolean;
|
|
49
|
+
/** Level of error handling */
|
|
50
|
+
level?: "off" | "warn" | "fatal";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Parse YAML frontmatter from markdown content
|
|
55
|
+
* Returns { frontmatter, body } where body has frontmatter stripped
|
|
56
|
+
*/
|
|
57
|
+
export function parseFrontmatter(
|
|
58
|
+
content: string,
|
|
59
|
+
options?: FrontmatterOptions,
|
|
60
|
+
): { frontmatter: Record<string, unknown>; body: string } {
|
|
61
|
+
const { location, source, fallback, normalize = true, level = "warn" } = options ?? {};
|
|
62
|
+
const loc = location ?? source;
|
|
63
|
+
const frontmatter: Record<string, unknown> = Object.assign({}, fallback);
|
|
64
|
+
|
|
65
|
+
const normalized = normalize ? stripHtmlComments(content.replace(/\r\n/g, "\n").replace(/\r/g, "\n")) : content;
|
|
66
|
+
if (!normalized.startsWith("---")) {
|
|
67
|
+
return { frontmatter, body: normalized };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
71
|
+
if (endIndex === -1) {
|
|
72
|
+
return { frontmatter, body: normalized };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const metadata = normalized.slice(4, endIndex);
|
|
76
|
+
const body = normalized.slice(endIndex + 4).trim();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// Replace tabs with spaces for YAML compatibility, use failsafe mode for robustness
|
|
80
|
+
const loaded = YAML.parse(metadata.replaceAll("\t", " ")) as Record<string, unknown> | null;
|
|
81
|
+
return { frontmatter: Object.assign(frontmatter, loaded), body: body };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const err = new FrontmatterError(toError(error), loc ?? `Inline '${truncate(content, 64)}'`);
|
|
84
|
+
if (level === "warn" || level === "fatal") {
|
|
85
|
+
logger.warn("Failed to parse YAML frontmatter", { err: err.toString() });
|
|
86
|
+
}
|
|
87
|
+
if (level === "fatal") {
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Simple YAML parsing - just key: value pairs
|
|
92
|
+
for (const line of metadata.split("\n")) {
|
|
93
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
94
|
+
if (match) {
|
|
95
|
+
frontmatter[match[1]] = match[2].trim();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { frontmatter, body: body };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Fuzzy search. Matches if all query characters appear in order (not necessarily consecutive).
|
|
2
|
+
// Lower score = better match.
|
|
3
|
+
|
|
4
|
+
export interface FuzzyMatch {
|
|
5
|
+
matches: boolean;
|
|
6
|
+
score: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function fuzzyMatch(query: string, text: string): FuzzyMatch {
|
|
10
|
+
const queryLower = query.toLowerCase();
|
|
11
|
+
const textLower = text.toLowerCase();
|
|
12
|
+
|
|
13
|
+
if (queryLower.length === 0) {
|
|
14
|
+
return { matches: true, score: 0 };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (queryLower.length > textLower.length) {
|
|
18
|
+
return { matches: false, score: 0 };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let queryIndex = 0;
|
|
22
|
+
let score = 0;
|
|
23
|
+
let lastMatchIndex = -1;
|
|
24
|
+
let consecutiveMatches = 0;
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < textLower.length && queryIndex < queryLower.length; i++) {
|
|
27
|
+
if (textLower[i] === queryLower[queryIndex]) {
|
|
28
|
+
const isWordBoundary = i === 0 || /[\s\-_./]/.test(textLower[i - 1]!);
|
|
29
|
+
|
|
30
|
+
// Reward consecutive character matches (e.g., typing "foo" matches "foobar" better than "f_o_o")
|
|
31
|
+
if (lastMatchIndex === i - 1) {
|
|
32
|
+
consecutiveMatches++;
|
|
33
|
+
score -= consecutiveMatches * 5;
|
|
34
|
+
} else {
|
|
35
|
+
consecutiveMatches = 0;
|
|
36
|
+
// Penalize gaps between matched characters
|
|
37
|
+
if (lastMatchIndex >= 0) {
|
|
38
|
+
score += (i - lastMatchIndex - 1) * 2;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Reward matches at word boundaries (start of words are more likely intentional targets)
|
|
43
|
+
if (isWordBoundary) {
|
|
44
|
+
score -= 10;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Slight penalty for matches later in the string (prefer earlier matches)
|
|
48
|
+
score += i * 0.1;
|
|
49
|
+
|
|
50
|
+
lastMatchIndex = i;
|
|
51
|
+
queryIndex++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Not all query characters were found in order
|
|
56
|
+
if (queryIndex < queryLower.length) {
|
|
57
|
+
return { matches: false, score: 0 };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { matches: true, score };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Filter and sort items by fuzzy match quality (best matches first)
|
|
64
|
+
// Supports space-separated tokens: all tokens must match, sorted by match count then score
|
|
65
|
+
export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) => string): T[] {
|
|
66
|
+
if (!query.trim()) {
|
|
67
|
+
return items;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Split query into tokens
|
|
71
|
+
const tokens = query
|
|
72
|
+
.trim()
|
|
73
|
+
.split(/\s+/)
|
|
74
|
+
.filter(t => t.length > 0);
|
|
75
|
+
|
|
76
|
+
if (tokens.length === 0) {
|
|
77
|
+
return items;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const results: { item: T; totalScore: number }[] = [];
|
|
81
|
+
|
|
82
|
+
for (const item of items) {
|
|
83
|
+
const text = getText(item);
|
|
84
|
+
let totalScore = 0;
|
|
85
|
+
let allMatch = true;
|
|
86
|
+
|
|
87
|
+
// Check each token against the text - ALL must match
|
|
88
|
+
for (const token of tokens) {
|
|
89
|
+
const match = fuzzyMatch(token, text);
|
|
90
|
+
if (match.matches) {
|
|
91
|
+
totalScore += match.score;
|
|
92
|
+
} else {
|
|
93
|
+
allMatch = false;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Only include if all tokens match
|
|
99
|
+
if (allMatch) {
|
|
100
|
+
results.push({ item, totalScore });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Sort by score (asc, lower is better)
|
|
105
|
+
results.sort((a, b) => a.totalScore - b.totalScore);
|
|
106
|
+
|
|
107
|
+
return results.map(r => r.item);
|
|
108
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ignore file handling for .gitignore/.ignore/.fdignore support when scanning directories.
|
|
3
|
+
*/
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import ignore from "ignore";
|
|
6
|
+
|
|
7
|
+
export const IGNORE_FILE_NAMES = [".gitignore", ".ignore", ".fdignore"] as const;
|
|
8
|
+
|
|
9
|
+
export type IgnoreMatcher = ignore.Ignore;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Convert a path to POSIX format (forward slashes).
|
|
13
|
+
*/
|
|
14
|
+
export function toPosixPath(p: string): string {
|
|
15
|
+
return p.split(path.sep).join("/");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Prefix an ignore pattern to make it relative to a subdirectory.
|
|
20
|
+
* Returns null for comments and empty lines.
|
|
21
|
+
*/
|
|
22
|
+
export function prefixIgnorePattern(line: string, prefix: string): string | null {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (!trimmed) return null;
|
|
25
|
+
if (trimmed.startsWith("#") && !trimmed.startsWith("\\#")) return null;
|
|
26
|
+
|
|
27
|
+
let pattern = line;
|
|
28
|
+
let negated = false;
|
|
29
|
+
|
|
30
|
+
if (pattern.startsWith("!")) {
|
|
31
|
+
negated = true;
|
|
32
|
+
pattern = pattern.slice(1);
|
|
33
|
+
} else if (pattern.startsWith("\\!")) {
|
|
34
|
+
pattern = pattern.slice(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (pattern.startsWith("/")) {
|
|
38
|
+
pattern = pattern.slice(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const prefixed = prefix ? `${prefix}${pattern}` : pattern;
|
|
42
|
+
return negated ? `!${prefixed}` : prefixed;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Read and add ignore rules from a directory to the matcher.
|
|
47
|
+
*/
|
|
48
|
+
export async function addIgnoreRules(
|
|
49
|
+
ig: IgnoreMatcher,
|
|
50
|
+
dir: string,
|
|
51
|
+
rootDir: string,
|
|
52
|
+
readFile: (path: string) => Promise<string | null>,
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
const relativeDir = path.relative(rootDir, dir);
|
|
55
|
+
const prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : "";
|
|
56
|
+
|
|
57
|
+
for (const filename of IGNORE_FILE_NAMES) {
|
|
58
|
+
const ignorePath = path.join(dir, filename);
|
|
59
|
+
const content = await readFile(ignorePath);
|
|
60
|
+
if (!content) continue;
|
|
61
|
+
|
|
62
|
+
const patterns = content
|
|
63
|
+
.split(/\r?\n/)
|
|
64
|
+
.map(line => prefixIgnorePattern(line, prefix))
|
|
65
|
+
.filter((line): line is string => Boolean(line));
|
|
66
|
+
|
|
67
|
+
if (patterns.length > 0) {
|
|
68
|
+
ig.add(patterns);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Read and add ignore rules from a directory to the matcher (synchronous version).
|
|
75
|
+
*/
|
|
76
|
+
export function addIgnoreRulesSync(
|
|
77
|
+
ig: IgnoreMatcher,
|
|
78
|
+
dir: string,
|
|
79
|
+
rootDir: string,
|
|
80
|
+
readFileSync: (path: string) => string | null,
|
|
81
|
+
): void {
|
|
82
|
+
const relativeDir = path.relative(rootDir, dir);
|
|
83
|
+
const prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : "";
|
|
84
|
+
|
|
85
|
+
for (const filename of IGNORE_FILE_NAMES) {
|
|
86
|
+
const ignorePath = path.join(dir, filename);
|
|
87
|
+
const content = readFileSync(ignorePath);
|
|
88
|
+
if (!content) continue;
|
|
89
|
+
|
|
90
|
+
const patterns = content
|
|
91
|
+
.split(/\r?\n/)
|
|
92
|
+
.map(line => prefixIgnorePattern(line, prefix))
|
|
93
|
+
.filter((line): line is string => Boolean(line));
|
|
94
|
+
|
|
95
|
+
if (patterns.length > 0) {
|
|
96
|
+
ig.add(patterns);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a fresh ignore matcher.
|
|
103
|
+
*/
|
|
104
|
+
export function createIgnoreMatcher(): IgnoreMatcher {
|
|
105
|
+
return ignore();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if a path should be ignored.
|
|
110
|
+
* @param ig - The ignore matcher
|
|
111
|
+
* @param root - The root directory for relative path calculation
|
|
112
|
+
* @param fullPath - The full path to check
|
|
113
|
+
* @param isDir - Whether the path is a directory
|
|
114
|
+
*/
|
|
115
|
+
export function shouldIgnore(ig: IgnoreMatcher, root: string, fullPath: string, isDir: boolean): boolean {
|
|
116
|
+
const relPath = toPosixPath(path.relative(root, fullPath));
|
|
117
|
+
const ignorePath = isDir ? `${relPath}/` : relPath;
|
|
118
|
+
return ig.ignores(ignorePath);
|
|
119
|
+
}
|