@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,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /review command - Interactive code review launcher
|
|
3
|
+
*
|
|
4
|
+
* Provides a menu to select review mode:
|
|
5
|
+
* 1. Review against a base branch (PR style)
|
|
6
|
+
* 2. Review uncommitted changes
|
|
7
|
+
* 3. Review a specific commit
|
|
8
|
+
* 4. Custom review instructions
|
|
9
|
+
*
|
|
10
|
+
* Runs git diff upfront, parses results, filters noise, and provides
|
|
11
|
+
* rich context for the orchestrating agent to distribute work across
|
|
12
|
+
* multiple reviewer agents based on diff weight and locality.
|
|
13
|
+
*/
|
|
14
|
+
import { renderPromptTemplate } from "../../../../config/prompt-templates";
|
|
15
|
+
import type { CustomCommand, CustomCommandAPI } from "../../../../extensibility/custom-commands/types";
|
|
16
|
+
import type { HookCommandContext } from "../../../../extensibility/hooks/types";
|
|
17
|
+
import reviewRequestTemplate from "../../../../prompts/review-request.md" with { type: "text" };
|
|
18
|
+
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// Types
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
interface FileDiff {
|
|
24
|
+
path: string;
|
|
25
|
+
linesAdded: number;
|
|
26
|
+
linesRemoved: number;
|
|
27
|
+
hunks: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface DiffStats {
|
|
31
|
+
files: FileDiff[];
|
|
32
|
+
totalAdded: number;
|
|
33
|
+
totalRemoved: number;
|
|
34
|
+
excluded: { path: string; reason: string; linesAdded: number; linesRemoved: number }[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
// Exclusion patterns for noise files
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
const EXCLUDED_PATTERNS: { pattern: RegExp; reason: string }[] = [
|
|
42
|
+
// Lock files
|
|
43
|
+
{ pattern: /\.lock$/, reason: "lock file" },
|
|
44
|
+
{ pattern: /-lock\.(json|yaml|yml)$/, reason: "lock file" },
|
|
45
|
+
{ pattern: /package-lock\.json$/, reason: "lock file" },
|
|
46
|
+
{ pattern: /yarn\.lock$/, reason: "lock file" },
|
|
47
|
+
{ pattern: /pnpm-lock\.yaml$/, reason: "lock file" },
|
|
48
|
+
{ pattern: /Cargo\.lock$/, reason: "lock file" },
|
|
49
|
+
{ pattern: /Gemfile\.lock$/, reason: "lock file" },
|
|
50
|
+
{ pattern: /poetry\.lock$/, reason: "lock file" },
|
|
51
|
+
{ pattern: /composer\.lock$/, reason: "lock file" },
|
|
52
|
+
{ pattern: /flake\.lock$/, reason: "lock file" },
|
|
53
|
+
|
|
54
|
+
// Generated/build artifacts
|
|
55
|
+
{ pattern: /\.min\.(js|css)$/, reason: "minified" },
|
|
56
|
+
{ pattern: /\.generated\./, reason: "generated" },
|
|
57
|
+
{ pattern: /\.snap$/, reason: "snapshot" },
|
|
58
|
+
{ pattern: /\.map$/, reason: "source map" },
|
|
59
|
+
{ pattern: /^dist\//, reason: "build output" },
|
|
60
|
+
{ pattern: /^build\//, reason: "build output" },
|
|
61
|
+
{ pattern: /^out\//, reason: "build output" },
|
|
62
|
+
{ pattern: /node_modules\//, reason: "vendor" },
|
|
63
|
+
{ pattern: /vendor\//, reason: "vendor" },
|
|
64
|
+
|
|
65
|
+
// Binary/assets (usually shown as binary in diff anyway)
|
|
66
|
+
{ pattern: /\.(png|jpg|jpeg|gif|ico|webp|avif)$/i, reason: "image" },
|
|
67
|
+
{ pattern: /\.(woff|woff2|ttf|eot|otf)$/i, reason: "font" },
|
|
68
|
+
{ pattern: /\.(pdf|zip|tar|gz|rar|7z)$/i, reason: "binary" },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
// Diff parsing
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a file path should be excluded from review.
|
|
77
|
+
* Returns the exclusion reason if excluded, undefined otherwise.
|
|
78
|
+
*/
|
|
79
|
+
function getExclusionReason(path: string): string | undefined {
|
|
80
|
+
for (const { pattern, reason } of EXCLUDED_PATTERNS) {
|
|
81
|
+
if (pattern.test(path)) return reason;
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parse unified diff output into per-file stats.
|
|
88
|
+
* Splits on file boundaries, counts +/- lines, and filters excluded files.
|
|
89
|
+
*/
|
|
90
|
+
function parseDiff(diffOutput: string): DiffStats {
|
|
91
|
+
const files: FileDiff[] = [];
|
|
92
|
+
const excluded: DiffStats["excluded"] = [];
|
|
93
|
+
let totalAdded = 0;
|
|
94
|
+
let totalRemoved = 0;
|
|
95
|
+
|
|
96
|
+
// Split by file boundary: "diff --git a/... b/..."
|
|
97
|
+
const fileChunks = diffOutput.split(/^diff --git /m).filter(Boolean);
|
|
98
|
+
|
|
99
|
+
for (const chunk of fileChunks) {
|
|
100
|
+
// Extract file path from "a/path b/path" line
|
|
101
|
+
const headerMatch = chunk.match(/^a\/(.+?) b\/(.+)/);
|
|
102
|
+
if (!headerMatch) continue;
|
|
103
|
+
|
|
104
|
+
const path = headerMatch[2];
|
|
105
|
+
|
|
106
|
+
// Count added/removed lines (lines starting with + or - but not ++ or --)
|
|
107
|
+
let linesAdded = 0;
|
|
108
|
+
let linesRemoved = 0;
|
|
109
|
+
|
|
110
|
+
const lines = chunk.split("\n");
|
|
111
|
+
for (const line of lines) {
|
|
112
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
113
|
+
linesAdded++;
|
|
114
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
115
|
+
linesRemoved++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const exclusionReason = getExclusionReason(path);
|
|
120
|
+
if (exclusionReason) {
|
|
121
|
+
excluded.push({ path, reason: exclusionReason, linesAdded, linesRemoved });
|
|
122
|
+
} else {
|
|
123
|
+
files.push({
|
|
124
|
+
path,
|
|
125
|
+
linesAdded,
|
|
126
|
+
linesRemoved,
|
|
127
|
+
hunks: `diff --git ${chunk}`,
|
|
128
|
+
});
|
|
129
|
+
totalAdded += linesAdded;
|
|
130
|
+
totalRemoved += linesRemoved;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { files, totalAdded, totalRemoved, excluded };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get file extension for display purposes.
|
|
139
|
+
*/
|
|
140
|
+
function getFileExt(path: string): string {
|
|
141
|
+
const match = path.match(/\.([^.]+)$/);
|
|
142
|
+
return match ? match[1] : "";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Determine recommended number of reviewer agents based on diff weight.
|
|
147
|
+
* Uses total lines changed as the primary metric.
|
|
148
|
+
*/
|
|
149
|
+
function getRecommendedAgentCount(stats: DiffStats): number {
|
|
150
|
+
const totalLines = stats.totalAdded + stats.totalRemoved;
|
|
151
|
+
const fileCount = stats.files.length;
|
|
152
|
+
|
|
153
|
+
// Heuristics:
|
|
154
|
+
// - Tiny (<100 lines or 1-2 files): 1 agent
|
|
155
|
+
// - Small (<500 lines): 1-2 agents
|
|
156
|
+
// - Medium (<2000 lines): 2-4 agents
|
|
157
|
+
// - Large (<5000 lines): 4-8 agents
|
|
158
|
+
// - Huge (>5000 lines): 8-16 agents
|
|
159
|
+
|
|
160
|
+
if (totalLines < 100 || fileCount <= 2) return 1;
|
|
161
|
+
if (totalLines < 500) return Math.min(2, fileCount);
|
|
162
|
+
if (totalLines < 2000) return Math.min(4, Math.ceil(fileCount / 3));
|
|
163
|
+
if (totalLines < 5000) return Math.min(8, Math.ceil(fileCount / 2));
|
|
164
|
+
return Math.min(16, fileCount);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Extract first N lines of actual diff content (excluding headers) for preview.
|
|
169
|
+
*/
|
|
170
|
+
function getDiffPreview(hunks: string, maxLines: number): string {
|
|
171
|
+
const lines = hunks.split("\n");
|
|
172
|
+
const contentLines: string[] = [];
|
|
173
|
+
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
// Skip diff headers, keep actual content
|
|
176
|
+
if (
|
|
177
|
+
line.startsWith("diff --git") ||
|
|
178
|
+
line.startsWith("index ") ||
|
|
179
|
+
line.startsWith("---") ||
|
|
180
|
+
line.startsWith("+++") ||
|
|
181
|
+
line.startsWith("@@")
|
|
182
|
+
) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
contentLines.push(line);
|
|
186
|
+
if (contentLines.length >= maxLines) break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return contentLines.join("\n");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Thresholds for diff inclusion
|
|
193
|
+
const MAX_DIFF_CHARS = 50_000; // Don't include diff above this
|
|
194
|
+
const MAX_FILES_FOR_INLINE_DIFF = 20; // Don't include diff if more files than this
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Build the full review prompt with diff stats and distribution guidance.
|
|
198
|
+
*/
|
|
199
|
+
function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string): string {
|
|
200
|
+
const agentCount = getRecommendedAgentCount(stats);
|
|
201
|
+
const skipDiff = rawDiff.length > MAX_DIFF_CHARS || stats.files.length > MAX_FILES_FOR_INLINE_DIFF;
|
|
202
|
+
const totalLines = stats.totalAdded + stats.totalRemoved;
|
|
203
|
+
const linesPerFile = skipDiff ? Math.max(5, Math.floor(100 / stats.files.length)) : 0;
|
|
204
|
+
|
|
205
|
+
const filesWithExt = stats.files.map(f => ({
|
|
206
|
+
...f,
|
|
207
|
+
ext: getFileExt(f.path),
|
|
208
|
+
hunksPreview: skipDiff ? getDiffPreview(f.hunks, linesPerFile) : "",
|
|
209
|
+
}));
|
|
210
|
+
|
|
211
|
+
return renderPromptTemplate(reviewRequestTemplate, {
|
|
212
|
+
mode,
|
|
213
|
+
files: filesWithExt,
|
|
214
|
+
excluded: stats.excluded,
|
|
215
|
+
totalAdded: stats.totalAdded,
|
|
216
|
+
totalRemoved: stats.totalRemoved,
|
|
217
|
+
totalLines,
|
|
218
|
+
agentCount,
|
|
219
|
+
multiAgent: agentCount > 1,
|
|
220
|
+
skipDiff,
|
|
221
|
+
rawDiff: rawDiff.trim(),
|
|
222
|
+
linesPerFile,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export class ReviewCommand implements CustomCommand {
|
|
227
|
+
name = "review";
|
|
228
|
+
description = "Launch interactive code review";
|
|
229
|
+
|
|
230
|
+
constructor(private api: CustomCommandAPI) {}
|
|
231
|
+
|
|
232
|
+
async execute(_args: string[], ctx: HookCommandContext): Promise<string | undefined> {
|
|
233
|
+
if (!ctx.hasUI) {
|
|
234
|
+
return "Use the Review tool to review recent code changes.";
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const mode = await ctx.ui.select("Review Mode", [
|
|
238
|
+
"1. Review against a base branch (PR Style)",
|
|
239
|
+
"2. Review uncommitted changes",
|
|
240
|
+
"3. Review a specific commit",
|
|
241
|
+
"4. Custom review instructions",
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
if (!mode) return undefined;
|
|
245
|
+
|
|
246
|
+
const modeNum = parseInt(mode[0], 10);
|
|
247
|
+
|
|
248
|
+
switch (modeNum) {
|
|
249
|
+
case 1: {
|
|
250
|
+
// PR-style review against base branch
|
|
251
|
+
const branches = await getGitBranches(this.api);
|
|
252
|
+
if (branches.length === 0) {
|
|
253
|
+
ctx.ui.notify("No git branches found", "error");
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const baseBranch = await ctx.ui.select("Select base branch to compare against", branches);
|
|
258
|
+
if (!baseBranch) return undefined;
|
|
259
|
+
|
|
260
|
+
const currentBranch = await getCurrentBranch(this.api);
|
|
261
|
+
const diffResult = await this.api.exec("git", ["diff", `${baseBranch}...${currentBranch}`], {
|
|
262
|
+
timeout: 30000,
|
|
263
|
+
});
|
|
264
|
+
if (diffResult.code !== 0) {
|
|
265
|
+
ctx.ui.notify(`Failed to get diff: ${diffResult.stderr}`, "error");
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!diffResult.stdout.trim()) {
|
|
270
|
+
ctx.ui.notify(`No changes between ${baseBranch} and ${currentBranch}`, "warning");
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const stats = parseDiff(diffResult.stdout);
|
|
275
|
+
if (stats.files.length === 0) {
|
|
276
|
+
ctx.ui.notify("No reviewable files (all changes filtered out)", "warning");
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return buildReviewPrompt(
|
|
281
|
+
`Reviewing changes between \`${baseBranch}\` and \`${currentBranch}\` (PR-style)`,
|
|
282
|
+
stats,
|
|
283
|
+
diffResult.stdout,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
case 2: {
|
|
288
|
+
// Uncommitted changes - combine staged and unstaged
|
|
289
|
+
const status = await getGitStatus(this.api);
|
|
290
|
+
if (!status.trim()) {
|
|
291
|
+
ctx.ui.notify("No uncommitted changes found", "warning");
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const [unstagedResult, stagedResult] = await Promise.all([
|
|
296
|
+
this.api.exec("git", ["diff"], { timeout: 30000 }),
|
|
297
|
+
this.api.exec("git", ["diff", "--cached"], { timeout: 30000 }),
|
|
298
|
+
]);
|
|
299
|
+
|
|
300
|
+
const combinedDiff = [unstagedResult.stdout, stagedResult.stdout].filter(Boolean).join("\n");
|
|
301
|
+
|
|
302
|
+
if (!combinedDiff.trim()) {
|
|
303
|
+
ctx.ui.notify("No diff content found", "warning");
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const stats = parseDiff(combinedDiff);
|
|
308
|
+
if (stats.files.length === 0) {
|
|
309
|
+
ctx.ui.notify("No reviewable files (all changes filtered out)", "warning");
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return buildReviewPrompt("Reviewing uncommitted changes (staged + unstaged)", stats, combinedDiff);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
case 3: {
|
|
317
|
+
// Specific commit
|
|
318
|
+
const commits = await getRecentCommits(this.api, 20);
|
|
319
|
+
if (commits.length === 0) {
|
|
320
|
+
ctx.ui.notify("No commits found", "error");
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const selected = await ctx.ui.select("Select commit to review", commits);
|
|
325
|
+
if (!selected) return undefined;
|
|
326
|
+
|
|
327
|
+
// Extract commit hash from selection (format: "abc1234 message")
|
|
328
|
+
const hash = selected.split(" ")[0];
|
|
329
|
+
|
|
330
|
+
// Get the commit diff (with timeout)
|
|
331
|
+
const showResult = await this.api.exec("git", ["show", "--format=", hash], { timeout: 30000 });
|
|
332
|
+
if (showResult.code !== 0) {
|
|
333
|
+
ctx.ui.notify(`Failed to get commit: ${showResult.stderr}`, "error");
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!showResult.stdout.trim()) {
|
|
338
|
+
ctx.ui.notify("Commit has no diff content", "warning");
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const stats = parseDiff(showResult.stdout);
|
|
343
|
+
if (stats.files.length === 0) {
|
|
344
|
+
ctx.ui.notify("No reviewable files in commit (all changes filtered out)", "warning");
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return buildReviewPrompt(`Reviewing commit \`${hash}\``, stats, showResult.stdout);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
case 4: {
|
|
352
|
+
// Custom instructions - still uses the old approach since user provides context
|
|
353
|
+
const instructions = await ctx.ui.editor("Enter custom review instructions", "Review the following:\n\n");
|
|
354
|
+
if (!instructions?.trim()) return undefined;
|
|
355
|
+
|
|
356
|
+
// For custom, we still try to get current diff for context
|
|
357
|
+
const diffResult = await this.api.exec("git", ["diff", "HEAD"], { timeout: 30000 });
|
|
358
|
+
const hasDiff = diffResult.code === 0 && diffResult.stdout.trim();
|
|
359
|
+
|
|
360
|
+
if (hasDiff) {
|
|
361
|
+
const stats = parseDiff(diffResult.stdout);
|
|
362
|
+
// Even if all files filtered, include the custom instructions
|
|
363
|
+
return `${buildReviewPrompt(
|
|
364
|
+
`Custom review: ${instructions.split("\n")[0].slice(0, 60)}…`,
|
|
365
|
+
stats,
|
|
366
|
+
diffResult.stdout,
|
|
367
|
+
)}\n\n### Additional Instructions\n\n${instructions}`;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// No diff available, just pass instructions
|
|
371
|
+
return `## Code Review Request
|
|
372
|
+
|
|
373
|
+
### Mode
|
|
374
|
+
Custom review instructions
|
|
375
|
+
|
|
376
|
+
### Instructions
|
|
377
|
+
|
|
378
|
+
${instructions}
|
|
379
|
+
|
|
380
|
+
Use the Review tool to execute this review.`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
default:
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function getGitBranches(api: CustomCommandAPI): Promise<string[]> {
|
|
390
|
+
try {
|
|
391
|
+
const result = await api.exec("git", ["branch", "-a", "--format=%(refname:short)"]);
|
|
392
|
+
if (result.code !== 0) return [];
|
|
393
|
+
return result.stdout
|
|
394
|
+
.split("\n")
|
|
395
|
+
.map(b => b.trim())
|
|
396
|
+
.filter(Boolean);
|
|
397
|
+
} catch {
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function getCurrentBranch(api: CustomCommandAPI): Promise<string> {
|
|
403
|
+
try {
|
|
404
|
+
const result = await api.exec("git", ["branch", "--show-current"]);
|
|
405
|
+
return result.stdout.trim() || "HEAD";
|
|
406
|
+
} catch {
|
|
407
|
+
return "HEAD";
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function getGitStatus(api: CustomCommandAPI): Promise<string> {
|
|
412
|
+
try {
|
|
413
|
+
const result = await api.exec("git", ["status", "--porcelain"]);
|
|
414
|
+
return result.stdout;
|
|
415
|
+
} catch {
|
|
416
|
+
return "";
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function getRecentCommits(api: CustomCommandAPI, count: number): Promise<string[]> {
|
|
421
|
+
try {
|
|
422
|
+
const result = await api.exec("git", ["log", `-${count}`, "--oneline", "--no-decorate"]);
|
|
423
|
+
if (result.code !== 0) return [];
|
|
424
|
+
return result.stdout
|
|
425
|
+
.split("\n")
|
|
426
|
+
.map(c => c.trim())
|
|
427
|
+
.filter(Boolean);
|
|
428
|
+
} catch {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export default ReviewCommand;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type DiscoverCustomCommandsOptions,
|
|
3
|
+
type DiscoverCustomCommandsResult,
|
|
4
|
+
discoverCustomCommands,
|
|
5
|
+
type LoadCustomCommandsOptions,
|
|
6
|
+
loadCustomCommands,
|
|
7
|
+
} from "./loader";
|
|
8
|
+
export type {
|
|
9
|
+
CustomCommand,
|
|
10
|
+
CustomCommandAPI,
|
|
11
|
+
CustomCommandFactory,
|
|
12
|
+
CustomCommandSource,
|
|
13
|
+
CustomCommandsLoadResult,
|
|
14
|
+
LoadedCustomCommand,
|
|
15
|
+
} from "./types";
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom command loader - loads TypeScript command modules using native Bun import.
|
|
3
|
+
*
|
|
4
|
+
* Dependencies (@sinclair/typebox and pi-coding-agent) are injected via the CustomCommandAPI
|
|
5
|
+
* to avoid import resolution issues with custom commands loaded from user directories.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import * as piCodingAgent from "@nghyane/arcane";
|
|
10
|
+
import { isEnoent, logger } from "@nghyane/arcane-utils";
|
|
11
|
+
import { getAgentDir, getProjectDir } from "@nghyane/arcane-utils/dirs";
|
|
12
|
+
import * as typebox from "@sinclair/typebox";
|
|
13
|
+
import { getConfigDirs } from "../../config";
|
|
14
|
+
import { execCommand } from "../../exec/exec";
|
|
15
|
+
import { ReviewCommand } from "./bundled/review";
|
|
16
|
+
import type {
|
|
17
|
+
CustomCommand,
|
|
18
|
+
CustomCommandAPI,
|
|
19
|
+
CustomCommandFactory,
|
|
20
|
+
CustomCommandSource,
|
|
21
|
+
CustomCommandsLoadResult,
|
|
22
|
+
LoadedCustomCommand,
|
|
23
|
+
} from "./types";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Load a single command module using native Bun import.
|
|
27
|
+
*/
|
|
28
|
+
async function loadCommandModule(
|
|
29
|
+
commandPath: string,
|
|
30
|
+
_cwd: string,
|
|
31
|
+
sharedApi: CustomCommandAPI,
|
|
32
|
+
): Promise<{ commands: CustomCommand[] | null; error: string | null }> {
|
|
33
|
+
try {
|
|
34
|
+
const module = await import(commandPath);
|
|
35
|
+
const factory = (module.default ?? module) as CustomCommandFactory;
|
|
36
|
+
|
|
37
|
+
if (typeof factory !== "function") {
|
|
38
|
+
return { commands: null, error: "Command must export a default function" };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const result = await factory(sharedApi);
|
|
42
|
+
const commands = Array.isArray(result) ? result : [result];
|
|
43
|
+
|
|
44
|
+
// Validate commands
|
|
45
|
+
for (const cmd of commands) {
|
|
46
|
+
if (!cmd.name || typeof cmd.name !== "string") {
|
|
47
|
+
return { commands: null, error: "Command must have a name" };
|
|
48
|
+
}
|
|
49
|
+
if (!cmd.description || typeof cmd.description !== "string") {
|
|
50
|
+
return { commands: null, error: `Command "${cmd.name}" must have a description` };
|
|
51
|
+
}
|
|
52
|
+
if (typeof cmd.execute !== "function") {
|
|
53
|
+
return { commands: null, error: `Command "${cmd.name}" must have an execute function` };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { commands, error: null };
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
+
return { commands: null, error: `Failed to load command: ${message}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface DiscoverCustomCommandsOptions {
|
|
65
|
+
/** Current working directory. Default: getProjectDir() */
|
|
66
|
+
cwd?: string;
|
|
67
|
+
/** Agent config directory. Default: from getAgentDir() */
|
|
68
|
+
agentDir?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface DiscoverCustomCommandsResult {
|
|
72
|
+
/** Paths to command modules */
|
|
73
|
+
paths: Array<{ path: string; source: CustomCommandSource }>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Discover custom command modules (TypeScript slash commands).
|
|
78
|
+
* Markdown slash commands are handled by core/slash-commands.ts.
|
|
79
|
+
*/
|
|
80
|
+
export async function discoverCustomCommands(
|
|
81
|
+
options: DiscoverCustomCommandsOptions = {},
|
|
82
|
+
): Promise<DiscoverCustomCommandsResult> {
|
|
83
|
+
const cwd = options.cwd ?? getProjectDir();
|
|
84
|
+
const agentDir = options.agentDir ?? getAgentDir();
|
|
85
|
+
const paths: Array<{ path: string; source: CustomCommandSource }> = [];
|
|
86
|
+
const seen = new Set<string>();
|
|
87
|
+
|
|
88
|
+
const addPath = (commandPath: string, source: CustomCommandSource): void => {
|
|
89
|
+
const resolved = path.resolve(commandPath);
|
|
90
|
+
if (seen.has(resolved)) return;
|
|
91
|
+
seen.add(resolved);
|
|
92
|
+
paths.push({ path: resolved, source });
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const commandDirs: Array<{ path: string; source: CustomCommandSource }> = [];
|
|
96
|
+
if (agentDir) {
|
|
97
|
+
const userCommandsDir = path.join(agentDir, "commands");
|
|
98
|
+
if (fs.existsSync(userCommandsDir)) {
|
|
99
|
+
commandDirs.push({ path: userCommandsDir, source: "user" });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const entry of getConfigDirs("commands", { cwd, existingOnly: true })) {
|
|
104
|
+
const source = entry.level === "user" ? "user" : "project";
|
|
105
|
+
if (!commandDirs.some(d => d.path === entry.path)) {
|
|
106
|
+
commandDirs.push({ path: entry.path, source });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const indexCandidates = ["index.ts", "index.js", "index.mjs", "index.cjs"];
|
|
111
|
+
for (const { path: commandsDir, source } of commandDirs) {
|
|
112
|
+
let entries: fs.Dirent[];
|
|
113
|
+
try {
|
|
114
|
+
entries = await fs.promises.readdir(commandsDir, { withFileTypes: true });
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (!isEnoent(error)) {
|
|
117
|
+
logger.warn("Failed to read custom commands directory", { path: commandsDir, error: String(error) });
|
|
118
|
+
}
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
123
|
+
const commandDir = path.join(commandsDir, entry.name);
|
|
124
|
+
|
|
125
|
+
for (const filename of indexCandidates) {
|
|
126
|
+
const candidate = path.join(commandDir, filename);
|
|
127
|
+
if (fs.existsSync(candidate)) {
|
|
128
|
+
addPath(candidate, source);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { paths };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface LoadCustomCommandsOptions {
|
|
139
|
+
/** Current working directory. Default: getProjectDir() */
|
|
140
|
+
cwd?: string;
|
|
141
|
+
/** Agent config directory. Default: from getAgentDir() */
|
|
142
|
+
agentDir?: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Load bundled commands (shipped with pi-coding-agent).
|
|
147
|
+
*/
|
|
148
|
+
function loadBundledCommands(sharedApi: CustomCommandAPI): LoadedCustomCommand[] {
|
|
149
|
+
const bundled: LoadedCustomCommand[] = [];
|
|
150
|
+
|
|
151
|
+
// Add bundled commands here
|
|
152
|
+
bundled.push({
|
|
153
|
+
path: "bundled:review",
|
|
154
|
+
resolvedPath: "bundled:review",
|
|
155
|
+
command: new ReviewCommand(sharedApi),
|
|
156
|
+
source: "bundled",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return bundled;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Discover and load custom commands from standard locations.
|
|
164
|
+
*/
|
|
165
|
+
export async function loadCustomCommands(options: LoadCustomCommandsOptions = {}): Promise<CustomCommandsLoadResult> {
|
|
166
|
+
const cwd = options.cwd ?? getProjectDir();
|
|
167
|
+
const agentDir = options.agentDir ?? getAgentDir();
|
|
168
|
+
|
|
169
|
+
const { paths } = await discoverCustomCommands({ cwd, agentDir });
|
|
170
|
+
|
|
171
|
+
const commands: LoadedCustomCommand[] = [];
|
|
172
|
+
const errors: Array<{ path: string; error: string }> = [];
|
|
173
|
+
const seenNames = new Set<string>();
|
|
174
|
+
|
|
175
|
+
// Shared API object - all commands get the same instance
|
|
176
|
+
const sharedApi: CustomCommandAPI = {
|
|
177
|
+
cwd,
|
|
178
|
+
exec: (command: string, args: string[], execOptions) =>
|
|
179
|
+
execCommand(command, args, execOptions?.cwd ?? cwd, execOptions),
|
|
180
|
+
typebox,
|
|
181
|
+
pi: piCodingAgent,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// 1. Load bundled commands first (lowest priority - can be overridden)
|
|
185
|
+
for (const loaded of loadBundledCommands(sharedApi)) {
|
|
186
|
+
seenNames.add(loaded.command.name);
|
|
187
|
+
commands.push(loaded);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 2. Load user/project commands (can override bundled)
|
|
191
|
+
for (const { path: commandPath, source } of paths) {
|
|
192
|
+
const { commands: loadedCommands, error } = await loadCommandModule(commandPath, cwd, sharedApi);
|
|
193
|
+
|
|
194
|
+
if (error) {
|
|
195
|
+
errors.push({ path: commandPath, error });
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (loadedCommands) {
|
|
200
|
+
for (const command of loadedCommands) {
|
|
201
|
+
// Allow overriding bundled commands, but not user/project conflicts
|
|
202
|
+
const existingIdx = commands.findIndex(c => c.command.name === command.name);
|
|
203
|
+
if (existingIdx !== -1) {
|
|
204
|
+
const existing = commands[existingIdx];
|
|
205
|
+
if (existing.source === "bundled") {
|
|
206
|
+
// Override bundled command
|
|
207
|
+
commands.splice(existingIdx, 1);
|
|
208
|
+
seenNames.delete(command.name);
|
|
209
|
+
} else {
|
|
210
|
+
// Conflict between user/project commands
|
|
211
|
+
errors.push({
|
|
212
|
+
path: commandPath,
|
|
213
|
+
error: `Command name "${command.name}" conflicts with existing command`,
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
seenNames.add(command.name);
|
|
220
|
+
commands.push({
|
|
221
|
+
path: commandPath,
|
|
222
|
+
resolvedPath: path.resolve(commandPath),
|
|
223
|
+
command,
|
|
224
|
+
source,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return { commands, errors };
|
|
231
|
+
}
|