hyacinth-ai 0.9.6
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/LICENSE +21 -0
- package/README.md +205 -0
- package/commands.json +25 -0
- package/dist/agents/builtins.d.ts +8 -0
- package/dist/agents/builtins.js +50 -0
- package/dist/agents/config-loader.d.ts +15 -0
- package/dist/agents/config-loader.js +75 -0
- package/dist/agents/delegate-tool.d.ts +109 -0
- package/dist/agents/delegate-tool.js +407 -0
- package/dist/agents/index.d.ts +5 -0
- package/dist/agents/index.js +5 -0
- package/dist/agents/registry.d.ts +2 -0
- package/dist/agents/registry.js +2 -0
- package/dist/bypass/agents/companion/index.d.ts +57 -0
- package/dist/bypass/agents/companion/index.js +487 -0
- package/dist/bypass/agents/orchestrator/index.d.ts +17 -0
- package/dist/bypass/agents/orchestrator/index.js +414 -0
- package/dist/bypass/agents/orchestrator.d.ts +16 -0
- package/dist/bypass/agents/orchestrator.js +377 -0
- package/dist/bypass/agents/world-engine/agent.d.ts +41 -0
- package/dist/bypass/agents/world-engine/agent.js +385 -0
- package/dist/bypass/agents/world-engine/index.d.ts +52 -0
- package/dist/bypass/agents/world-engine/index.js +198 -0
- package/dist/bypass/agents/world-engine/sim.d.ts +14 -0
- package/dist/bypass/agents/world-engine/sim.js +120 -0
- package/dist/bypass/agents/world-engine/store.d.ts +110 -0
- package/dist/bypass/agents/world-engine/store.js +519 -0
- package/dist/bypass/agents/world-engine/ticker.d.ts +37 -0
- package/dist/bypass/agents/world-engine/ticker.js +214 -0
- package/dist/bypass/agents/world-engine/tools.d.ts +6 -0
- package/dist/bypass/agents/world-engine/tools.js +380 -0
- package/dist/bypass/agents/world-engine/types.d.ts +202 -0
- package/dist/bypass/agents/world-engine/types.js +56 -0
- package/dist/bypass/agents/world-engine.d.ts +20 -0
- package/dist/bypass/agents/world-engine.js +63 -0
- package/dist/bypass/base.d.ts +48 -0
- package/dist/bypass/base.js +126 -0
- package/dist/bypass/index.d.ts +5 -0
- package/dist/bypass/index.js +6 -0
- package/dist/bypass/manager.d.ts +41 -0
- package/dist/bypass/manager.js +179 -0
- package/dist/bypass/types.d.ts +63 -0
- package/dist/bypass/types.js +14 -0
- package/dist/channels/auto-detect.d.ts +25 -0
- package/dist/channels/auto-detect.js +77 -0
- package/dist/channels/builtin/http-webhook.d.ts +25 -0
- package/dist/channels/builtin/http-webhook.js +287 -0
- package/dist/channels/builtin/tui-channel.d.ts +33 -0
- package/dist/channels/builtin/tui-channel.js +61 -0
- package/dist/channels/builtin/tui-ws-session.d.ts +16 -0
- package/dist/channels/builtin/tui-ws-session.js +90 -0
- package/dist/channels/builtin/webui-output-handler.d.ts +30 -0
- package/dist/channels/builtin/webui-output-handler.js +93 -0
- package/dist/channels/index.d.ts +5 -0
- package/dist/channels/index.js +3 -0
- package/dist/channels/interface.d.ts +148 -0
- package/dist/channels/interface.js +15 -0
- package/dist/channels/manager.d.ts +43 -0
- package/dist/channels/manager.js +156 -0
- package/dist/channels/message-queue.d.ts +36 -0
- package/dist/channels/message-queue.js +91 -0
- package/dist/channels/plugins/clawbot/clawbot-auth.d.ts +50 -0
- package/dist/channels/plugins/clawbot/clawbot-auth.js +235 -0
- package/dist/channels/plugins/clawbot/clawbot-channel.d.ts +76 -0
- package/dist/channels/plugins/clawbot/clawbot-channel.js +566 -0
- package/dist/channels/plugins/clawbot/clawbot-client.d.ts +180 -0
- package/dist/channels/plugins/clawbot/clawbot-client.js +312 -0
- package/dist/channels/plugins/clawbot/clawbot-config.d.ts +30 -0
- package/dist/channels/plugins/clawbot/clawbot-config.js +28 -0
- package/dist/channels/plugins/clawbot/clawbot-message-queue.d.ts +27 -0
- package/dist/channels/plugins/clawbot/clawbot-message-queue.js +114 -0
- package/dist/channels/plugins/clawbot/clawbot-session.d.ts +10 -0
- package/dist/channels/plugins/clawbot/clawbot-session.js +26 -0
- package/dist/channels/plugins/clawbot/index.d.ts +17 -0
- package/dist/channels/plugins/clawbot/index.js +58 -0
- package/dist/channels/plugins/feishu/feishu-channel.d.ts +57 -0
- package/dist/channels/plugins/feishu/feishu-channel.js +602 -0
- package/dist/channels/plugins/feishu/feishu-client.d.ts +23 -0
- package/dist/channels/plugins/feishu/feishu-client.js +86 -0
- package/dist/channels/plugins/feishu/feishu-config.d.ts +53 -0
- package/dist/channels/plugins/feishu/feishu-config.js +35 -0
- package/dist/channels/plugins/feishu/feishu-event.d.ts +102 -0
- package/dist/channels/plugins/feishu/feishu-event.js +257 -0
- package/dist/channels/plugins/feishu/feishu-log-suppressor.d.ts +7 -0
- package/dist/channels/plugins/feishu/feishu-log-suppressor.js +34 -0
- package/dist/channels/plugins/feishu/feishu-message-queue.d.ts +27 -0
- package/dist/channels/plugins/feishu/feishu-message-queue.js +112 -0
- package/dist/channels/plugins/feishu/feishu-send.d.ts +44 -0
- package/dist/channels/plugins/feishu/feishu-send.js +232 -0
- package/dist/channels/plugins/feishu/feishu-session.d.ts +44 -0
- package/dist/channels/plugins/feishu/feishu-session.js +84 -0
- package/dist/channels/plugins/feishu/feishu-streaming.d.ts +49 -0
- package/dist/channels/plugins/feishu/feishu-streaming.js +185 -0
- package/dist/channels/plugins/feishu/feishu-transport.d.ts +29 -0
- package/dist/channels/plugins/feishu/feishu-transport.js +99 -0
- package/dist/channels/plugins/feishu/index.d.ts +22 -0
- package/dist/channels/plugins/feishu/index.js +68 -0
- package/dist/cli/doctor.d.ts +20 -0
- package/dist/cli/doctor.js +317 -0
- package/dist/context/cache-strategy.d.ts +76 -0
- package/dist/context/cache-strategy.js +123 -0
- package/dist/context/composer.d.ts +77 -0
- package/dist/context/composer.js +318 -0
- package/dist/context/compressor.d.ts +218 -0
- package/dist/context/compressor.js +909 -0
- package/dist/context/index.d.ts +14 -0
- package/dist/context/index.js +8 -0
- package/dist/context/interface.d.ts +33 -0
- package/dist/context/interface.js +2 -0
- package/dist/context/manifest-defaults.d.ts +51 -0
- package/dist/context/manifest-defaults.js +133 -0
- package/dist/context/manifest-loader.d.ts +16 -0
- package/dist/context/manifest-loader.js +103 -0
- package/dist/context/manifest-types.d.ts +26 -0
- package/dist/context/manifest-types.js +2 -0
- package/dist/context/precision/analyzer.d.ts +31 -0
- package/dist/context/precision/analyzer.js +131 -0
- package/dist/context/precision/companion.d.ts +27 -0
- package/dist/context/precision/companion.js +144 -0
- package/dist/context/precision/default.d.ts +14 -0
- package/dist/context/precision/default.js +17 -0
- package/dist/context/precision/index.d.ts +9 -0
- package/dist/context/precision/index.js +7 -0
- package/dist/context/precision/keyword-pool.d.ts +27 -0
- package/dist/context/precision/keyword-pool.js +66 -0
- package/dist/context/precision/strategy.d.ts +18 -0
- package/dist/context/precision/strategy.js +51 -0
- package/dist/context/precision/summary-store.d.ts +18 -0
- package/dist/context/precision/summary-store.js +67 -0
- package/dist/context/precision/types.d.ts +23 -0
- package/dist/context/precision/types.js +2 -0
- package/dist/context/profiles.d.ts +45 -0
- package/dist/context/profiles.js +92 -0
- package/dist/context/prompt-builder.d.ts +76 -0
- package/dist/context/prompt-builder.js +133 -0
- package/dist/context/retriever.d.ts +44 -0
- package/dist/context/retriever.js +203 -0
- package/dist/context/router.d.ts +122 -0
- package/dist/context/router.js +291 -0
- package/dist/context/section-resolver.d.ts +38 -0
- package/dist/context/section-resolver.js +398 -0
- package/dist/context/tokenizer.d.ts +23 -0
- package/dist/context/tokenizer.js +78 -0
- package/dist/dependency/analyzer.d.ts +33 -0
- package/dist/dependency/analyzer.js +242 -0
- package/dist/dependency/data-flow-tracker.d.ts +12 -0
- package/dist/dependency/data-flow-tracker.js +97 -0
- package/dist/dependency/function-parser.d.ts +34 -0
- package/dist/dependency/function-parser.js +295 -0
- package/dist/dependency/index.d.ts +14 -0
- package/dist/dependency/index.js +20 -0
- package/dist/dependency/parser.d.ts +10 -0
- package/dist/dependency/parser.js +93 -0
- package/dist/dependency/types.d.ts +57 -0
- package/dist/dependency/types.js +2 -0
- package/dist/env/env-collector.d.ts +45 -0
- package/dist/env/env-collector.js +221 -0
- package/dist/env/index.d.ts +3 -0
- package/dist/env/index.js +2 -0
- package/dist/event-store.d.ts +27 -0
- package/dist/event-store.js +71 -0
- package/dist/evolution/git-manager.d.ts +46 -0
- package/dist/evolution/git-manager.js +276 -0
- package/dist/evolution/index.d.ts +3 -0
- package/dist/evolution/index.js +2 -0
- package/dist/flow/bootstrap-flow.d.ts +19 -0
- package/dist/flow/bootstrap-flow.js +72 -0
- package/dist/flow/flow-registry.d.ts +12 -0
- package/dist/flow/flow-registry.js +74 -0
- package/dist/flow/flow-tools.d.ts +8 -0
- package/dist/flow/flow-tools.js +106 -0
- package/dist/flow/index.d.ts +6 -0
- package/dist/flow/index.js +8 -0
- package/dist/flow/todo-flow.d.ts +25 -0
- package/dist/flow/todo-flow.js +107 -0
- package/dist/flow/types.d.ts +78 -0
- package/dist/flow/types.js +12 -0
- package/dist/flows/bootstrap.d.ts +18 -0
- package/dist/flows/bootstrap.js +119 -0
- package/dist/flows/index.d.ts +6 -0
- package/dist/flows/index.js +11 -0
- package/dist/flows/todo.d.ts +26 -0
- package/dist/flows/todo.js +174 -0
- package/dist/flows/tools.d.ts +8 -0
- package/dist/flows/tools.js +119 -0
- package/dist/flows/types.d.ts +27 -0
- package/dist/flows/types.js +10 -0
- package/dist/gateway/cli.d.ts +5 -0
- package/dist/gateway/cli.js +1027 -0
- package/dist/gateway/factory.d.ts +80 -0
- package/dist/gateway/factory.js +832 -0
- package/dist/gateway/guardian.d.ts +8 -0
- package/dist/gateway/guardian.js +30 -0
- package/dist/gateway/index.d.ts +3 -0
- package/dist/gateway/index.js +3 -0
- package/dist/gateway/server.d.ts +17 -0
- package/dist/gateway/server.js +96 -0
- package/dist/gateway/tui.d.ts +16 -0
- package/dist/gateway/tui.js +3273 -0
- package/dist/hot-reload/agent-watcher.d.ts +24 -0
- package/dist/hot-reload/agent-watcher.js +175 -0
- package/dist/hot-reload/bundle-watcher.d.ts +12 -0
- package/dist/hot-reload/bundle-watcher.js +35 -0
- package/dist/hot-reload/channel-watcher.d.ts +19 -0
- package/dist/hot-reload/channel-watcher.js +54 -0
- package/dist/hot-reload/command-watcher.d.ts +14 -0
- package/dist/hot-reload/command-watcher.js +54 -0
- package/dist/hot-reload/config-watcher.d.ts +20 -0
- package/dist/hot-reload/config-watcher.js +58 -0
- package/dist/hot-reload/index.d.ts +12 -0
- package/dist/hot-reload/index.js +12 -0
- package/dist/hot-reload/manager.d.ts +58 -0
- package/dist/hot-reload/manager.js +243 -0
- package/dist/hot-reload/manifest-watcher.d.ts +9 -0
- package/dist/hot-reload/manifest-watcher.js +42 -0
- package/dist/hot-reload/mcp-watcher.d.ts +18 -0
- package/dist/hot-reload/mcp-watcher.js +64 -0
- package/dist/hot-reload/model-catalog-watcher.d.ts +18 -0
- package/dist/hot-reload/model-catalog-watcher.js +62 -0
- package/dist/hot-reload/plugin-watcher.d.ts +20 -0
- package/dist/hot-reload/plugin-watcher.js +56 -0
- package/dist/hot-reload/prompt-watcher.d.ts +6 -0
- package/dist/hot-reload/prompt-watcher.js +38 -0
- package/dist/hot-reload/provider-watcher.d.ts +16 -0
- package/dist/hot-reload/provider-watcher.js +45 -0
- package/dist/hot-reload/skill-watcher.d.ts +19 -0
- package/dist/hot-reload/skill-watcher.js +82 -0
- package/dist/hot-reload/tool-watcher.d.ts +16 -0
- package/dist/hot-reload/tool-watcher.js +168 -0
- package/dist/hot-reload/workflow-watcher.d.ts +19 -0
- package/dist/hot-reload/workflow-watcher.js +84 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/knowledge/fts5-retriever.d.ts +30 -0
- package/dist/knowledge/fts5-retriever.js +223 -0
- package/dist/knowledge/index.d.ts +11 -0
- package/dist/knowledge/index.js +14 -0
- package/dist/knowledge/indexer.d.ts +41 -0
- package/dist/knowledge/indexer.js +186 -0
- package/dist/knowledge/query-preprocessor.d.ts +13 -0
- package/dist/knowledge/query-preprocessor.js +63 -0
- package/dist/knowledge/retriever.d.ts +45 -0
- package/dist/knowledge/retriever.js +9 -0
- package/dist/knowledge/store.d.ts +27 -0
- package/dist/knowledge/store.js +61 -0
- package/dist/knowledge/structured-store.d.ts +78 -0
- package/dist/knowledge/structured-store.js +280 -0
- package/dist/knowledge/structured-tools.d.ts +12 -0
- package/dist/knowledge/structured-tools.js +151 -0
- package/dist/knowledge/tools.d.ts +20 -0
- package/dist/knowledge/tools.js +238 -0
- package/dist/knowledge/watcher.d.ts +28 -0
- package/dist/knowledge/watcher.js +113 -0
- package/dist/lifecycle/index.d.ts +12 -0
- package/dist/lifecycle/index.js +14 -0
- package/dist/lifecycle/interface.d.ts +94 -0
- package/dist/lifecycle/interface.js +2 -0
- package/dist/lifecycle/local-model.d.ts +62 -0
- package/dist/lifecycle/local-model.js +331 -0
- package/dist/lifecycle/manager.d.ts +70 -0
- package/dist/lifecycle/manager.js +374 -0
- package/dist/lifecycle/supervisor.d.ts +89 -0
- package/dist/lifecycle/supervisor.js +277 -0
- package/dist/lifecycle-integration-test.d.ts +2 -0
- package/dist/lifecycle-integration-test.js +72 -0
- package/dist/local-model/download-manager.d.ts +59 -0
- package/dist/local-model/download-manager.js +517 -0
- package/dist/local-model/index.d.ts +109 -0
- package/dist/local-model/index.js +233 -0
- package/dist/local-model/llamacpp-provider.d.ts +95 -0
- package/dist/local-model/llamacpp-provider.js +379 -0
- package/dist/local-model/model-bridge.d.ts +48 -0
- package/dist/local-model/model-bridge.js +256 -0
- package/dist/local-model/model-registry.d.ts +47 -0
- package/dist/local-model/model-registry.js +188 -0
- package/dist/local-model/types.d.ts +49 -0
- package/dist/local-model/types.js +2 -0
- package/dist/logging/index.d.ts +3 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging/logger.d.ts +56 -0
- package/dist/logging/logger.js +94 -0
- package/dist/machine/flows/bootstrap.d.ts +18 -0
- package/dist/machine/flows/bootstrap.js +119 -0
- package/dist/machine/flows/index.d.ts +6 -0
- package/dist/machine/flows/index.js +11 -0
- package/dist/machine/flows/spec.d.ts +17 -0
- package/dist/machine/flows/spec.js +176 -0
- package/dist/machine/flows/todo.d.ts +26 -0
- package/dist/machine/flows/todo.js +175 -0
- package/dist/machine/flows/tools.d.ts +8 -0
- package/dist/machine/flows/tools.js +119 -0
- package/dist/machine/flows/types.d.ts +33 -0
- package/dist/machine/flows/types.js +10 -0
- package/dist/machine/index.d.ts +8 -0
- package/dist/machine/index.js +9 -0
- package/dist/machine/registry.d.ts +34 -0
- package/dist/machine/registry.js +98 -0
- package/dist/machine/runner.d.ts +41 -0
- package/dist/machine/runner.js +177 -0
- package/dist/machine/types.d.ts +121 -0
- package/dist/machine/types.js +9 -0
- package/dist/mcp/bridge.d.ts +33 -0
- package/dist/mcp/bridge.js +99 -0
- package/dist/mcp/client.d.ts +37 -0
- package/dist/mcp/client.js +161 -0
- package/dist/mcp/config.d.ts +15 -0
- package/dist/mcp/config.js +85 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/install-manager.d.ts +31 -0
- package/dist/mcp/install-manager.js +158 -0
- package/dist/mcp/lifecycle.d.ts +41 -0
- package/dist/mcp/lifecycle.js +175 -0
- package/dist/mcp/process-transport.d.ts +21 -0
- package/dist/mcp/process-transport.js +59 -0
- package/dist/mcp/side-effect.d.ts +5 -0
- package/dist/mcp/side-effect.js +21 -0
- package/dist/mcp/system.d.ts +95 -0
- package/dist/mcp/system.js +279 -0
- package/dist/memory/companion-session.d.ts +22 -0
- package/dist/memory/companion-session.js +77 -0
- package/dist/memory/compression-boundary.d.ts +20 -0
- package/dist/memory/compression-boundary.js +40 -0
- package/dist/memory/conversation.d.ts +34 -0
- package/dist/memory/conversation.js +107 -0
- package/dist/memory/events.d.ts +42 -0
- package/dist/memory/events.js +56 -0
- package/dist/memory/index.d.ts +7 -0
- package/dist/memory/index.js +6 -0
- package/dist/memory/memory-store.d.ts +11 -0
- package/dist/memory/memory-store.js +51 -0
- package/dist/memory/session-allowlist.d.ts +11 -0
- package/dist/memory/session-allowlist.js +50 -0
- package/dist/memory/session.d.ts +48 -0
- package/dist/memory/session.js +275 -0
- package/dist/memory/stats.d.ts +23 -0
- package/dist/memory/stats.js +59 -0
- package/dist/memory/summary.d.ts +7 -0
- package/dist/memory/summary.js +23 -0
- package/dist/multimodal/index.d.ts +66 -0
- package/dist/multimodal/index.js +210 -0
- package/dist/orchestrator/index.d.ts +5 -0
- package/dist/orchestrator/index.js +4 -0
- package/dist/orchestrator/loop.d.ts +331 -0
- package/dist/orchestrator/loop.js +2443 -0
- package/dist/orchestrator/plan-store.d.ts +81 -0
- package/dist/orchestrator/plan-store.js +167 -0
- package/dist/orchestrator/planner.d.ts +20 -0
- package/dist/orchestrator/planner.js +34 -0
- package/dist/parser/index.d.ts +2 -0
- package/dist/parser/index.js +2 -0
- package/dist/parser/router.d.ts +30 -0
- package/dist/parser/router.js +61 -0
- package/dist/plugins/api.d.ts +39 -0
- package/dist/plugins/api.js +85 -0
- package/dist/plugins/define.d.ts +27 -0
- package/dist/plugins/define.js +32 -0
- package/dist/plugins/index.d.ts +9 -0
- package/dist/plugins/index.js +5 -0
- package/dist/plugins/loader.d.ts +39 -0
- package/dist/plugins/loader.js +128 -0
- package/dist/plugins/manager.d.ts +61 -0
- package/dist/plugins/manager.js +257 -0
- package/dist/plugins/types.d.ts +82 -0
- package/dist/plugins/types.js +2 -0
- package/dist/prompts/agents/code-reviewer.md +16 -0
- package/dist/prompts/agents/security-auditor.md +17 -0
- package/dist/prompts/agents/test-writer.md +15 -0
- package/dist/prompts/attention/attention.md +12 -0
- package/dist/prompts/environment/environment.md +18 -0
- package/dist/prompts/environment/feishu.md +107 -0
- package/dist/prompts/flows/bootstrap-confirm.md +14 -0
- package/dist/prompts/flows/bootstrap-identity.md +11 -0
- package/dist/prompts/flows/bootstrap-soul.md +19 -0
- package/dist/prompts/flows/bootstrap-user.md +11 -0
- package/dist/prompts/flows/spec-checklist.md +27 -0
- package/dist/prompts/flows/spec-define.md +64 -0
- package/dist/prompts/flows/spec-executing-checklist.md +15 -0
- package/dist/prompts/flows/spec-executing-tasks.md +15 -0
- package/dist/prompts/flows/spec-spec.md +51 -0
- package/dist/prompts/flows/spec-tasks.md +32 -0
- package/dist/prompts/flows/todo-execution.md +7 -0
- package/dist/prompts/flows/todo-planning.md +18 -0
- package/dist/prompts/framework/architecture.md +131 -0
- package/dist/prompts/identity/identity.md +2 -0
- package/dist/prompts/loader.d.ts +24 -0
- package/dist/prompts/loader.js +102 -0
- package/dist/prompts/loader.ts +119 -0
- package/dist/prompts/orchestration/summary.md +14 -0
- package/dist/prompts/persona/BOOTSTRAP.md +5 -0
- package/dist/prompts/persona/IDENTITY.md +16 -0
- package/dist/prompts/persona/PartnerMemory.md +5 -0
- package/dist/prompts/persona/PartnerSoul.md +7 -0
- package/dist/prompts/persona/SOUL.md +30 -0
- package/dist/prompts/persona/USER.md +16 -0
- package/dist/prompts/precise/analyze.md +23 -0
- package/dist/prompts/precise/persona.md +18 -0
- package/dist/prompts/safety/coding-standards.md +5 -0
- package/dist/prompts/safety/safety.md +4 -0
- package/dist/prompts/skills/code-review.md +4 -0
- package/dist/prompts/skills/debug.md +7 -0
- package/dist/prompts/skills/framework-reference.md +131 -0
- package/dist/prompts/skills/refactor.md +6 -0
- package/dist/prompts/summary.md +22 -0
- package/dist/prompts/tools/framework-capabilities.md +241 -0
- package/dist/prompts/tools/tool-rules.md +15 -0
- package/dist/provider/anthropic.d.ts +62 -0
- package/dist/provider/anthropic.js +361 -0
- package/dist/provider/catalog.d.ts +58 -0
- package/dist/provider/catalog.js +97 -0
- package/dist/provider/compatible.d.ts +89 -0
- package/dist/provider/compatible.js +328 -0
- package/dist/provider/config.d.ts +23 -0
- package/dist/provider/config.js +158 -0
- package/dist/provider/deepseek.d.ts +25 -0
- package/dist/provider/deepseek.js +37 -0
- package/dist/provider/fallback.d.ts +51 -0
- package/dist/provider/fallback.js +113 -0
- package/dist/provider/gemini.d.ts +27 -0
- package/dist/provider/gemini.js +170 -0
- package/dist/provider/index.d.ts +36 -0
- package/dist/provider/index.js +42 -0
- package/dist/provider/interface.d.ts +62 -0
- package/dist/provider/interface.js +2 -0
- package/dist/provider/local-config.d.ts +37 -0
- package/dist/provider/local-config.js +146 -0
- package/dist/provider/local.d.ts +45 -0
- package/dist/provider/local.js +248 -0
- package/dist/provider/manager.d.ts +67 -0
- package/dist/provider/manager.js +355 -0
- package/dist/provider/mimo.d.ts +19 -0
- package/dist/provider/mimo.js +30 -0
- package/dist/provider/minimax.d.ts +22 -0
- package/dist/provider/minimax.js +33 -0
- package/dist/provider/model-catalog-loader.d.ts +42 -0
- package/dist/provider/model-catalog-loader.js +89 -0
- package/dist/provider/model-channel-registry.d.ts +130 -0
- package/dist/provider/model-channel-registry.js +488 -0
- package/dist/provider/model-router.d.ts +60 -0
- package/dist/provider/model-router.js +87 -0
- package/dist/provider/openai.d.ts +54 -0
- package/dist/provider/openai.js +274 -0
- package/dist/provider/qwen.d.ts +19 -0
- package/dist/provider/qwen.js +30 -0
- package/dist/provider/resilient.d.ts +46 -0
- package/dist/provider/resilient.js +181 -0
- package/dist/provider/retry.d.ts +16 -0
- package/dist/provider/retry.js +78 -0
- package/dist/provider/router.d.ts +68 -0
- package/dist/provider/router.js +145 -0
- package/dist/provider/tool-args-recovery.d.ts +36 -0
- package/dist/provider/tool-args-recovery.js +91 -0
- package/dist/provider/user-id.d.ts +13 -0
- package/dist/provider/user-id.js +53 -0
- package/dist/provider/zhipu.d.ts +19 -0
- package/dist/provider/zhipu.js +32 -0
- package/dist/registry/agent.registry.d.ts +57 -0
- package/dist/registry/agent.registry.js +224 -0
- package/dist/registry/base.d.ts +54 -0
- package/dist/registry/base.js +99 -0
- package/dist/registry/channel.registry.d.ts +53 -0
- package/dist/registry/channel.registry.js +87 -0
- package/dist/registry/index.d.ts +9 -0
- package/dist/registry/index.js +10 -0
- package/dist/registry/mcp.registry.d.ts +2 -0
- package/dist/registry/mcp.registry.js +2 -0
- package/dist/registry/plugin.registry.d.ts +33 -0
- package/dist/registry/plugin.registry.js +66 -0
- package/dist/registry/provider.registry.d.ts +51 -0
- package/dist/registry/provider.registry.js +104 -0
- package/dist/registry/skill.registry.d.ts +33 -0
- package/dist/registry/skill.registry.js +95 -0
- package/dist/registry/tool.registry.d.ts +68 -0
- package/dist/registry/tool.registry.js +185 -0
- package/dist/repair/loop-guard.d.ts +98 -0
- package/dist/repair/loop-guard.js +217 -0
- package/dist/repair/scavenge.d.ts +19 -0
- package/dist/repair/scavenge.js +81 -0
- package/dist/repair/storm.d.ts +7 -0
- package/dist/repair/storm.js +7 -0
- package/dist/rollback/index.d.ts +5 -0
- package/dist/rollback/index.js +4 -0
- package/dist/rollback/rollback-tool.d.ts +13 -0
- package/dist/rollback/rollback-tool.js +167 -0
- package/dist/rollback/turn-recorder.d.ts +37 -0
- package/dist/rollback/turn-recorder.js +183 -0
- package/dist/rollback/turn-store.d.ts +29 -0
- package/dist/rollback/turn-store.js +114 -0
- package/dist/rollback/types.d.ts +51 -0
- package/dist/rollback/types.js +9 -0
- package/dist/runtime/config-center.d.ts +92 -0
- package/dist/runtime/config-center.js +467 -0
- package/dist/runtime/config-schema.d.ts +201 -0
- package/dist/runtime/config-schema.js +2 -0
- package/dist/runtime/defaults.d.ts +3 -0
- package/dist/runtime/defaults.js +146 -0
- package/dist/schedule/cron.d.ts +24 -0
- package/dist/schedule/cron.js +145 -0
- package/dist/schedule/index.d.ts +6 -0
- package/dist/schedule/index.js +4 -0
- package/dist/schedule/persistence.d.ts +33 -0
- package/dist/schedule/persistence.js +106 -0
- package/dist/schedule/scheduler.d.ts +85 -0
- package/dist/schedule/scheduler.js +630 -0
- package/dist/schedule/types.d.ts +185 -0
- package/dist/schedule/types.js +2 -0
- package/dist/setup/config.d.ts +189 -0
- package/dist/setup/config.js +253 -0
- package/dist/setup/index.d.ts +3 -0
- package/dist/setup/index.js +3 -0
- package/dist/setup/model-defaults.d.ts +13 -0
- package/dist/setup/model-defaults.js +87 -0
- package/dist/setup/persona-bootstrap.d.ts +42 -0
- package/dist/setup/persona-bootstrap.js +379 -0
- package/dist/setup/wizard.d.ts +32 -0
- package/dist/setup/wizard.js +384 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +4 -0
- package/dist/skills/loader.d.ts +12 -0
- package/dist/skills/loader.js +83 -0
- package/dist/skills/registry.d.ts +2 -0
- package/dist/skills/registry.js +2 -0
- package/dist/skills/skill-tool.d.ts +20 -0
- package/dist/skills/skill-tool.js +49 -0
- package/dist/tools/archive.d.ts +17 -0
- package/dist/tools/archive.js +131 -0
- package/dist/tools/ask-user.d.ts +9 -0
- package/dist/tools/ask-user.js +62 -0
- package/dist/tools/background-registry.d.ts +45 -0
- package/dist/tools/background-registry.js +164 -0
- package/dist/tools/bash.d.ts +56 -0
- package/dist/tools/bash.js +391 -0
- package/dist/tools/bundle-registry.d.ts +42 -0
- package/dist/tools/bundle-registry.js +239 -0
- package/dist/tools/bundle-tools.d.ts +11 -0
- package/dist/tools/bundle-tools.js +168 -0
- package/dist/tools/channel-info.d.ts +5 -0
- package/dist/tools/channel-info.js +38 -0
- package/dist/tools/compression.d.ts +7 -0
- package/dist/tools/compression.js +21 -0
- package/dist/tools/config.d.ts +23 -0
- package/dist/tools/config.js +255 -0
- package/dist/tools/db-query.d.ts +12 -0
- package/dist/tools/db-query.js +107 -0
- package/dist/tools/diagnostics.d.ts +30 -0
- package/dist/tools/diagnostics.js +111 -0
- package/dist/tools/diff-channel.d.ts +9 -0
- package/dist/tools/diff-channel.js +17 -0
- package/dist/tools/diff-files.d.ts +17 -0
- package/dist/tools/diff-files.js +88 -0
- package/dist/tools/disk-usage.d.ts +34 -0
- package/dist/tools/disk-usage.js +258 -0
- package/dist/tools/edit.d.ts +30 -0
- package/dist/tools/edit.js +235 -0
- package/dist/tools/executor.d.ts +32 -0
- package/dist/tools/executor.js +66 -0
- package/dist/tools/file-tracker.d.ts +20 -0
- package/dist/tools/file-tracker.js +44 -0
- package/dist/tools/filtered-registry.d.ts +29 -0
- package/dist/tools/filtered-registry.js +50 -0
- package/dist/tools/flow.d.ts +10 -0
- package/dist/tools/flow.js +180 -0
- package/dist/tools/git-tool.d.ts +24 -0
- package/dist/tools/git-tool.js +113 -0
- package/dist/tools/glob.d.ts +29 -0
- package/dist/tools/glob.js +152 -0
- package/dist/tools/grep.d.ts +14 -0
- package/dist/tools/grep.js +325 -0
- package/dist/tools/http-request.d.ts +14 -0
- package/dist/tools/http-request.js +126 -0
- package/dist/tools/index.d.ts +43 -0
- package/dist/tools/index.js +76 -0
- package/dist/tools/injection-filter.d.ts +6 -0
- package/dist/tools/injection-filter.js +71 -0
- package/dist/tools/insert.d.ts +13 -0
- package/dist/tools/insert.js +61 -0
- package/dist/tools/interface.d.ts +30 -0
- package/dist/tools/interface.js +2 -0
- package/dist/tools/json-edit.d.ts +15 -0
- package/dist/tools/json-edit.js +122 -0
- package/dist/tools/multi-edit.d.ts +9 -0
- package/dist/tools/multi-edit.js +146 -0
- package/dist/tools/path-sandbox.d.ts +14 -0
- package/dist/tools/path-sandbox.js +60 -0
- package/dist/tools/process-tools.d.ts +15 -0
- package/dist/tools/process-tools.js +95 -0
- package/dist/tools/python-bridge/bridge.d.ts +24 -0
- package/dist/tools/python-bridge/bridge.js +39 -0
- package/dist/tools/python-bridge/executor.d.ts +6 -0
- package/dist/tools/python-bridge/executor.js +69 -0
- package/dist/tools/python-bridge/index.d.ts +5 -0
- package/dist/tools/python-bridge/index.js +4 -0
- package/dist/tools/python-bridge/parser.d.ts +21 -0
- package/dist/tools/python-bridge/parser.js +258 -0
- package/dist/tools/read.d.ts +16 -0
- package/dist/tools/read.js +276 -0
- package/dist/tools/registry.d.ts +2 -0
- package/dist/tools/registry.js +2 -0
- package/dist/tools/restart.d.ts +10 -0
- package/dist/tools/restart.js +46 -0
- package/dist/tools/result-buffer.d.ts +66 -0
- package/dist/tools/result-buffer.js +146 -0
- package/dist/tools/runtime-control.d.ts +163 -0
- package/dist/tools/runtime-control.js +1780 -0
- package/dist/tools/symbol-references.d.ts +5 -0
- package/dist/tools/symbol-references.js +331 -0
- package/dist/tools/system-info.d.ts +3 -0
- package/dist/tools/system-info.js +55 -0
- package/dist/tools/write.d.ts +18 -0
- package/dist/tools/write.js +118 -0
- package/dist/tools/xref/index.d.ts +25 -0
- package/dist/tools/xref/index.js +25 -0
- package/dist/tools/xref/manager.d.ts +85 -0
- package/dist/tools/xref/manager.js +948 -0
- package/dist/tools/xref/parser.d.ts +34 -0
- package/dist/tools/xref/parser.js +64 -0
- package/dist/tools/xref/regex-parser.d.ts +26 -0
- package/dist/tools/xref/regex-parser.js +446 -0
- package/dist/tools/xref/schema.d.ts +104 -0
- package/dist/tools/xref/schema.js +68 -0
- package/dist/tools/xref/ts-parser.d.ts +20 -0
- package/dist/tools/xref/ts-parser.js +403 -0
- package/dist/tools/xref/xref-build.d.ts +20 -0
- package/dist/tools/xref/xref-build.js +128 -0
- package/dist/tools/xref/xref-graph.d.ts +24 -0
- package/dist/tools/xref/xref-graph.js +88 -0
- package/dist/tools/xref/xref-query.d.ts +30 -0
- package/dist/tools/xref/xref-query.js +123 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.js +2 -0
- package/dist/ui/assistant-message.d.ts +7 -0
- package/dist/ui/assistant-message.js +18 -0
- package/dist/ui/chat-log.d.ts +52 -0
- package/dist/ui/chat-log.js +246 -0
- package/dist/ui/command-registry.d.ts +73 -0
- package/dist/ui/command-registry.js +838 -0
- package/dist/ui/diff-component.d.ts +14 -0
- package/dist/ui/diff-component.js +42 -0
- package/dist/ui/hyperlink-markdown.d.ts +10 -0
- package/dist/ui/hyperlink-markdown.js +21 -0
- package/dist/ui/markdown-message.d.ts +10 -0
- package/dist/ui/markdown-message.js +16 -0
- package/dist/ui/osc8-hyperlinks.d.ts +4 -0
- package/dist/ui/osc8-hyperlinks.js +142 -0
- package/dist/ui/pi-tui-editor.d.ts +35 -0
- package/dist/ui/pi-tui-editor.js +79 -0
- package/dist/ui/slash-commands.d.ts +16 -0
- package/dist/ui/slash-commands.js +29 -0
- package/dist/ui/slash-panel.d.ts +54 -0
- package/dist/ui/slash-panel.js +199 -0
- package/dist/ui/theme.d.ts +120 -0
- package/dist/ui/theme.js +154 -0
- package/dist/ui/tool-display.d.ts +13 -0
- package/dist/ui/tool-display.js +85 -0
- package/dist/ui/tool-execution.d.ts +22 -0
- package/dist/ui/tool-execution.js +87 -0
- package/dist/ui/tui-formatters.d.ts +2 -0
- package/dist/ui/tui-formatters.js +145 -0
- package/dist/ui/user-message.d.ts +5 -0
- package/dist/ui/user-message.js +11 -0
- package/dist/update/check.d.ts +8 -0
- package/dist/update/check.js +28 -0
- package/dist/update/config.d.ts +6 -0
- package/dist/update/config.js +18 -0
- package/dist/update/download.d.ts +3 -0
- package/dist/update/download.js +27 -0
- package/dist/update/index.d.ts +6 -0
- package/dist/update/index.js +5 -0
- package/dist/update/install.d.ts +3 -0
- package/dist/update/install.js +33 -0
- package/dist/update/types.d.ts +16 -0
- package/dist/update/types.js +2 -0
- package/dist/utils/diff.d.ts +9 -0
- package/dist/utils/diff.js +45 -0
- package/dist/utils/misc.d.ts +12 -0
- package/dist/utils/misc.js +34 -0
- package/dist/world-engine/agent.d.ts +41 -0
- package/dist/world-engine/agent.js +385 -0
- package/dist/world-engine/index.d.ts +45 -0
- package/dist/world-engine/index.js +160 -0
- package/dist/world-engine/sim.d.ts +14 -0
- package/dist/world-engine/sim.js +120 -0
- package/dist/world-engine/store.d.ts +110 -0
- package/dist/world-engine/store.js +519 -0
- package/dist/world-engine/ticker.d.ts +37 -0
- package/dist/world-engine/ticker.js +214 -0
- package/dist/world-engine/tools.d.ts +6 -0
- package/dist/world-engine/tools.js +380 -0
- package/dist/world-engine/types.d.ts +202 -0
- package/dist/world-engine/types.js +56 -0
- package/docs/api.md +180 -0
- package/docs/architecture.md +206 -0
- package/docs/diagnostics/2026-06-08-config-prompt-guidance.md +67 -0
- package/docs/superpowers/plans/2026-06-08-global-bootstrap-mode.md +89 -0
- package/docs/superpowers/specs/2026-06-08-global-bootstrap-mode-design.md +174 -0
- package/docs/user-guide.md +226 -0
- package/package.json +63 -0
- package/plugins/example-greeter/index.js +55 -0
- package/plugins/example-greeter/plugin.json +8 -0
- package/pnpm-workspace.yaml +5 -0
- package/provider_arch.md +179 -0
- package/start-webui.sh +27 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/webui-channel-design.md +789 -0
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XrefManager — 交叉引用索引管理器。
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - 管理 SQLite 数据库(创建、打开、关闭)
|
|
6
|
+
* - build(): 扫描项目、解析源码、存入索引
|
|
7
|
+
* - query(): 执行交叉引用查询
|
|
8
|
+
* - graph(): 生成可视化输出
|
|
9
|
+
* - 增量更新:只重新解析修改过的文件
|
|
10
|
+
*
|
|
11
|
+
* 数据库位置:~/.agent/cache/xref-<projectKey>.sqlite
|
|
12
|
+
*
|
|
13
|
+
* 上下文注册链路:
|
|
14
|
+
* manifest-defaults.ts → 无(工具通过 Tool API 提供,不进入上下文)
|
|
15
|
+
* 工具注册:factory.ts → toolRegistry.register(new XrefBuildTool(manager))
|
|
16
|
+
* 数据库:better-sqlite3 → ~/.agent/cache/xref-*.sqlite
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs/promises';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import os from 'node:os';
|
|
21
|
+
import { SCHEMA_DDL } from './schema.js';
|
|
22
|
+
import { createParserRegistry } from './parser.js';
|
|
23
|
+
import { toProjectKey } from '../../utils/misc.js';
|
|
24
|
+
// 动态导入 better-sqlite3(使用 any 类型,与 db-query.ts 保持一致)
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
let BetterSqlite3 = null;
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
async function loadDb() {
|
|
29
|
+
if (BetterSqlite3)
|
|
30
|
+
return BetterSqlite3;
|
|
31
|
+
BetterSqlite3 = (await import('better-sqlite3')).default;
|
|
32
|
+
return BetterSqlite3;
|
|
33
|
+
}
|
|
34
|
+
export class XrefManager {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
db = null;
|
|
37
|
+
dbPath = '';
|
|
38
|
+
parserRegistry = null;
|
|
39
|
+
rootDir = '';
|
|
40
|
+
/** 初始化数据库(创建 if not exists,运行 schema DDL) */
|
|
41
|
+
async init(rootDir) {
|
|
42
|
+
this.rootDir = rootDir;
|
|
43
|
+
const projectKey = toProjectKey(rootDir);
|
|
44
|
+
const cacheDir = path.join(os.homedir(), '.agent', 'cache');
|
|
45
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
46
|
+
this.dbPath = path.join(cacheDir, `xref-${projectKey}.sqlite`);
|
|
47
|
+
const SqliteDb = await loadDb();
|
|
48
|
+
this.db = new SqliteDb(this.dbPath);
|
|
49
|
+
this.db.pragma('journal_mode = WAL');
|
|
50
|
+
this.db.pragma('foreign_keys = ON');
|
|
51
|
+
this.db.exec(SCHEMA_DDL);
|
|
52
|
+
}
|
|
53
|
+
/** 是否已初始化 */
|
|
54
|
+
isReady() {
|
|
55
|
+
return this.db !== null;
|
|
56
|
+
}
|
|
57
|
+
/** 获取索引统计信息 */
|
|
58
|
+
getStats() {
|
|
59
|
+
if (!this.db)
|
|
60
|
+
return null;
|
|
61
|
+
const files = this.db.prepare('SELECT COUNT(*) as c FROM files').get();
|
|
62
|
+
const symbols = this.db.prepare('SELECT COUNT(*) as c FROM symbols').get();
|
|
63
|
+
const refs = this.db.prepare('SELECT COUNT(*) as c FROM refs').get();
|
|
64
|
+
const imports = this.db.prepare('SELECT COUNT(*) as c FROM imports').get();
|
|
65
|
+
const meta = this.db.prepare('SELECT value FROM meta WHERE key = ?').get('built_at');
|
|
66
|
+
return {
|
|
67
|
+
files: files.c, symbols: symbols.c, refs: refs.c, imports: imports.c,
|
|
68
|
+
built_at: meta?.value ?? null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// ── 构建索引 ────────────────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* 构建/重建交叉引用索引。
|
|
74
|
+
* @param changedFiles 可选:只重建这些文件(增量更新)。undefined = 全量重建。
|
|
75
|
+
* @param dirs 可选:只扫描这些子目录(相对路径)。undefined = 全项目扫描。
|
|
76
|
+
*/
|
|
77
|
+
async build(changedFiles, dirs) {
|
|
78
|
+
if (!this.db)
|
|
79
|
+
throw new Error('XrefManager not initialized. Call init() first.');
|
|
80
|
+
const startedAt = Date.now();
|
|
81
|
+
const SqliteDb = await loadDb();
|
|
82
|
+
// 初始化 parser
|
|
83
|
+
if (!this.parserRegistry) {
|
|
84
|
+
this.parserRegistry = await createParserRegistry();
|
|
85
|
+
}
|
|
86
|
+
const isFullBuild = !changedFiles || changedFiles.length === 0;
|
|
87
|
+
if (isFullBuild) {
|
|
88
|
+
// 全量重建:清空所有数据
|
|
89
|
+
this.db.exec('DELETE FROM refs');
|
|
90
|
+
this.db.exec('DELETE FROM symbols');
|
|
91
|
+
this.db.exec('DELETE FROM imports');
|
|
92
|
+
this.db.exec('DELETE FROM files');
|
|
93
|
+
}
|
|
94
|
+
// 收集要解析的文件
|
|
95
|
+
let filesToParse;
|
|
96
|
+
if (isFullBuild) {
|
|
97
|
+
filesToParse = await this.scanFiles(this.rootDir, dirs);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// 增量:先删除旧数据
|
|
101
|
+
const deleteFile = this.db.prepare('DELETE FROM files WHERE path = ?');
|
|
102
|
+
for (const f of changedFiles) {
|
|
103
|
+
deleteFile.run(f);
|
|
104
|
+
}
|
|
105
|
+
filesToParse = changedFiles.filter(f => {
|
|
106
|
+
const ext = path.extname(f).toLowerCase();
|
|
107
|
+
return this.parserRegistry.getParser(f) !== null;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// 批量解析
|
|
111
|
+
const insertFile = this.db.prepare('INSERT OR REPLACE INTO files (path, language, hash, last_parsed_at) VALUES (?, ?, ?, ?)');
|
|
112
|
+
const insertSymbol = this.db.prepare('INSERT INTO symbols (name, kind, file_id, line, col, signature, is_exported, parent_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
|
|
113
|
+
const insertRef = this.db.prepare('INSERT INTO refs (symbol_name, file_id, line, col, kind, context, caller_name) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
|
114
|
+
const insertImport = this.db.prepare('INSERT INTO imports (from_file_id, to_file_id, symbols, import_type) VALUES (?, ?, ?, ?)');
|
|
115
|
+
let totalSymbols = 0;
|
|
116
|
+
let totalRefs = 0;
|
|
117
|
+
let totalImports = 0;
|
|
118
|
+
const langBreakdown = {};
|
|
119
|
+
// 分批解析(每次最多 50 个文件,避免内存问题)
|
|
120
|
+
const BATCH_SIZE = 50;
|
|
121
|
+
const allParsed = [];
|
|
122
|
+
for (let i = 0; i < filesToParse.length; i += BATCH_SIZE) {
|
|
123
|
+
const batch = filesToParse.slice(i, i + BATCH_SIZE);
|
|
124
|
+
const batchResults = await Promise.all(batch.map(async (f) => {
|
|
125
|
+
const parser = this.parserRegistry.getParser(f);
|
|
126
|
+
if (!parser)
|
|
127
|
+
return null;
|
|
128
|
+
try {
|
|
129
|
+
return { file: f, data: await parser.parseFile(f) };
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
135
|
+
for (const r of batchResults) {
|
|
136
|
+
if (r)
|
|
137
|
+
allParsed.push(r);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// 预解析所有导入路径(异步操作,不能放在事务中)
|
|
141
|
+
const resolvedImports = new Map();
|
|
142
|
+
for (const { file, data } of allParsed) {
|
|
143
|
+
for (const imp of data.imports) {
|
|
144
|
+
const key = `${file}::${imp.to_path}`;
|
|
145
|
+
if (!resolvedImports.has(key)) {
|
|
146
|
+
resolvedImports.set(key, await this.resolveImportPath(imp.to_path, file));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// 事务批量写入(同步操作)
|
|
151
|
+
// eslint-disable-next-line @typescript-es/no-explicit-any
|
|
152
|
+
const doBatch = this.db.transaction((parsedFiles) => {
|
|
153
|
+
for (const { file, data } of parsedFiles) {
|
|
154
|
+
const now = new Date().toISOString();
|
|
155
|
+
const result = insertFile.run(file, data.language, data.hash, now);
|
|
156
|
+
const fileId = result.lastInsertRowid;
|
|
157
|
+
langBreakdown[data.language] = (langBreakdown[data.language] ?? 0) + 1;
|
|
158
|
+
for (const sym of data.symbols) {
|
|
159
|
+
insertSymbol.run(sym.name, sym.kind, fileId, sym.line, sym.col, sym.signature ?? null, sym.is_exported ? 1 : 0, sym.parent_name ?? null);
|
|
160
|
+
totalSymbols++;
|
|
161
|
+
}
|
|
162
|
+
for (const ref of data.refs) {
|
|
163
|
+
insertRef.run(ref.symbol_name, fileId, ref.line, ref.col, ref.kind, ref.context ?? null, ref.caller_name ?? null);
|
|
164
|
+
totalRefs++;
|
|
165
|
+
}
|
|
166
|
+
for (const imp of data.imports) {
|
|
167
|
+
const key = `${file}::${imp.to_path}`;
|
|
168
|
+
const resolved = resolvedImports.get(key);
|
|
169
|
+
if (resolved) {
|
|
170
|
+
// 查找或创建目标文件记录
|
|
171
|
+
const toFile = this.db.prepare('SELECT id FROM files WHERE path = ?').get(resolved);
|
|
172
|
+
let toFileId;
|
|
173
|
+
if (toFile) {
|
|
174
|
+
toFileId = toFile.id;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const ext = path.extname(resolved).toLowerCase();
|
|
178
|
+
const lang = this.guessLanguage(ext);
|
|
179
|
+
const insResult = insertFile.run(resolved, lang, null, null);
|
|
180
|
+
toFileId = insResult.lastInsertRowid;
|
|
181
|
+
}
|
|
182
|
+
insertImport.run(fileId, toFileId, JSON.stringify(imp.symbols), imp.import_type);
|
|
183
|
+
totalImports++;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
doBatch(allParsed);
|
|
189
|
+
// 更新 meta
|
|
190
|
+
this.db.prepare('INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)').run('built_at', new Date().toISOString());
|
|
191
|
+
this.db.prepare('INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)').run('root_dir', this.rootDir);
|
|
192
|
+
const duration = Date.now() - startedAt;
|
|
193
|
+
return {
|
|
194
|
+
files: allParsed.length,
|
|
195
|
+
symbols: totalSymbols,
|
|
196
|
+
refs: totalRefs,
|
|
197
|
+
imports: totalImports,
|
|
198
|
+
duration_ms: duration,
|
|
199
|
+
language_breakdown: langBreakdown,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// ── 查询 ────────────────────────────────────────────────────────────
|
|
203
|
+
/**
|
|
204
|
+
* 执行交叉引用查询。
|
|
205
|
+
*/
|
|
206
|
+
query(params) {
|
|
207
|
+
if (!this.db)
|
|
208
|
+
return 'Error: Xref index not built. Run xref_build first.';
|
|
209
|
+
const stats = this.getStats();
|
|
210
|
+
if (!stats || stats.files === 0) {
|
|
211
|
+
return 'Error: Xref index is empty. Run xref_build to build the index first.';
|
|
212
|
+
}
|
|
213
|
+
switch (params.action) {
|
|
214
|
+
case 'refs': return this.queryRefs(params);
|
|
215
|
+
case 'defs': return this.queryDefs(params);
|
|
216
|
+
case 'callers': return this.queryCallers(params);
|
|
217
|
+
case 'callees': return this.queryCallees(params);
|
|
218
|
+
case 'deps': return this.queryDeps(params);
|
|
219
|
+
case 'dependents': return this.queryDependents(params);
|
|
220
|
+
case 'hierarchy': return this.queryHierarchy(params);
|
|
221
|
+
case 'impact': return this.queryImpact(params);
|
|
222
|
+
case 'trace': return this.queryTrace(params);
|
|
223
|
+
case 'symbol_search': return this.querySymbolSearch(params);
|
|
224
|
+
default:
|
|
225
|
+
return `Unknown action: "${params.action}". Supported: refs, defs, callers, callees, deps, dependents, hierarchy, impact, trace, symbol_search`;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
queryRefs(params) {
|
|
229
|
+
const { symbol } = params;
|
|
230
|
+
if (!symbol)
|
|
231
|
+
return 'Error: symbol is required for "refs" action.';
|
|
232
|
+
const rows = this.db.prepare(`SELECT r.*, f.path as file_path FROM refs r
|
|
233
|
+
JOIN files f ON r.file_id = f.id
|
|
234
|
+
WHERE r.symbol_name = ?
|
|
235
|
+
ORDER BY f.path, r.line
|
|
236
|
+
LIMIT 200`).all(symbol);
|
|
237
|
+
if (rows.length === 0)
|
|
238
|
+
return `No references to "${symbol}" found.`;
|
|
239
|
+
const lines = [];
|
|
240
|
+
lines.push(`References to "${symbol}" (${rows.length} found):`);
|
|
241
|
+
for (const r of rows) {
|
|
242
|
+
const shortPath = this.toRelative(r.file_path);
|
|
243
|
+
lines.push(` ${shortPath}:${r.line} [${r.kind}]${r.context ? ` — ${r.context}` : ''}`);
|
|
244
|
+
}
|
|
245
|
+
if (rows.length === 200)
|
|
246
|
+
lines.push(' ... (truncated at 200 results)');
|
|
247
|
+
return lines.join('\n');
|
|
248
|
+
}
|
|
249
|
+
queryDefs(params) {
|
|
250
|
+
const { symbol } = params;
|
|
251
|
+
if (!symbol)
|
|
252
|
+
return 'Error: symbol is required for "defs" action.';
|
|
253
|
+
const rows = this.db.prepare(`SELECT s.*, f.path as file_path FROM symbols s
|
|
254
|
+
JOIN files f ON s.file_id = f.id
|
|
255
|
+
WHERE s.name = ?
|
|
256
|
+
ORDER BY s.is_exported DESC, f.path`).all(symbol);
|
|
257
|
+
if (rows.length === 0)
|
|
258
|
+
return `No definition of "${symbol}" found.`;
|
|
259
|
+
// 如果指定了 kind,过滤
|
|
260
|
+
let filtered = rows;
|
|
261
|
+
if (params.kind) {
|
|
262
|
+
filtered = rows.filter(r => r.kind === params.kind);
|
|
263
|
+
}
|
|
264
|
+
const lines = [];
|
|
265
|
+
lines.push(`Definition(s) of "${symbol}" (${filtered.length}):`);
|
|
266
|
+
for (const s of filtered) {
|
|
267
|
+
const shortPath = this.toRelative(s.file_path);
|
|
268
|
+
const flags = [];
|
|
269
|
+
if (s.is_exported)
|
|
270
|
+
flags.push('exported');
|
|
271
|
+
if (s.parent_name)
|
|
272
|
+
flags.push(`in ${s.parent_name}`);
|
|
273
|
+
const flagStr = flags.length > 0 ? ` (${flags.join(', ')})` : '';
|
|
274
|
+
lines.push(` [${s.kind}] ${shortPath}:${s.line}${flagStr}`);
|
|
275
|
+
if (s.signature)
|
|
276
|
+
lines.push(` ${s.signature}`);
|
|
277
|
+
}
|
|
278
|
+
if (filtered.length > 1) {
|
|
279
|
+
lines.push(`Tip: use "kind" parameter to filter by type (function, class, method, etc.)`);
|
|
280
|
+
}
|
|
281
|
+
return lines.join('\n');
|
|
282
|
+
}
|
|
283
|
+
queryCallers(params) {
|
|
284
|
+
const { symbol } = params;
|
|
285
|
+
if (!symbol)
|
|
286
|
+
return 'Error: symbol is required for "callers" action.';
|
|
287
|
+
// 查找所有调用该符号的位置(kind = 'call' or 'new')
|
|
288
|
+
const rows = this.db.prepare(`SELECT r.*, f.path as file_path FROM refs r
|
|
289
|
+
JOIN files f ON r.file_id = f.id
|
|
290
|
+
WHERE r.symbol_name = ? AND r.kind IN ('call', 'new')
|
|
291
|
+
ORDER BY f.path, r.line
|
|
292
|
+
LIMIT 200`).all(symbol);
|
|
293
|
+
if (rows.length === 0)
|
|
294
|
+
return `No callers of "${symbol}" found.`;
|
|
295
|
+
const lines = [];
|
|
296
|
+
lines.push(`Callers of "${symbol}" (${rows.length} found):`);
|
|
297
|
+
for (const r of rows) {
|
|
298
|
+
const shortPath = this.toRelative(r.file_path);
|
|
299
|
+
const callerInfo = r.caller_name ? ` (in ${r.caller_name})` : '';
|
|
300
|
+
lines.push(` ${shortPath}:${r.line}${callerInfo}`);
|
|
301
|
+
if (r.context)
|
|
302
|
+
lines.push(` ${r.context}`);
|
|
303
|
+
}
|
|
304
|
+
if (rows.length === 200)
|
|
305
|
+
lines.push(' ... (truncated at 200 results)');
|
|
306
|
+
return lines.join('\n');
|
|
307
|
+
}
|
|
308
|
+
queryCallees(params) {
|
|
309
|
+
const { symbol, depth } = params;
|
|
310
|
+
if (!symbol)
|
|
311
|
+
return 'Error: symbol is required for "callees" action.';
|
|
312
|
+
const maxDepth = depth ?? 1;
|
|
313
|
+
const visited = new Set();
|
|
314
|
+
const output = [];
|
|
315
|
+
output.push(`Callees of "${symbol}" (max depth: ${maxDepth}):`);
|
|
316
|
+
this.traceCalleesRecursive(symbol, maxDepth, visited, output, 1);
|
|
317
|
+
if (output.length === 1) {
|
|
318
|
+
return `No callees found for "${symbol}". The function may have no calls or its body wasn't parsed.`;
|
|
319
|
+
}
|
|
320
|
+
return output.join('\n');
|
|
321
|
+
}
|
|
322
|
+
traceCalleesRecursive(funcName, maxDepth, visited, output, currentDepth) {
|
|
323
|
+
if (currentDepth > maxDepth || visited.has(funcName))
|
|
324
|
+
return;
|
|
325
|
+
visited.add(funcName);
|
|
326
|
+
// 查找该函数定义所在的文件
|
|
327
|
+
const symRow = this.db.prepare(`SELECT s.*, f.path as file_path FROM symbols s
|
|
328
|
+
JOIN files f ON s.file_id = f.id
|
|
329
|
+
WHERE s.name = ? AND s.kind IN ('function', 'method', 'arrow')
|
|
330
|
+
LIMIT 1`).get(funcName);
|
|
331
|
+
if (!symRow)
|
|
332
|
+
return;
|
|
333
|
+
// 查找该文件中以该函数为 caller_name 的 call refs
|
|
334
|
+
const calleeRows = this.db.prepare(`SELECT DISTINCT r.symbol_name FROM refs r
|
|
335
|
+
WHERE r.file_id = ? AND r.caller_name = ? AND r.kind = 'call'
|
|
336
|
+
LIMIT 50`).all(symRow.file_id, funcName);
|
|
337
|
+
const indent = ' '.repeat(currentDepth);
|
|
338
|
+
for (const callee of calleeRows) {
|
|
339
|
+
if (visited.has(callee.symbol_name))
|
|
340
|
+
continue;
|
|
341
|
+
output.push(`${indent}- ${callee.symbol_name}`);
|
|
342
|
+
this.traceCalleesRecursive(callee.symbol_name, maxDepth, visited, output, currentDepth + 1);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
queryDeps(params) {
|
|
346
|
+
const { file } = params;
|
|
347
|
+
if (!file)
|
|
348
|
+
return 'Error: file is required for "deps" action.';
|
|
349
|
+
const resolved = this.resolvePath(file);
|
|
350
|
+
const fileRow = this.db.prepare('SELECT id FROM files WHERE path = ?').get(resolved);
|
|
351
|
+
if (!fileRow)
|
|
352
|
+
return `File not in index: ${file}`;
|
|
353
|
+
const rows = this.db.prepare(`SELECT i.*, f.path as to_path FROM imports i
|
|
354
|
+
JOIN files f ON i.to_file_id = f.id
|
|
355
|
+
WHERE i.from_file_id = ?
|
|
356
|
+
ORDER BY f.path`).all(fileRow.id);
|
|
357
|
+
if (rows.length === 0)
|
|
358
|
+
return `${this.toRelative(resolved)} has no tracked dependencies.`;
|
|
359
|
+
const lines = [];
|
|
360
|
+
lines.push(`${this.toRelative(resolved)} depends on (${rows.length}):`);
|
|
361
|
+
for (const imp of rows) {
|
|
362
|
+
const syms = imp.symbols.length > 0 ? ` {${imp.symbols.join(', ')}}` : '';
|
|
363
|
+
lines.push(` - ${this.toRelative(imp.to_path)}${syms}`);
|
|
364
|
+
}
|
|
365
|
+
return lines.join('\n');
|
|
366
|
+
}
|
|
367
|
+
queryDependents(params) {
|
|
368
|
+
const { file } = params;
|
|
369
|
+
if (!file)
|
|
370
|
+
return 'Error: file is required for "dependents" action.';
|
|
371
|
+
const resolved = this.resolvePath(file);
|
|
372
|
+
const fileRow = this.db.prepare('SELECT id FROM files WHERE path = ?').get(resolved);
|
|
373
|
+
if (!fileRow)
|
|
374
|
+
return `File not in index: ${file}`;
|
|
375
|
+
const rows = this.db.prepare(`SELECT i.*, f.path as from_path FROM imports i
|
|
376
|
+
JOIN files f ON i.from_file_id = f.id
|
|
377
|
+
WHERE i.to_file_id = ?
|
|
378
|
+
ORDER BY f.path`).all(fileRow.id);
|
|
379
|
+
if (rows.length === 0)
|
|
380
|
+
return `No files depend on ${this.toRelative(resolved)}.`;
|
|
381
|
+
const lines = [];
|
|
382
|
+
lines.push(`Files depending on ${this.toRelative(resolved)} (${rows.length}):`);
|
|
383
|
+
for (const imp of rows) {
|
|
384
|
+
const syms = imp.symbols.length > 0 ? ` {${imp.symbols.join(', ')}}` : '';
|
|
385
|
+
lines.push(` - ${this.toRelative(imp.from_path)}${syms}`);
|
|
386
|
+
}
|
|
387
|
+
return lines.join('\n');
|
|
388
|
+
}
|
|
389
|
+
queryHierarchy(params) {
|
|
390
|
+
const { symbol } = params;
|
|
391
|
+
if (!symbol)
|
|
392
|
+
return 'Error: symbol is required for "hierarchy" action.';
|
|
393
|
+
// 查找类的继承关系
|
|
394
|
+
const classRow = this.db.prepare(`SELECT s.*, f.path as file_path FROM symbols s
|
|
395
|
+
JOIN files f ON s.file_id = f.id
|
|
396
|
+
WHERE s.name = ? AND s.kind = 'class'
|
|
397
|
+
LIMIT 1`).get(symbol);
|
|
398
|
+
if (!classRow)
|
|
399
|
+
return `Class "${symbol}" not found in index.`;
|
|
400
|
+
// 查找父类(通过 inherit refs)
|
|
401
|
+
const parentRefs = this.db.prepare(`SELECT r.symbol_name FROM refs r
|
|
402
|
+
WHERE r.file_id = ? AND r.kind = 'inherit' AND r.caller_name = ?
|
|
403
|
+
ORDER BY r.line`).all(classRow.file_id, symbol);
|
|
404
|
+
// 查找子类(通过 inherit refs 引用了该 symbol)
|
|
405
|
+
const childRefs = this.db.prepare(`SELECT r.caller_name as child_name, f.path as file_path, r.line FROM refs r
|
|
406
|
+
JOIN files f ON r.file_id = f.id
|
|
407
|
+
WHERE r.symbol_name = ? AND r.kind = 'inherit'
|
|
408
|
+
ORDER BY f.path`).all(symbol);
|
|
409
|
+
const lines = [];
|
|
410
|
+
lines.push(`Hierarchy for class "${symbol}" (${this.toRelative(classRow.file_path)}:${classRow.line}):`);
|
|
411
|
+
if (parentRefs.length > 0) {
|
|
412
|
+
lines.push(` Parents:`);
|
|
413
|
+
for (const p of parentRefs) {
|
|
414
|
+
lines.push(` - ${p.symbol_name}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
lines.push(` Parents: (none — root class)`);
|
|
419
|
+
}
|
|
420
|
+
if (childRefs.length > 0) {
|
|
421
|
+
lines.push(` Children (${childRefs.length}):`);
|
|
422
|
+
for (const c of childRefs) {
|
|
423
|
+
lines.push(` - ${c.child_name} (${this.toRelative(c.file_path)}:${c.line})`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
lines.push(` Children: (none)`);
|
|
428
|
+
}
|
|
429
|
+
return lines.join('\n');
|
|
430
|
+
}
|
|
431
|
+
queryImpact(params) {
|
|
432
|
+
const { file, symbol, depth } = params;
|
|
433
|
+
if (!file)
|
|
434
|
+
return 'Error: file is required for "impact" action.';
|
|
435
|
+
const resolved = this.resolvePath(file);
|
|
436
|
+
const maxDepth = depth ?? 2;
|
|
437
|
+
// BFS: 查找依赖该文件的所有文件
|
|
438
|
+
const visited = new Set();
|
|
439
|
+
const queue = [{ path: resolved, depth: 0 }];
|
|
440
|
+
visited.add(resolved);
|
|
441
|
+
const layers = new Map();
|
|
442
|
+
while (queue.length > 0) {
|
|
443
|
+
const { path: current, depth: d } = queue.shift();
|
|
444
|
+
if (d >= maxDepth)
|
|
445
|
+
continue;
|
|
446
|
+
const fileRow = this.db.prepare('SELECT id FROM files WHERE path = ?').get(current);
|
|
447
|
+
if (!fileRow)
|
|
448
|
+
continue;
|
|
449
|
+
// 查找导入 current 的文件
|
|
450
|
+
const dependents = this.db.prepare(`SELECT DISTINCT f.path as from_path FROM imports i
|
|
451
|
+
JOIN files f ON i.from_file_id = f.id
|
|
452
|
+
WHERE i.to_file_id = ?
|
|
453
|
+
ORDER BY f.path`).all(fileRow.id);
|
|
454
|
+
for (const dep of dependents) {
|
|
455
|
+
if (visited.has(dep.from_path))
|
|
456
|
+
continue;
|
|
457
|
+
visited.add(dep.from_path);
|
|
458
|
+
const nextDepth = d + 1;
|
|
459
|
+
const layer = layers.get(nextDepth) ?? [];
|
|
460
|
+
layer.push(dep.from_path);
|
|
461
|
+
layers.set(nextDepth, layer);
|
|
462
|
+
queue.push({ path: dep.from_path, depth: nextDepth });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (layers.size === 0) {
|
|
466
|
+
const symSuffix = symbol ? ` (symbol: ${symbol})` : '';
|
|
467
|
+
return `No files are affected by changing ${this.toRelative(resolved)}${symSuffix}.`;
|
|
468
|
+
}
|
|
469
|
+
const lines = [];
|
|
470
|
+
const symSuffix = symbol ? ` (symbol: ${symbol})` : '';
|
|
471
|
+
lines.push(`Impact of changing ${this.toRelative(resolved)}${symSuffix} (max depth: ${maxDepth}):`);
|
|
472
|
+
const sortedDepths = [...layers.keys()].sort((a, b) => a - b);
|
|
473
|
+
let total = 0;
|
|
474
|
+
for (const d of sortedDepths) {
|
|
475
|
+
const files = layers.get(d);
|
|
476
|
+
total += files.length;
|
|
477
|
+
lines.push(` Layer ${d} (${files.length} files):`);
|
|
478
|
+
for (const f of files) {
|
|
479
|
+
lines.push(` - ${this.toRelative(f)}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
lines.push(` Total: ${total} files across ${layers.size} layers`);
|
|
483
|
+
return lines.join('\n');
|
|
484
|
+
}
|
|
485
|
+
queryTrace(params) {
|
|
486
|
+
const { symbol, file } = params;
|
|
487
|
+
if (!symbol)
|
|
488
|
+
return 'Error: symbol is required for "trace" action.';
|
|
489
|
+
if (!file)
|
|
490
|
+
return 'Error: file is required for "trace" action.';
|
|
491
|
+
const resolved = this.resolvePath(file);
|
|
492
|
+
const fileRow = this.db.prepare('SELECT id FROM files WHERE path = ?').get(resolved);
|
|
493
|
+
if (!fileRow)
|
|
494
|
+
return `File not in index: ${file}`;
|
|
495
|
+
// 查找该变量在该文件中的所有引用
|
|
496
|
+
const rows = this.db.prepare(`SELECT * FROM refs
|
|
497
|
+
WHERE symbol_name = ? AND file_id = ?
|
|
498
|
+
ORDER BY line`).all(symbol, fileRow.id);
|
|
499
|
+
if (rows.length === 0)
|
|
500
|
+
return `No references to "${symbol}" found in ${this.toRelative(resolved)}.`;
|
|
501
|
+
const lines = [];
|
|
502
|
+
lines.push(`Data flow for "${symbol}" in ${this.toRelative(resolved)} (${rows.length} points):`);
|
|
503
|
+
// 按 kind 分组
|
|
504
|
+
const kindOrder = {
|
|
505
|
+
'write': 0, 'export': 1, 'call': 2, 'new': 3, 'read': 4, 'import': 5, 'inherit': 6,
|
|
506
|
+
};
|
|
507
|
+
const sorted = [...rows].sort((a, b) => {
|
|
508
|
+
const ka = kindOrder[a.kind] ?? 99;
|
|
509
|
+
const kb = kindOrder[b.kind] ?? 99;
|
|
510
|
+
if (ka !== kb)
|
|
511
|
+
return ka - kb;
|
|
512
|
+
return a.line - b.line;
|
|
513
|
+
});
|
|
514
|
+
let currentKind = '';
|
|
515
|
+
for (const r of sorted) {
|
|
516
|
+
if (r.kind !== currentKind) {
|
|
517
|
+
currentKind = r.kind;
|
|
518
|
+
const kindCount = sorted.filter(rr => rr.kind === currentKind).length;
|
|
519
|
+
lines.push(` ${currentKind} (${kindCount}):`);
|
|
520
|
+
}
|
|
521
|
+
lines.push(` L${r.line}${r.context ? `: ${r.context}` : ''}`);
|
|
522
|
+
}
|
|
523
|
+
return lines.join('\n');
|
|
524
|
+
}
|
|
525
|
+
querySymbolSearch(params) {
|
|
526
|
+
const { symbol } = params;
|
|
527
|
+
if (!symbol)
|
|
528
|
+
return 'Error: symbol is required for "symbol_search" action.';
|
|
529
|
+
// 查找哪些文件通过 import 引入了该符号
|
|
530
|
+
const rows = this.db.prepare(`SELECT i.*, f1.path as from_path, f2.path as to_path FROM imports i
|
|
531
|
+
JOIN files f1 ON i.from_file_id = f1.id
|
|
532
|
+
JOIN files f2 ON i.to_file_id = f2.id
|
|
533
|
+
WHERE i.symbols LIKE ?
|
|
534
|
+
ORDER BY f1.path
|
|
535
|
+
LIMIT 200`).all(`%${symbol}%`);
|
|
536
|
+
// 过滤:精确匹配符号名
|
|
537
|
+
const matched = rows.filter(r => {
|
|
538
|
+
try {
|
|
539
|
+
const syms = typeof r.symbols === 'string' ? JSON.parse(r.symbols) : r.symbols;
|
|
540
|
+
return syms.includes(symbol);
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
if (matched.length === 0)
|
|
547
|
+
return `No files import symbol "${symbol}".`;
|
|
548
|
+
const lines = [];
|
|
549
|
+
lines.push(`Files importing "${symbol}" (${matched.length}):`);
|
|
550
|
+
// 去重
|
|
551
|
+
const seen = new Set();
|
|
552
|
+
for (const imp of matched) {
|
|
553
|
+
const key = `${imp.from_path}→${imp.to_path}`;
|
|
554
|
+
if (seen.has(key))
|
|
555
|
+
continue;
|
|
556
|
+
seen.add(key);
|
|
557
|
+
lines.push(` ${this.toRelative(imp.from_path)} → ${this.toRelative(imp.to_path)} [${imp.import_type}]`);
|
|
558
|
+
}
|
|
559
|
+
return lines.join('\n');
|
|
560
|
+
}
|
|
561
|
+
// ── 可视化 ──────────────────────────────────────────────────────────
|
|
562
|
+
/**
|
|
563
|
+
* 生成调用图/依赖图可视化。
|
|
564
|
+
*/
|
|
565
|
+
graph(options) {
|
|
566
|
+
if (!this.db)
|
|
567
|
+
return 'Error: Xref index not built. Run xref_build first.';
|
|
568
|
+
const format = options.format ?? 'mermaid';
|
|
569
|
+
const maxDepth = options.max_depth ?? 3;
|
|
570
|
+
switch (format) {
|
|
571
|
+
case 'mermaid': return this.graphMermaid(options, maxDepth);
|
|
572
|
+
case 'text': return this.graphText(options, maxDepth);
|
|
573
|
+
case 'graphviz': return this.graphGraphviz(options, maxDepth);
|
|
574
|
+
default: return `Unknown format: ${format}. Supported: text, mermaid, graphviz`;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
graphMermaid(options, maxDepth) {
|
|
578
|
+
const { symbol, file, direction } = options;
|
|
579
|
+
const dir = direction ?? 'both';
|
|
580
|
+
const lines = [];
|
|
581
|
+
lines.push('```mermaid');
|
|
582
|
+
lines.push('graph LR');
|
|
583
|
+
const visited = new Set();
|
|
584
|
+
const edges = []; // [from, to, label]
|
|
585
|
+
if (symbol) {
|
|
586
|
+
// 以符号为中心的调用图
|
|
587
|
+
if (dir === 'callers' || dir === 'both') {
|
|
588
|
+
this.collectCallerEdges(symbol, maxDepth, visited, edges, 'calls');
|
|
589
|
+
}
|
|
590
|
+
if (dir === 'callees' || dir === 'both') {
|
|
591
|
+
this.collectCalleeEdges(symbol, maxDepth, visited, edges, 'calls');
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (file) {
|
|
595
|
+
// 以文件为中心的依赖图
|
|
596
|
+
this.collectFileDepsEdges(file, maxDepth, visited, edges);
|
|
597
|
+
}
|
|
598
|
+
if (edges.length === 0) {
|
|
599
|
+
lines.push(' empty[No relationships found]');
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
const nodeIds = new Set();
|
|
603
|
+
for (const [from, to] of edges) {
|
|
604
|
+
nodeIds.add(from);
|
|
605
|
+
nodeIds.add(to);
|
|
606
|
+
}
|
|
607
|
+
for (const id of nodeIds) {
|
|
608
|
+
const escaped = id.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
609
|
+
lines.push(` ${escaped}[${this.shorten(id)}]`);
|
|
610
|
+
}
|
|
611
|
+
for (const [from, to, label] of edges) {
|
|
612
|
+
const fEsc = from.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
613
|
+
const tEsc = to.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
614
|
+
lines.push(` ${fEsc} -->|${label}| ${tEsc}`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
lines.push('```');
|
|
618
|
+
return lines.join('\n');
|
|
619
|
+
}
|
|
620
|
+
graphText(options, maxDepth) {
|
|
621
|
+
const { symbol, file, direction } = options;
|
|
622
|
+
const dir = direction ?? 'both';
|
|
623
|
+
const visited = new Set();
|
|
624
|
+
const lines = [];
|
|
625
|
+
if (symbol) {
|
|
626
|
+
lines.push(`Call graph for "${symbol}" (max depth: ${maxDepth}):`);
|
|
627
|
+
if (dir === 'callers' || dir === 'both') {
|
|
628
|
+
lines.push(' ▲ Callers (who calls this):');
|
|
629
|
+
this.textTreeCallers(symbol, '', visited, maxDepth, 0, lines);
|
|
630
|
+
}
|
|
631
|
+
if (dir === 'callees' || dir === 'both') {
|
|
632
|
+
lines.push(' ▼ Callees (this calls):');
|
|
633
|
+
this.textTreeCallees(symbol, '', new Set(), maxDepth, 0, lines);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (file) {
|
|
637
|
+
const resolved = this.resolvePath(file);
|
|
638
|
+
lines.push(`Dependency graph for ${this.toRelative(resolved)} (max depth: ${maxDepth}):`);
|
|
639
|
+
this.textTreeFileDeps(resolved, '', new Set(), maxDepth, 0, lines);
|
|
640
|
+
}
|
|
641
|
+
return lines.join('\n');
|
|
642
|
+
}
|
|
643
|
+
textTreeCallers(symbol, prefix, visited, maxDepth, depth, output) {
|
|
644
|
+
if (depth >= maxDepth || visited.has(symbol))
|
|
645
|
+
return;
|
|
646
|
+
visited.add(symbol);
|
|
647
|
+
const callers = this.db.prepare(`SELECT DISTINCT r.caller_name FROM refs r
|
|
648
|
+
WHERE r.symbol_name = ? AND r.kind IN ('call', 'new') AND r.caller_name IS NOT NULL
|
|
649
|
+
LIMIT 30`).all(symbol);
|
|
650
|
+
for (let i = 0; i < callers.length; i++) {
|
|
651
|
+
const c = callers[i];
|
|
652
|
+
const isLast = i === callers.length - 1;
|
|
653
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
654
|
+
output.push(`${prefix}${connector}${c.caller_name}`);
|
|
655
|
+
const nextPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
656
|
+
this.textTreeCallers(c.caller_name, nextPrefix, visited, maxDepth, depth + 1, output);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
textTreeCallees(symbol, prefix, visited, maxDepth, depth, output) {
|
|
660
|
+
if (depth >= maxDepth || visited.has(symbol))
|
|
661
|
+
return;
|
|
662
|
+
visited.add(symbol);
|
|
663
|
+
const symRow = this.db.prepare(`SELECT s.id, s.file_id FROM symbols s WHERE s.name = ? AND s.kind IN ('function', 'method', 'arrow') LIMIT 1`).get(symbol);
|
|
664
|
+
if (!symRow)
|
|
665
|
+
return;
|
|
666
|
+
const callees = this.db.prepare(`SELECT DISTINCT r.symbol_name FROM refs r
|
|
667
|
+
WHERE r.file_id = ? AND r.caller_name = ? AND r.kind = 'call'
|
|
668
|
+
LIMIT 30`).all(symRow.file_id, symbol);
|
|
669
|
+
for (let i = 0; i < callees.length; i++) {
|
|
670
|
+
const c = callees[i];
|
|
671
|
+
const isLast = i === callees.length - 1;
|
|
672
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
673
|
+
output.push(`${prefix}${connector}${c.symbol_name}`);
|
|
674
|
+
const nextPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
675
|
+
this.textTreeCallees(c.symbol_name, nextPrefix, visited, maxDepth, depth + 1, output);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
textTreeFileDeps(filePath, prefix, visited, maxDepth, depth, output) {
|
|
679
|
+
if (depth >= maxDepth || visited.has(filePath))
|
|
680
|
+
return;
|
|
681
|
+
visited.add(filePath);
|
|
682
|
+
const fileRow = this.db.prepare('SELECT id FROM files WHERE path = ?').get(filePath);
|
|
683
|
+
if (!fileRow)
|
|
684
|
+
return;
|
|
685
|
+
const deps = this.db.prepare(`SELECT DISTINCT f.path FROM imports i
|
|
686
|
+
JOIN files f ON i.from_file_id = f.id
|
|
687
|
+
WHERE i.to_file_id = ?
|
|
688
|
+
LIMIT 30`).all(fileRow.id);
|
|
689
|
+
for (let i = 0; i < deps.length; i++) {
|
|
690
|
+
const d = deps[i];
|
|
691
|
+
const isLast = i === deps.length - 1;
|
|
692
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
693
|
+
output.push(`${prefix}${connector}${this.toRelative(d.path)}`);
|
|
694
|
+
const nextPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
695
|
+
this.textTreeFileDeps(d.path, nextPrefix, visited, maxDepth, depth + 1, output);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
graphGraphviz(options, maxDepth) {
|
|
699
|
+
const { symbol } = options;
|
|
700
|
+
const lines = [];
|
|
701
|
+
lines.push('```dot');
|
|
702
|
+
lines.push('digraph G {');
|
|
703
|
+
lines.push(' rankdir=LR;');
|
|
704
|
+
lines.push(' node [shape=box, style=rounded];');
|
|
705
|
+
const visited = new Set();
|
|
706
|
+
const edges = [];
|
|
707
|
+
if (symbol) {
|
|
708
|
+
this.collectCallerEdges(symbol, maxDepth, visited, edges, 'calls');
|
|
709
|
+
this.collectCalleeEdges(symbol, maxDepth, visited, edges, 'calls');
|
|
710
|
+
}
|
|
711
|
+
if (edges.length === 0) {
|
|
712
|
+
lines.push(' empty [label="No relationships found"];');
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
const nodeIds = new Set();
|
|
716
|
+
for (const [from, to] of edges) {
|
|
717
|
+
nodeIds.add(from);
|
|
718
|
+
nodeIds.add(to);
|
|
719
|
+
}
|
|
720
|
+
for (const id of nodeIds) {
|
|
721
|
+
lines.push(` "${id}" [label="${this.shorten(id)}"];`);
|
|
722
|
+
}
|
|
723
|
+
for (const [from, to, label] of edges) {
|
|
724
|
+
lines.push(` "${from}" -> "${to}" [label="${label}"];`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
lines.push('}');
|
|
728
|
+
lines.push('```');
|
|
729
|
+
return lines.join('\n');
|
|
730
|
+
}
|
|
731
|
+
// ── 图遍历辅助 ──────────────────────────────────────────────────────
|
|
732
|
+
collectCallerEdges(symbol, maxDepth, visited, edges, label, depth = 0) {
|
|
733
|
+
if (depth >= maxDepth || visited.has(symbol))
|
|
734
|
+
return;
|
|
735
|
+
visited.add(symbol);
|
|
736
|
+
const callers = this.db.prepare(`SELECT DISTINCT r.caller_name FROM refs r
|
|
737
|
+
WHERE r.symbol_name = ? AND r.kind IN ('call', 'new') AND r.caller_name IS NOT NULL
|
|
738
|
+
LIMIT 30`).all(symbol);
|
|
739
|
+
for (const c of callers) {
|
|
740
|
+
edges.push([c.caller_name, symbol, label]);
|
|
741
|
+
this.collectCallerEdges(c.caller_name, maxDepth, visited, edges, label, depth + 1);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
collectCalleeEdges(symbol, maxDepth, visited, edges, label, depth = 0) {
|
|
745
|
+
if (depth >= maxDepth || visited.has(symbol))
|
|
746
|
+
return;
|
|
747
|
+
visited.add(symbol);
|
|
748
|
+
const symRow = this.db.prepare(`SELECT s.id, s.file_id FROM symbols s WHERE s.name = ? AND s.kind IN ('function', 'method', 'arrow') LIMIT 1`).get(symbol);
|
|
749
|
+
if (!symRow)
|
|
750
|
+
return;
|
|
751
|
+
const callees = this.db.prepare(`SELECT DISTINCT r.symbol_name FROM refs r
|
|
752
|
+
WHERE r.file_id = ? AND r.caller_name = ? AND r.kind = 'call'
|
|
753
|
+
LIMIT 30`).all(symRow.file_id, symbol);
|
|
754
|
+
for (const c of callees) {
|
|
755
|
+
edges.push([symbol, c.symbol_name, label]);
|
|
756
|
+
this.collectCalleeEdges(c.symbol_name, maxDepth, visited, edges, label, depth + 1);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
collectFileDepsEdges(file, maxDepth, visited, edges) {
|
|
760
|
+
const resolved = this.resolvePath(file);
|
|
761
|
+
const queue = [{ path: resolved, depth: 0 }];
|
|
762
|
+
visited.add(resolved);
|
|
763
|
+
while (queue.length > 0) {
|
|
764
|
+
const { path: current, depth } = queue.shift();
|
|
765
|
+
if (depth >= maxDepth)
|
|
766
|
+
continue;
|
|
767
|
+
const fileRow = this.db.prepare('SELECT id FROM files WHERE path = ?').get(current);
|
|
768
|
+
if (!fileRow)
|
|
769
|
+
continue;
|
|
770
|
+
const deps = this.db.prepare(`SELECT DISTINCT f.path FROM imports i
|
|
771
|
+
JOIN files f ON i.from_file_id = f.id
|
|
772
|
+
WHERE i.to_file_id = ?
|
|
773
|
+
LIMIT 30`).all(fileRow.id);
|
|
774
|
+
for (const dep of deps) {
|
|
775
|
+
edges.push([this.toRelative(dep.path), this.toRelative(current), 'imports']);
|
|
776
|
+
if (!visited.has(dep.path)) {
|
|
777
|
+
visited.add(dep.path);
|
|
778
|
+
queue.push({ path: dep.path, depth: depth + 1 });
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
// ── 文件扫描 ────────────────────────────────────────────────────────
|
|
784
|
+
/**
|
|
785
|
+
* 扫描项目目录,收集需要解析的源文件。
|
|
786
|
+
* @param dirs 可选:只扫描这些子目录(相对路径)。undefined = 全项目扫描。
|
|
787
|
+
*/
|
|
788
|
+
async scanFiles(rootDir, dirs) {
|
|
789
|
+
const exts = this.parserRegistry?.getAllExtensions() ?? ['.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs'];
|
|
790
|
+
const extSet = new Set(exts);
|
|
791
|
+
const files = [];
|
|
792
|
+
const scanDir = async (d) => {
|
|
793
|
+
try {
|
|
794
|
+
const entries = await fs.readdir(d, { withFileTypes: true });
|
|
795
|
+
for (const entry of entries) {
|
|
796
|
+
const fullPath = path.join(d, entry.name);
|
|
797
|
+
if (entry.isDirectory()) {
|
|
798
|
+
if (['node_modules', 'dist', '.git', 'coverage', '.next', 'build', 'target', '__pycache__', '.venv', 'venv', '.agent'].includes(entry.name))
|
|
799
|
+
continue;
|
|
800
|
+
if (entry.name.startsWith('.'))
|
|
801
|
+
continue;
|
|
802
|
+
await scanDir(fullPath);
|
|
803
|
+
}
|
|
804
|
+
else if (extSet.has(path.extname(entry.name).toLowerCase())) {
|
|
805
|
+
// 跳过 .d.ts 和 .min.js
|
|
806
|
+
if (!entry.name.endsWith('.d.ts') && !entry.name.endsWith('.min.js') && !entry.name.endsWith('.min.css')) {
|
|
807
|
+
files.push(fullPath);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
catch {
|
|
813
|
+
// skip
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
if (dirs && dirs.length > 0) {
|
|
817
|
+
// 目录过滤模式:只扫描指定的子目录
|
|
818
|
+
for (const d of dirs) {
|
|
819
|
+
const absDir = path.resolve(rootDir, d);
|
|
820
|
+
await scanDir(absDir);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
// 全项目扫描
|
|
825
|
+
await scanDir(rootDir);
|
|
826
|
+
}
|
|
827
|
+
return files;
|
|
828
|
+
}
|
|
829
|
+
/** 删除当前项目或指定项目的交叉引用数据库 */
|
|
830
|
+
deleteDatabase(project) {
|
|
831
|
+
let targetPath;
|
|
832
|
+
if (project) {
|
|
833
|
+
// 指定项目:支持绝对路径、相对路径、projectKey
|
|
834
|
+
if (path.isAbsolute(project)) {
|
|
835
|
+
const projectKey = toProjectKey(project);
|
|
836
|
+
targetPath = path.join(os.homedir(), '.agent', 'cache', `xref-${projectKey}.sqlite`);
|
|
837
|
+
}
|
|
838
|
+
else if (project.includes('/') || project.includes('\\')) {
|
|
839
|
+
// 相对路径 → 转绝对
|
|
840
|
+
const projectKey = toProjectKey(path.resolve(project));
|
|
841
|
+
targetPath = path.join(os.homedir(), '.agent', 'cache', `xref-${projectKey}.sqlite`);
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
// 直接传的 projectKey
|
|
845
|
+
targetPath = path.join(os.homedir(), '.agent', 'cache', `xref-${project}.sqlite`);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
// 当前项目
|
|
850
|
+
if (!this.dbPath)
|
|
851
|
+
return 'Error: no xref database is currently open.';
|
|
852
|
+
targetPath = this.dbPath;
|
|
853
|
+
}
|
|
854
|
+
// 关闭当前连接(如果删除的是当前项目)
|
|
855
|
+
if (targetPath === this.dbPath) {
|
|
856
|
+
this.db?.close();
|
|
857
|
+
this.db = null;
|
|
858
|
+
this.dbPath = '';
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
fs.unlink(targetPath);
|
|
862
|
+
// macOS/Linux 的 WAL 也会产生 -wal 和 -shm 文件
|
|
863
|
+
try {
|
|
864
|
+
fs.unlink(targetPath + '-wal');
|
|
865
|
+
}
|
|
866
|
+
catch { }
|
|
867
|
+
try {
|
|
868
|
+
fs.unlink(targetPath + '-shm');
|
|
869
|
+
}
|
|
870
|
+
catch { }
|
|
871
|
+
return `✅ Xref database deleted: ${targetPath}`;
|
|
872
|
+
}
|
|
873
|
+
catch {
|
|
874
|
+
return `Database not found or already deleted: ${targetPath}`;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
// ── 路径工具 ────────────────────────────────────────────────────────
|
|
878
|
+
resolvePath(file) {
|
|
879
|
+
if (path.isAbsolute(file))
|
|
880
|
+
return file.replace(/\\/g, '/');
|
|
881
|
+
return path.resolve(this.rootDir, file).replace(/\\/g, '/');
|
|
882
|
+
}
|
|
883
|
+
toRelative(absPath) {
|
|
884
|
+
const normalized = absPath.replace(/\\/g, '/');
|
|
885
|
+
const rootNormalized = this.rootDir.replace(/\\/g, '/');
|
|
886
|
+
if (normalized.startsWith(rootNormalized + '/')) {
|
|
887
|
+
return normalized.slice(rootNormalized.length + 1);
|
|
888
|
+
}
|
|
889
|
+
if (normalized.startsWith(rootNormalized)) {
|
|
890
|
+
return normalized.slice(rootNormalized.length);
|
|
891
|
+
}
|
|
892
|
+
return normalized;
|
|
893
|
+
}
|
|
894
|
+
async resolveImportPath(modulePath, fromFile) {
|
|
895
|
+
if (!modulePath.startsWith('.'))
|
|
896
|
+
return null; // 跳过非相对路径(npm 包等)
|
|
897
|
+
const dir = path.dirname(fromFile);
|
|
898
|
+
const resolved = path.resolve(dir, modulePath);
|
|
899
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs', '.c', '.h', '.cpp', '.hpp', '.java'];
|
|
900
|
+
const resolvedNorm = resolved.replace(/\\/g, '/');
|
|
901
|
+
// 精确匹配
|
|
902
|
+
for (const ext of extensions) {
|
|
903
|
+
const fullPath = resolvedNorm + ext;
|
|
904
|
+
try {
|
|
905
|
+
await fs.access(fullPath);
|
|
906
|
+
return fullPath;
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
// continue
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
// index 文件
|
|
913
|
+
for (const ext of extensions) {
|
|
914
|
+
const indexPath = `${resolvedNorm}/index${ext}`;
|
|
915
|
+
try {
|
|
916
|
+
await fs.access(indexPath);
|
|
917
|
+
return indexPath;
|
|
918
|
+
}
|
|
919
|
+
catch {
|
|
920
|
+
// continue
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return null;
|
|
924
|
+
}
|
|
925
|
+
guessLanguage(ext) {
|
|
926
|
+
const map = {
|
|
927
|
+
'.ts': 'typescript', '.tsx': 'typescript',
|
|
928
|
+
'.js': 'javascript', '.jsx': 'javascript',
|
|
929
|
+
'.py': 'python', '.go': 'go', '.rs': 'rust',
|
|
930
|
+
'.c': 'c', '.h': 'c', '.cpp': 'cpp', '.hpp': 'cpp',
|
|
931
|
+
'.java': 'java', '.kt': 'kotlin', '.swift': 'swift',
|
|
932
|
+
};
|
|
933
|
+
return map[ext] ?? 'unknown';
|
|
934
|
+
}
|
|
935
|
+
shorten(text) {
|
|
936
|
+
if (text.length <= 30)
|
|
937
|
+
return text;
|
|
938
|
+
return text.slice(0, 13) + '...' + text.slice(-14);
|
|
939
|
+
}
|
|
940
|
+
/** 关闭数据库连接 */
|
|
941
|
+
close() {
|
|
942
|
+
if (this.db) {
|
|
943
|
+
this.db.close();
|
|
944
|
+
this.db = null;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
//# sourceMappingURL=manager.js.map
|