homegraph 1.1.3 → 1.4.1
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/README.md +43 -18
- package/dist/arkts/ohos-api-index.d.ts +15 -0
- package/dist/arkts/ohos-api-index.d.ts.map +1 -0
- package/dist/arkts/ohos-api-index.js +190 -0
- package/dist/arkts/ohos-api-index.js.map +1 -0
- package/dist/arkts/ohos-sdk-input.d.ts +36 -0
- package/dist/arkts/ohos-sdk-input.d.ts.map +1 -0
- package/dist/arkts/ohos-sdk-input.js +214 -0
- package/dist/arkts/ohos-sdk-input.js.map +1 -0
- package/dist/bin/command-supervision.d.ts +12 -0
- package/dist/bin/command-supervision.d.ts.map +1 -0
- package/dist/bin/command-supervision.js +86 -0
- package/dist/bin/command-supervision.js.map +1 -0
- package/dist/bin/homegraph.d.ts +1 -1
- package/dist/bin/homegraph.js +709 -168
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +85 -0
- package/dist/context/index.js.map +1 -1
- package/dist/db/index.d.ts +84 -5
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +217 -14
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +58 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +131 -2
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +473 -9
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +52 -1
- package/dist/db/wal-valve.d.ts +106 -0
- package/dist/db/wal-valve.d.ts.map +1 -0
- package/dist/db/wal-valve.js +208 -0
- package/dist/db/wal-valve.js.map +1 -0
- package/dist/directory.d.ts +36 -0
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +274 -0
- package/dist/directory.js.map +1 -1
- package/dist/extraction/arkts-batch-worker.d.ts +2 -0
- package/dist/extraction/arkts-batch-worker.d.ts.map +1 -0
- package/dist/extraction/arkts-batch-worker.js +28 -0
- package/dist/extraction/arkts-batch-worker.js.map +1 -0
- package/dist/extraction/cfml-extractor.d.ts +107 -0
- package/dist/extraction/cfml-extractor.d.ts.map +1 -0
- package/dist/extraction/cfml-extractor.js +494 -0
- package/dist/extraction/cfml-extractor.js.map +1 -0
- package/dist/extraction/context.d.ts +11 -0
- package/dist/extraction/context.d.ts.map +1 -1
- package/dist/extraction/context.js +20 -0
- package/dist/extraction/context.js.map +1 -1
- package/dist/extraction/grammars.d.ts +25 -1
- package/dist/extraction/grammars.d.ts.map +1 -1
- package/dist/extraction/grammars.js +177 -24
- package/dist/extraction/grammars.js.map +1 -1
- package/dist/extraction/index.d.ts +55 -2
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +923 -381
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts-state-decorators.d.ts +13 -0
- package/dist/extraction/languages/arkts-state-decorators.d.ts.map +1 -0
- package/dist/extraction/languages/arkts-state-decorators.js +26 -0
- package/dist/extraction/languages/arkts-state-decorators.js.map +1 -0
- package/dist/extraction/languages/arkts-viewtree.d.ts +4 -2
- package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -1
- package/dist/extraction/languages/arkts-viewtree.js +21 -6
- package/dist/extraction/languages/arkts-viewtree.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +98 -2
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +1290 -61
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/languages/c-cpp.d.ts +98 -0
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
- package/dist/extraction/languages/c-cpp.js +563 -1
- package/dist/extraction/languages/c-cpp.js.map +1 -1
- package/dist/extraction/languages/cfquery.d.ts +12 -0
- package/dist/extraction/languages/cfquery.d.ts.map +1 -0
- package/dist/extraction/languages/cfquery.js +28 -0
- package/dist/extraction/languages/cfquery.js.map +1 -0
- package/dist/extraction/languages/cfscript.d.ts +3 -0
- package/dist/extraction/languages/cfscript.d.ts.map +1 -0
- package/dist/extraction/languages/cfscript.js +73 -0
- package/dist/extraction/languages/cfscript.js.map +1 -0
- package/dist/extraction/languages/cobol.d.ts +33 -0
- package/dist/extraction/languages/cobol.d.ts.map +1 -0
- package/dist/extraction/languages/cobol.js +499 -0
- package/dist/extraction/languages/cobol.js.map +1 -0
- package/dist/extraction/languages/erlang.d.ts +3 -0
- package/dist/extraction/languages/erlang.d.ts.map +1 -0
- package/dist/extraction/languages/erlang.js +350 -0
- package/dist/extraction/languages/erlang.js.map +1 -0
- package/dist/extraction/languages/index.d.ts.map +1 -1
- package/dist/extraction/languages/index.js +16 -0
- package/dist/extraction/languages/index.js.map +1 -1
- package/dist/extraction/languages/nix.d.ts +3 -0
- package/dist/extraction/languages/nix.d.ts.map +1 -0
- package/dist/extraction/languages/nix.js +294 -0
- package/dist/extraction/languages/nix.js.map +1 -0
- package/dist/extraction/languages/ohos-api-consumer.d.ts +34 -0
- package/dist/extraction/languages/ohos-api-consumer.d.ts.map +1 -0
- package/dist/extraction/languages/ohos-api-consumer.js +283 -0
- package/dist/extraction/languages/ohos-api-consumer.js.map +1 -0
- package/dist/extraction/languages/solidity.d.ts +3 -0
- package/dist/extraction/languages/solidity.d.ts.map +1 -0
- package/dist/extraction/languages/solidity.js +293 -0
- package/dist/extraction/languages/solidity.js.map +1 -0
- package/dist/extraction/languages/terraform.d.ts +3 -0
- package/dist/extraction/languages/terraform.d.ts.map +1 -0
- package/dist/extraction/languages/terraform.js +641 -0
- package/dist/extraction/languages/terraform.js.map +1 -0
- package/dist/extraction/languages/vbnet.d.ts +11 -0
- package/dist/extraction/languages/vbnet.d.ts.map +1 -0
- package/dist/extraction/languages/vbnet.js +141 -0
- package/dist/extraction/languages/vbnet.js.map +1 -0
- package/dist/extraction/mybatis-extractor.d.ts +30 -10
- package/dist/extraction/mybatis-extractor.d.ts.map +1 -1
- package/dist/extraction/mybatis-extractor.js +140 -38
- package/dist/extraction/mybatis-extractor.js.map +1 -1
- package/dist/extraction/parse-pool.d.ts +152 -0
- package/dist/extraction/parse-pool.d.ts.map +1 -0
- package/dist/extraction/parse-pool.js +387 -0
- package/dist/extraction/parse-pool.js.map +1 -0
- package/dist/extraction/parse-worker.js +9 -2
- package/dist/extraction/parse-worker.js.map +1 -1
- package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
- package/dist/extraction/tree-sitter-helpers.js +1 -0
- package/dist/extraction/tree-sitter-helpers.js.map +1 -1
- package/dist/extraction/tree-sitter-types.d.ts +20 -1
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.d.ts +59 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +952 -37
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
- package/dist/graph/traversal.d.ts.map +1 -1
- package/dist/graph/traversal.js +77 -18
- package/dist/graph/traversal.js.map +1 -1
- package/dist/index.d.ts +85 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +426 -9
- package/dist/index.js.map +1 -1
- package/dist/installer/index.d.ts +42 -0
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +97 -2
- package/dist/installer/index.js.map +1 -1
- package/dist/mcp/daemon-paths.d.ts +30 -3
- package/dist/mcp/daemon-paths.d.ts.map +1 -1
- package/dist/mcp/daemon-paths.js +50 -10
- package/dist/mcp/daemon-paths.js.map +1 -1
- package/dist/mcp/daemon-registry.d.ts.map +1 -1
- package/dist/mcp/daemon-registry.js +7 -3
- package/dist/mcp/daemon-registry.js.map +1 -1
- package/dist/mcp/daemon.d.ts +73 -3
- package/dist/mcp/daemon.d.ts.map +1 -1
- package/dist/mcp/daemon.js +258 -40
- package/dist/mcp/daemon.js.map +1 -1
- package/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
- package/dist/mcp/dynamic-boundaries.js +2 -1
- package/dist/mcp/dynamic-boundaries.js.map +1 -1
- package/dist/mcp/early-ppid.d.ts +26 -0
- package/dist/mcp/early-ppid.d.ts.map +1 -0
- package/dist/mcp/early-ppid.js +29 -0
- package/dist/mcp/early-ppid.js.map +1 -0
- package/dist/mcp/engine.d.ts +16 -0
- package/dist/mcp/engine.d.ts.map +1 -1
- package/dist/mcp/engine.js +100 -2
- package/dist/mcp/engine.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +66 -49
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/liveness-watchdog.d.ts +18 -1
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
- package/dist/mcp/liveness-watchdog.js +73 -13
- package/dist/mcp/liveness-watchdog.js.map +1 -1
- package/dist/mcp/memory-budget.d.ts +30 -0
- package/dist/mcp/memory-budget.d.ts.map +1 -0
- package/dist/mcp/memory-budget.js +123 -0
- package/dist/mcp/memory-budget.js.map +1 -0
- package/dist/mcp/ppid-watchdog.d.ts +18 -0
- package/dist/mcp/ppid-watchdog.d.ts.map +1 -1
- package/dist/mcp/ppid-watchdog.js +37 -0
- package/dist/mcp/ppid-watchdog.js.map +1 -1
- package/dist/mcp/proxy.d.ts.map +1 -1
- package/dist/mcp/proxy.js +88 -4
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/query-cache.d.ts +56 -0
- package/dist/mcp/query-cache.d.ts.map +1 -0
- package/dist/mcp/query-cache.js +261 -0
- package/dist/mcp/query-cache.js.map +1 -0
- package/dist/mcp/query-pool.d.ts +128 -0
- package/dist/mcp/query-pool.d.ts.map +1 -0
- package/dist/mcp/query-pool.js +374 -0
- package/dist/mcp/query-pool.js.map +1 -0
- package/dist/mcp/query-worker.d.ts +24 -0
- package/dist/mcp/query-worker.d.ts.map +1 -0
- package/dist/mcp/query-worker.js +87 -0
- package/dist/mcp/query-worker.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +4 -29
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +39 -86
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/session.d.ts +14 -0
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +28 -1
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/startup-handshake.d.ts +44 -0
- package/dist/mcp/startup-handshake.d.ts.map +1 -0
- package/dist/mcp/startup-handshake.js +73 -0
- package/dist/mcp/startup-handshake.js.map +1 -0
- package/dist/mcp/tools.d.ts +179 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +3027 -192
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/transport.d.ts.map +1 -1
- package/dist/mcp/transport.js +18 -0
- package/dist/mcp/transport.js.map +1 -1
- package/dist/project-config.d.ts +58 -0
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +141 -2
- package/dist/project-config.js.map +1 -1
- package/dist/reasoning/login.js +1 -1
- package/dist/reasoning/login.js.map +1 -1
- package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -29
- package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
- package/dist/resolution/c-fnptr-synthesizer.js +866 -148
- package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +9 -1
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +1232 -183
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/cooperative-yield.d.ts +32 -0
- package/dist/resolution/cooperative-yield.d.ts.map +1 -0
- package/dist/resolution/cooperative-yield.js +42 -0
- package/dist/resolution/cooperative-yield.js.map +1 -0
- package/dist/resolution/frameworks/cics.d.ts +20 -0
- package/dist/resolution/frameworks/cics.d.ts.map +1 -0
- package/dist/resolution/frameworks/cics.js +90 -0
- package/dist/resolution/frameworks/cics.js.map +1 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/dist/resolution/frameworks/index.js +3 -0
- package/dist/resolution/frameworks/index.js.map +1 -1
- package/dist/resolution/frameworks/java.d.ts.map +1 -1
- package/dist/resolution/frameworks/java.js +14 -6
- package/dist/resolution/frameworks/java.js.map +1 -1
- package/dist/resolution/frameworks/terraform.d.ts +38 -0
- package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
- package/dist/resolution/frameworks/terraform.js +277 -0
- package/dist/resolution/frameworks/terraform.js.map +1 -0
- package/dist/resolution/goframe-synthesizer.d.ts +2 -1
- package/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
- package/dist/resolution/goframe-synthesizer.js +8 -3
- package/dist/resolution/goframe-synthesizer.js.map +1 -1
- package/dist/resolution/import-resolver.d.ts +7 -0
- package/dist/resolution/import-resolver.d.ts.map +1 -1
- package/dist/resolution/import-resolver.js +129 -4
- package/dist/resolution/import-resolver.js.map +1 -1
- package/dist/resolution/index.d.ts +65 -3
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +477 -59
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/name-matcher.d.ts +22 -3
- package/dist/resolution/name-matcher.d.ts.map +1 -1
- package/dist/resolution/name-matcher.js +657 -39
- package/dist/resolution/name-matcher.js.map +1 -1
- package/dist/resolution/strip-comments.d.ts +1 -1
- package/dist/resolution/strip-comments.d.ts.map +1 -1
- package/dist/resolution/strip-comments.js +49 -0
- package/dist/resolution/strip-comments.js.map +1 -1
- package/dist/resolution/types.d.ts +29 -0
- package/dist/resolution/types.d.ts.map +1 -1
- package/dist/resolution/workspace-packages.d.ts +10 -0
- package/dist/resolution/workspace-packages.d.ts.map +1 -1
- package/dist/resolution/workspace-packages.js +142 -4
- package/dist/resolution/workspace-packages.js.map +1 -1
- package/dist/search/identifier-segments.d.ts +60 -0
- package/dist/search/identifier-segments.d.ts.map +1 -0
- package/dist/search/identifier-segments.js +176 -0
- package/dist/search/identifier-segments.js.map +1 -0
- package/dist/search/query-utils.d.ts +201 -0
- package/dist/search/query-utils.d.ts.map +1 -1
- package/dist/search/query-utils.js +919 -2
- package/dist/search/query-utils.js.map +1 -1
- package/dist/spec/config.d.ts +7 -11
- package/dist/spec/config.d.ts.map +1 -1
- package/dist/spec/config.js +77 -48
- package/dist/spec/config.js.map +1 -1
- package/dist/spec/db/fragment-node.d.ts +1 -0
- package/dist/spec/db/fragment-node.d.ts.map +1 -1
- package/dist/spec/db/fragment-node.js +8 -0
- package/dist/spec/db/fragment-node.js.map +1 -1
- package/dist/spec/db/fts.d.ts +14 -0
- package/dist/spec/db/fts.d.ts.map +1 -1
- package/dist/spec/db/fts.js +43 -4
- package/dist/spec/db/fts.js.map +1 -1
- package/dist/spec/db/schema.d.ts +6 -3
- package/dist/spec/db/schema.d.ts.map +1 -1
- package/dist/spec/db/schema.js +36 -4
- package/dist/spec/db/schema.js.map +1 -1
- package/dist/spec/db/schema.sql +11 -1
- package/dist/spec/evolve/logic-checker.d.ts +2 -2
- package/dist/spec/evolve/logic-checker.d.ts.map +1 -1
- package/dist/spec/evolve/logic-checker.js +3 -27
- package/dist/spec/evolve/logic-checker.js.map +1 -1
- package/dist/spec/evolve/pipeline.d.ts +21 -19
- package/dist/spec/evolve/pipeline.d.ts.map +1 -1
- package/dist/spec/evolve/pipeline.js +284 -31
- package/dist/spec/evolve/pipeline.js.map +1 -1
- package/dist/spec/evolve/spec-rewriter.d.ts +2 -2
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
- package/dist/spec/evolve/spec-rewriter.js +7 -31
- package/dist/spec/evolve/spec-rewriter.js.map +1 -1
- package/dist/spec/graph/queries.d.ts +97 -0
- package/dist/spec/graph/queries.d.ts.map +1 -1
- package/dist/spec/graph/queries.js +225 -0
- package/dist/spec/graph/queries.js.map +1 -1
- package/dist/spec/llm/client.d.ts +29 -0
- package/dist/spec/llm/client.d.ts.map +1 -0
- package/dist/spec/llm/client.js +123 -0
- package/dist/spec/llm/client.js.map +1 -0
- package/dist/spec/llm/index.d.ts +3 -0
- package/dist/spec/llm/index.d.ts.map +1 -0
- package/dist/spec/llm/index.js +11 -0
- package/dist/spec/llm/index.js.map +1 -0
- package/dist/spec/llm/prompts.d.ts +13 -0
- package/dist/spec/llm/prompts.d.ts.map +1 -0
- package/dist/spec/llm/prompts.js +75 -0
- package/dist/spec/llm/prompts.js.map +1 -0
- package/dist/spec/mining/git-scanner.d.ts +12 -0
- package/dist/spec/mining/git-scanner.d.ts.map +1 -1
- package/dist/spec/mining/git-scanner.js +41 -0
- package/dist/spec/mining/git-scanner.js.map +1 -1
- package/dist/spec/mining/pipeline.d.ts.map +1 -1
- package/dist/spec/mining/pipeline.js +14 -1
- package/dist/spec/mining/pipeline.js.map +1 -1
- package/dist/spec/utils.d.ts +2 -1
- package/dist/spec/utils.d.ts.map +1 -1
- package/dist/spec/utils.js +3 -1
- package/dist/spec/utils.js.map +1 -1
- package/dist/sync/git-hooks.d.ts.map +1 -1
- package/dist/sync/git-hooks.js +2 -0
- package/dist/sync/git-hooks.js.map +1 -1
- package/dist/sync/watcher.d.ts +10 -5
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +51 -14
- package/dist/sync/watcher.js.map +1 -1
- package/dist/sync/worktree.d.ts +9 -0
- package/dist/sync/worktree.d.ts.map +1 -1
- package/dist/sync/worktree.js +45 -0
- package/dist/sync/worktree.js.map +1 -1
- package/dist/types.d.ts +25 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/shimmer-progress.d.ts +2 -0
- package/dist/ui/shimmer-progress.d.ts.map +1 -1
- package/dist/ui/shimmer-progress.js +19 -2
- package/dist/ui/shimmer-progress.js.map +1 -1
- package/dist/upgrade/index.d.ts +32 -0
- package/dist/upgrade/index.d.ts.map +1 -1
- package/dist/upgrade/index.js +163 -15
- package/dist/upgrade/index.js.map +1 -1
- package/dist/upgrade/remove-binary.d.ts +87 -0
- package/dist/upgrade/remove-binary.d.ts.map +1 -0
- package/dist/upgrade/remove-binary.js +289 -0
- package/dist/upgrade/remove-binary.js.map +1 -0
- package/dist/upgrade/update-check.d.ts +92 -0
- package/dist/upgrade/update-check.d.ts.map +1 -0
- package/dist/upgrade/update-check.js +258 -0
- package/dist/upgrade/update-check.js.map +1 -0
- package/package.json +4 -4
- package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
- package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
- package/scripts/agent-eval/run-all.sh +6 -0
- package/scripts/build-bundle.sh +5 -0
- package/scripts/exp_boundary_eval/README.md +247 -0
- package/scripts/exp_boundary_eval/_test_mcp_chain.py +78 -0
- package/scripts/exp_boundary_eval/_test_stdin.py +8 -0
- package/scripts/exp_boundary_eval/_utils.py +1116 -0
- package/scripts/exp_boundary_eval/analyze.py +1313 -0
- package/scripts/exp_boundary_eval/deveco_arm.py +519 -0
- package/scripts/exp_boundary_eval/run_all.py +378 -0
- package/scripts/exp_boundary_eval/run_one.py +165 -0
- package/scripts/exp_boundary_eval/run_session.py +158 -0
- package/scripts/exp_boundary_eval/setup.py +120 -0
- package/scripts/exp_boundary_eval/win_mcp_launcher.py +73 -0
- package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +36 -0
- package/scripts/exp_boundary_eval/win_node_launcher.py +24 -0
- package/scripts/npm-shim.js +32 -3
- package/scripts/ohos-sdk-publish.mjs +133 -0
- package/scripts/qa_eval/README.md +0 -407
- package/scripts/qa_eval/_test_deveco_probe.py +0 -41
- package/scripts/qa_eval/agent_runner.py +0 -526
- package/scripts/qa_eval/data/.gitignore +0 -4
- package/scripts/qa_eval/data/test-set.jsonl +0 -2
- package/scripts/qa_eval/eval_metrics.py +0 -274
- package/scripts/qa_eval/external_agent.py +0 -976
- package/scripts/qa_eval/llm_config.py +0 -92
- package/scripts/qa_eval/memory_monitor.py +0 -132
- package/scripts/qa_eval/my_answer_accuracy.py +0 -187
- package/scripts/qa_eval/requirements.txt +0 -2
- package/scripts/qa_eval/run_pipeline.py +0 -804
- package/scripts/qa_eval/stats_efficiency.py +0 -279
- package/scripts/qa_eval/stats_scores.py +0 -207
package/dist/bin/homegraph.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* homegraph callers <symbol> Find what calls a function/method
|
|
21
21
|
* homegraph callees <symbol> Find what a function/method calls
|
|
22
22
|
* homegraph impact <symbol> Analyze what code is affected by changing a symbol
|
|
23
|
-
* homegraph
|
|
23
|
+
* homegraph index-api <tools> Build OHOS API db from command-line-tools
|
|
24
24
|
* homegraph upgrade [version] Update HomeGraph to the latest release
|
|
25
25
|
*/
|
|
26
26
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -60,14 +60,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
60
60
|
const commander_1 = require("commander");
|
|
61
61
|
const path = __importStar(require("path"));
|
|
62
62
|
const fs = __importStar(require("fs"));
|
|
63
|
-
const utils_1 = require("../spec/utils");
|
|
64
63
|
const directory_1 = require("../directory");
|
|
64
|
+
const utils_1 = require("../spec/utils");
|
|
65
65
|
const worktree_1 = require("../sync/worktree");
|
|
66
66
|
const shimmer_progress_1 = require("../ui/shimmer-progress");
|
|
67
67
|
const glyphs_1 = require("../ui/glyphs");
|
|
68
68
|
const node_version_check_1 = require("./node-version-check");
|
|
69
69
|
const fatal_handler_1 = require("./fatal-handler");
|
|
70
70
|
const wasm_runtime_flags_1 = require("../extraction/wasm-runtime-flags");
|
|
71
|
+
const command_supervision_1 = require("./command-supervision");
|
|
71
72
|
const extraction_version_1 = require("../extraction/extraction-version");
|
|
72
73
|
const telemetry_1 = require("../telemetry");
|
|
73
74
|
// Lazy-load heavy modules (HomeGraph, runInstaller) to keep CLI startup fast.
|
|
@@ -416,6 +417,19 @@ function main() {
|
|
|
416
417
|
fs.unlinkSync(logPath);
|
|
417
418
|
}
|
|
418
419
|
}
|
|
420
|
+
const ohosApiWarnings = result.errors.filter((e) => e.severity === 'warning' && e.code?.startsWith('ohos_api_'));
|
|
421
|
+
if (ohosApiWarnings.length > 0) {
|
|
422
|
+
clack.log.warn('OHOS SDK API db not attached — project index is complete; explore uses project code only.');
|
|
423
|
+
clack.note(ohosApiWarnings.map((e) => e.message).join('\n'), 'OHOS API notice');
|
|
424
|
+
}
|
|
425
|
+
const arktsDegraded = result.errors.filter((e) => e.code === 'arkts_degraded');
|
|
426
|
+
if (arktsDegraded.length > 0) {
|
|
427
|
+
clack.log.warn('ArkTS index quality notice — see details below.');
|
|
428
|
+
clack.note(`${arktsDegraded[0].message}\n\n` +
|
|
429
|
+
'Large ArkTS repos on Windows use an isolated indexer with enableMethodBodyBuild=true.\n' +
|
|
430
|
+
'If indexing failed, set HOMEGRAPH_ARKTS_STACK_SIZES_KB=65536,131072,262144 and retry:\n' +
|
|
431
|
+
' node --stack-size=65536 dist/bin/homegraph.js index <path>', 'ArkTS quality notice');
|
|
432
|
+
}
|
|
419
433
|
}
|
|
420
434
|
/**
|
|
421
435
|
* Write detailed error log to .homegraph/errors.log
|
|
@@ -512,20 +526,27 @@ function main() {
|
|
|
512
526
|
// Indexing runs by default now. The legacy -i/--index flag is still
|
|
513
527
|
// accepted (so existing muscle memory and scripts don't break) but is a
|
|
514
528
|
// no-op — initializing always builds the initial index.
|
|
529
|
+
// Supervise the index: self-terminate if orphaned or wedged (#999).
|
|
530
|
+
const supervision = (0, command_supervision_1.installCommandSupervision)('init');
|
|
515
531
|
let result;
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
532
|
+
try {
|
|
533
|
+
if (options.verbose) {
|
|
534
|
+
result = await cg.indexAll({
|
|
535
|
+
onProgress: createVerboseProgress(),
|
|
536
|
+
verbose: true,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
|
|
541
|
+
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
542
|
+
result = await cg.indexAll({
|
|
543
|
+
onProgress: progress.onProgress,
|
|
544
|
+
});
|
|
545
|
+
await progress.stop();
|
|
546
|
+
}
|
|
521
547
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
525
|
-
result = await cg.indexAll({
|
|
526
|
-
onProgress: progress.onProgress,
|
|
527
|
-
});
|
|
528
|
-
await progress.stop();
|
|
548
|
+
finally {
|
|
549
|
+
supervision.stop();
|
|
529
550
|
}
|
|
530
551
|
printIndexResult(clack, result, projectPath);
|
|
531
552
|
await recordIndexTelemetry(cg, result);
|
|
@@ -620,47 +641,56 @@ function main() {
|
|
|
620
641
|
process.exit(1);
|
|
621
642
|
}
|
|
622
643
|
const { default: HomeGraph } = await loadHomeGraph();
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
644
|
+
// `index` is a FULL re-index — identical to a fresh `init`. RECREATE the
|
|
645
|
+
// database from scratch (discard .homegraph/homegraph.db + its WAL) rather
|
|
646
|
+
// than opening the old graph and DELETE-ing every row. The clear-then-index
|
|
647
|
+
// approach reported "0 nodes" without the clear (#874); the recreate keeps
|
|
648
|
+
// that fixed AND avoids the failure mode where, on a large or pre-fix
|
|
649
|
+
// poisoned index, the per-row FTS delete churn wedged the main thread long
|
|
650
|
+
// enough to trip the liveness watchdog before scanning even began (#1067).
|
|
651
|
+
// recreate() hands back a fresh, empty instance — no clear() needed. For
|
|
652
|
+
// fast incremental updates use `sync`.
|
|
653
|
+
const cg = await HomeGraph.recreate(projectPath);
|
|
654
|
+
// Supervise the indexer: self-terminate if orphaned (parent shim killed)
|
|
655
|
+
// or if the main thread wedges — neither was guarded on this path (#999).
|
|
656
|
+
const supervision = (0, command_supervision_1.installCommandSupervision)('index');
|
|
657
|
+
try {
|
|
658
|
+
if (options.quiet) {
|
|
659
|
+
// Quiet mode: no UI, just run against the freshly-recreated graph.
|
|
660
|
+
const result = await cg.indexAll();
|
|
661
|
+
if (!result.success)
|
|
662
|
+
process.exit(1);
|
|
663
|
+
cg.destroy();
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
const clack = await importESM('@clack/prompts');
|
|
667
|
+
clack.intro('Indexing project');
|
|
668
|
+
let result;
|
|
669
|
+
if (options.verbose) {
|
|
670
|
+
result = await cg.indexAll({
|
|
671
|
+
onProgress: createVerboseProgress(),
|
|
672
|
+
verbose: true,
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
|
|
677
|
+
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
678
|
+
result = await cg.indexAll({
|
|
679
|
+
onProgress: progress.onProgress,
|
|
680
|
+
});
|
|
681
|
+
await progress.stop();
|
|
682
|
+
}
|
|
683
|
+
printIndexResult(clack, result, projectPath);
|
|
684
|
+
await recordIndexTelemetry(cg, result);
|
|
685
|
+
if (!result.success) {
|
|
630
686
|
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
clack.outro('Done');
|
|
631
689
|
cg.destroy();
|
|
632
|
-
return;
|
|
633
690
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
// `index` is a FULL re-index: clear the existing graph and rebuild it from
|
|
637
|
-
// scratch so the result is identical to a fresh `init`. Without the clear,
|
|
638
|
-
// indexAll() skips every unchanged file by its content hash and reports
|
|
639
|
-
// "0 nodes, 0 edges" against the already-populated graph — which reads as
|
|
640
|
-
// "index wiped my index" (#874). For fast incremental updates use `sync`.
|
|
641
|
-
cg.clear();
|
|
642
|
-
let result;
|
|
643
|
-
if (options.verbose) {
|
|
644
|
-
result = await cg.indexAll({
|
|
645
|
-
onProgress: createVerboseProgress(),
|
|
646
|
-
verbose: true,
|
|
647
|
-
});
|
|
691
|
+
finally {
|
|
692
|
+
supervision.stop();
|
|
648
693
|
}
|
|
649
|
-
else {
|
|
650
|
-
process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
|
|
651
|
-
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
652
|
-
result = await cg.indexAll({
|
|
653
|
-
onProgress: progress.onProgress,
|
|
654
|
-
});
|
|
655
|
-
await progress.stop();
|
|
656
|
-
}
|
|
657
|
-
printIndexResult(clack, result, projectPath);
|
|
658
|
-
await recordIndexTelemetry(cg, result);
|
|
659
|
-
if (!result.success) {
|
|
660
|
-
process.exit(1);
|
|
661
|
-
}
|
|
662
|
-
clack.outro('Done');
|
|
663
|
-
cg.destroy();
|
|
664
694
|
}
|
|
665
695
|
catch (err) {
|
|
666
696
|
error(`Failed to index: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -763,6 +793,8 @@ function main() {
|
|
|
763
793
|
const journalMode = cg.getJournalMode();
|
|
764
794
|
const buildInfo = cg.getIndexBuildInfo();
|
|
765
795
|
const reindexRecommended = cg.isIndexStale();
|
|
796
|
+
const indexState = cg.getIndexState();
|
|
797
|
+
const ohosApi = cg.getOhosApiBinding();
|
|
766
798
|
// JSON output mode
|
|
767
799
|
if (options.json) {
|
|
768
800
|
const lastIndexedMs = cg.getLastIndexedAt();
|
|
@@ -788,7 +820,11 @@ function main() {
|
|
|
788
820
|
worktreeMismatch: worktreeMismatch
|
|
789
821
|
? { worktreeRoot: worktreeMismatch.worktreeRoot, indexRoot: worktreeMismatch.indexRoot }
|
|
790
822
|
: null,
|
|
823
|
+
ohosApi: ohosApi
|
|
824
|
+
? { version: ohosApi.version, packageName: ohosApi.packageName, dbPath: ohosApi.dbPath }
|
|
825
|
+
: null,
|
|
791
826
|
index: {
|
|
827
|
+
state: indexState,
|
|
792
828
|
builtWithVersion: buildInfo.version,
|
|
793
829
|
builtWithExtractionVersion: buildInfo.extractionVersion,
|
|
794
830
|
currentExtractionVersion: extraction_version_1.EXTRACTION_VERSION,
|
|
@@ -842,6 +878,13 @@ function main() {
|
|
|
842
878
|
console.log(` ${lang.padEnd(15)} ${formatNumber(count)}`);
|
|
843
879
|
}
|
|
844
880
|
console.log();
|
|
881
|
+
if (ohosApi) {
|
|
882
|
+
console.log(chalk.bold('OHOS API Database:'));
|
|
883
|
+
console.log(` Version: ${ohosApi.version}`);
|
|
884
|
+
console.log(` Package: ${ohosApi.packageName}`);
|
|
885
|
+
console.log(` DB: ${ohosApi.dbPath}`);
|
|
886
|
+
console.log();
|
|
887
|
+
}
|
|
845
888
|
// Pending changes
|
|
846
889
|
const totalChanges = changes.added.length + changes.modified.length + changes.removed.length;
|
|
847
890
|
if (totalChanges > 0) {
|
|
@@ -918,13 +961,16 @@ function main() {
|
|
|
918
961
|
}
|
|
919
962
|
else {
|
|
920
963
|
console.log(chalk.bold(`\nSearch Results for "${search}":\n`));
|
|
964
|
+
// Results arrive already ranked by relevance, so the order conveys
|
|
965
|
+
// it. We don't print the raw score: it's an unbounded BM25/FTS value
|
|
966
|
+
// (relative-ranking only), and the old `(score * 100)%` rendered it
|
|
967
|
+
// as nonsensical percentages like "12042%" (#1045). The MCP search
|
|
968
|
+
// tool likewise shows no score. Raw `score` stays in --json output.
|
|
921
969
|
for (const result of results) {
|
|
922
970
|
const node = result.node;
|
|
923
971
|
const location = `${node.filePath}:${node.startLine}`;
|
|
924
|
-
const score = chalk.dim(`(${(result.score * 100).toFixed(0)}%)`);
|
|
925
972
|
console.log(chalk.cyan(node.kind.padEnd(12)) +
|
|
926
|
-
chalk.white(node.name)
|
|
927
|
-
' ' + score);
|
|
973
|
+
chalk.white(node.name));
|
|
928
974
|
console.log(chalk.dim(` ${location}`));
|
|
929
975
|
if (node.signature) {
|
|
930
976
|
console.log(chalk.dim(` ${node.signature}`));
|
|
@@ -1019,11 +1065,15 @@ function main() {
|
|
|
1019
1065
|
return;
|
|
1020
1066
|
}
|
|
1021
1067
|
const prompt = String(input.prompt || '');
|
|
1022
|
-
// Gate: only structural / flow / impact / where-how prompts get context
|
|
1023
|
-
//
|
|
1024
|
-
//
|
|
1025
|
-
|
|
1026
|
-
|
|
1068
|
+
// Gate: only structural / flow / impact / where-how prompts get context, so
|
|
1069
|
+
// every other prompt ("fix this typo") stays a zero-cost no-op. Language-aware
|
|
1070
|
+
// (English + CJK keywords, plus code-shaped tokens) so it fires for non-English
|
|
1071
|
+
// prompts too (issue #994). A keyword fires on its own; a code-token is only a
|
|
1072
|
+
// CANDIDATE — verified against the graph below, so a tech brand ("JavaScript")
|
|
1073
|
+
// that looks like a symbol but isn't one here doesn't inject spurious context.
|
|
1074
|
+
const keyworded = (0, directory_1.hasStructuralKeyword)(prompt);
|
|
1075
|
+
const codeTokens = keyworded ? [] : (0, directory_1.extractCodeTokens)(prompt);
|
|
1076
|
+
if (!keyworded && codeTokens.length === 0)
|
|
1027
1077
|
return;
|
|
1028
1078
|
// Decide what to inject, shaped by WHERE the index(es) are: the nearest
|
|
1029
1079
|
// indexed ancestor of cwd, or — when cwd is an un-indexed workspace root
|
|
@@ -1043,6 +1093,13 @@ function main() {
|
|
|
1043
1093
|
const { default: HomeGraph } = await loadHomeGraph();
|
|
1044
1094
|
const cg = await HomeGraph.open(plan.exploreRoot);
|
|
1045
1095
|
try {
|
|
1096
|
+
// Code-token-only prompt: require that at least one token is a REAL symbol
|
|
1097
|
+
// in THIS index before front-loading. Without it, a brand name or common
|
|
1098
|
+
// word that merely looks like code ("JavaScript", "GitHub") would run
|
|
1099
|
+
// explore and inject ~16KB of low-relevance context (issue #994 follow-up).
|
|
1100
|
+
// A keyword-bearing prompt skips this — the keyword is signal enough.
|
|
1101
|
+
if (!keyworded && !codeTokens.some((t) => cg.getNodesByName(t).length > 0))
|
|
1102
|
+
return;
|
|
1046
1103
|
const { ToolHandler } = await Promise.resolve().then(() => __importStar(require('../mcp/tools')));
|
|
1047
1104
|
const handler = new ToolHandler(cg);
|
|
1048
1105
|
const result = await handler.execute('homegraph_explore', { query: prompt });
|
|
@@ -1078,14 +1135,17 @@ function main() {
|
|
|
1078
1135
|
}
|
|
1079
1136
|
});
|
|
1080
1137
|
/**
|
|
1081
|
-
* homegraph node
|
|
1138
|
+
* homegraph node [name]
|
|
1082
1139
|
*
|
|
1083
1140
|
* The CLI face of the MCP homegraph_node tool: one symbol's source +
|
|
1084
1141
|
* caller/callee trail, or a whole file with line numbers + dependents
|
|
1085
1142
|
* (Read-parity). Same subagent/non-MCP rationale as `explore`.
|
|
1143
|
+
*
|
|
1144
|
+
* `name` is OPTIONAL because `--file` (file-read mode) carries no symbol —
|
|
1145
|
+
* a required `<name>` made `codegraph node -f <file>` unreachable (#1044).
|
|
1086
1146
|
*/
|
|
1087
1147
|
program
|
|
1088
|
-
.command('node
|
|
1148
|
+
.command('node [name]')
|
|
1089
1149
|
.description('One symbol\'s source + caller/callee trail, or read a file with line numbers + dependents (same output as the homegraph_node MCP tool)')
|
|
1090
1150
|
.option('-p, --path <path>', 'Project path')
|
|
1091
1151
|
.option('-f, --file <file>', 'Treat as file mode (or disambiguate a symbol to this file)')
|
|
@@ -1093,6 +1153,13 @@ function main() {
|
|
|
1093
1153
|
.option('--limit <number>', 'File mode: maximum lines')
|
|
1094
1154
|
.option('--symbols-only', 'File mode: just the symbol map + dependents')
|
|
1095
1155
|
.action(async (name, options) => {
|
|
1156
|
+
// Need a symbol (positional) OR a file (--file / a path-like positional).
|
|
1157
|
+
// With [name] optional, a bare `codegraph node` reaches here with neither
|
|
1158
|
+
// and must be told what to pass, rather than crashing downstream.
|
|
1159
|
+
if (!name && !options.file) {
|
|
1160
|
+
error("Pass a symbol name (e.g. 'codegraph node parseToken') or a file (e.g. 'codegraph node -f src/auth.ts', or 'codegraph node src/auth.ts').");
|
|
1161
|
+
process.exit(1);
|
|
1162
|
+
}
|
|
1096
1163
|
const projectPath = resolveProjectPath(options.path);
|
|
1097
1164
|
try {
|
|
1098
1165
|
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
@@ -1114,10 +1181,10 @@ function main() {
|
|
|
1114
1181
|
if (name && name !== options.file)
|
|
1115
1182
|
args.symbol = name;
|
|
1116
1183
|
}
|
|
1117
|
-
else if (name.includes('/') || name.includes('\\')) {
|
|
1184
|
+
else if (name && (name.includes('/') || name.includes('\\'))) {
|
|
1118
1185
|
args.file = name.replace(/\\/g, '/');
|
|
1119
1186
|
}
|
|
1120
|
-
else {
|
|
1187
|
+
else if (name) {
|
|
1121
1188
|
args.symbol = name;
|
|
1122
1189
|
args.includeCode = true;
|
|
1123
1190
|
}
|
|
@@ -1850,6 +1917,92 @@ function main() {
|
|
|
1850
1917
|
process.exit(1);
|
|
1851
1918
|
}
|
|
1852
1919
|
});
|
|
1920
|
+
/**
|
|
1921
|
+
* homegraph install
|
|
1922
|
+
*/
|
|
1923
|
+
program
|
|
1924
|
+
.command('install')
|
|
1925
|
+
.description('Install homegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
|
|
1926
|
+
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
|
|
1927
|
+
.option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
|
|
1928
|
+
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
|
|
1929
|
+
.option('--no-permissions', 'Skip writing the auto-allow permissions list (Claude Code only)')
|
|
1930
|
+
.option('--print-config <id>', 'Print MCP config snippet for the named agent and exit (no file writes)')
|
|
1931
|
+
.action(async (opts) => {
|
|
1932
|
+
if (opts.printConfig) {
|
|
1933
|
+
const { getTarget, listTargetIds } = await Promise.resolve().then(() => __importStar(require('../installer/targets/registry')));
|
|
1934
|
+
const target = getTarget(opts.printConfig);
|
|
1935
|
+
if (!target) {
|
|
1936
|
+
const known = listTargetIds().join(', ');
|
|
1937
|
+
error(`Unknown target "${opts.printConfig}". Known: ${known}.`);
|
|
1938
|
+
process.exit(1);
|
|
1939
|
+
}
|
|
1940
|
+
const loc = (opts.location === 'local' ? 'local' : 'global');
|
|
1941
|
+
process.stdout.write(target.printConfig(loc));
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
const { runInstallerWithOptions } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
1945
|
+
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
1946
|
+
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
1947
|
+
process.exit(1);
|
|
1948
|
+
}
|
|
1949
|
+
try {
|
|
1950
|
+
// Commander's `--no-permissions` makes `opts.permissions === false`;
|
|
1951
|
+
// omitting the flag leaves it `true` (the positive-form default).
|
|
1952
|
+
// We MUST treat the default-true as "user did not override — let
|
|
1953
|
+
// the orchestrator prompt" and only forward an explicit `false`
|
|
1954
|
+
// (or `true` when --yes implies it). Otherwise the auto-allow
|
|
1955
|
+
// prompt is silently skipped on every interactive run.
|
|
1956
|
+
const explicitNoPermissions = opts.permissions === false;
|
|
1957
|
+
const autoAllow = explicitNoPermissions
|
|
1958
|
+
? false
|
|
1959
|
+
: opts.yes
|
|
1960
|
+
? true
|
|
1961
|
+
: undefined;
|
|
1962
|
+
await runInstallerWithOptions({
|
|
1963
|
+
target: opts.target,
|
|
1964
|
+
location: opts.location,
|
|
1965
|
+
autoAllow,
|
|
1966
|
+
yes: opts.yes,
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
catch (err) {
|
|
1970
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1971
|
+
process.exit(1);
|
|
1972
|
+
}
|
|
1973
|
+
});
|
|
1974
|
+
/**
|
|
1975
|
+
* homegraph uninstall
|
|
1976
|
+
*
|
|
1977
|
+
* Inverse of `install`. Removes the homegraph MCP server entry,
|
|
1978
|
+
* instructions block, and permissions from every agent (or a
|
|
1979
|
+
* `--target` subset). Prompts global-vs-local when not given. Does NOT
|
|
1980
|
+
* delete the `.homegraph/` index — that's `homegraph uninit`.
|
|
1981
|
+
*/
|
|
1982
|
+
program
|
|
1983
|
+
.command('uninstall')
|
|
1984
|
+
.description('Remove homegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
|
|
1985
|
+
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
|
|
1986
|
+
.option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
|
|
1987
|
+
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
|
|
1988
|
+
.action(async (opts) => {
|
|
1989
|
+
const { runUninstaller } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
1990
|
+
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
1991
|
+
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
1992
|
+
process.exit(1);
|
|
1993
|
+
}
|
|
1994
|
+
try {
|
|
1995
|
+
await runUninstaller({
|
|
1996
|
+
target: opts.target,
|
|
1997
|
+
location: opts.location,
|
|
1998
|
+
yes: opts.yes,
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
catch (err) {
|
|
2002
|
+
error(err instanceof Error ? err.message : String(err));
|
|
2003
|
+
process.exit(1);
|
|
2004
|
+
}
|
|
2005
|
+
});
|
|
1853
2006
|
// =============================================================================
|
|
1854
2007
|
// Spec commands (Commit4Spec knowledge graph)
|
|
1855
2008
|
// =============================================================================
|
|
@@ -1926,7 +2079,6 @@ function main() {
|
|
|
1926
2079
|
const repoPath = resolveSpecProjectPath(options.path);
|
|
1927
2080
|
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
1928
2081
|
const { resolveDbPath, computeBudgetProfile } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
1929
|
-
const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
|
|
1930
2082
|
const { searchAndGetContext } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
1931
2083
|
const dbPath = resolveDbPath(repoPath, options.dbPath);
|
|
1932
2084
|
if (!fs.existsSync(dbPath)) {
|
|
@@ -1935,7 +2087,6 @@ function main() {
|
|
|
1935
2087
|
}
|
|
1936
2088
|
const created = createDatabase(dbPath);
|
|
1937
2089
|
db = created.db;
|
|
1938
|
-
initSpecSchema(db);
|
|
1939
2090
|
const topK = Math.max(1, Math.min(parseInt(options.topK || '5', 10) || 5, 50));
|
|
1940
2091
|
const includeFragments = options.fragments !== false;
|
|
1941
2092
|
const contexts = searchAndGetContext(db, text, topK, includeFragments);
|
|
@@ -1996,6 +2147,298 @@ function main() {
|
|
|
1996
2147
|
catch { /* best effort */ }
|
|
1997
2148
|
}
|
|
1998
2149
|
});
|
|
2150
|
+
/**
|
|
2151
|
+
* homegraph spec find <filePath>
|
|
2152
|
+
*/
|
|
2153
|
+
specCommand
|
|
2154
|
+
.command('find <filePath>')
|
|
2155
|
+
.description('Find specs related to the given file path via code-fragment matching')
|
|
2156
|
+
.option('-p, --path <path>', 'Path to the repository')
|
|
2157
|
+
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2158
|
+
.option('-j, --json', 'Output as JSON')
|
|
2159
|
+
.action(async (filePath, options) => {
|
|
2160
|
+
let db;
|
|
2161
|
+
try {
|
|
2162
|
+
const repoPath = resolveSpecProjectPath(options.path);
|
|
2163
|
+
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2164
|
+
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2165
|
+
const { findSpecsByFilePath } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2166
|
+
const dbPath = resolveDbPath(repoPath, options.dbPath);
|
|
2167
|
+
if (!fs.existsSync(dbPath)) {
|
|
2168
|
+
error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
|
|
2169
|
+
process.exit(1);
|
|
2170
|
+
}
|
|
2171
|
+
const created = createDatabase(dbPath);
|
|
2172
|
+
db = created.db;
|
|
2173
|
+
const result = findSpecsByFilePath(db, filePath);
|
|
2174
|
+
if (options.json) {
|
|
2175
|
+
console.log(JSON.stringify({
|
|
2176
|
+
filePath,
|
|
2177
|
+
matched_count: result.matched_count,
|
|
2178
|
+
truncated: result.truncated,
|
|
2179
|
+
results: result.results,
|
|
2180
|
+
}, null, 2));
|
|
2181
|
+
}
|
|
2182
|
+
else {
|
|
2183
|
+
console.log(chalk.bold(`\n${result.matched_count} spec${result.matched_count !== 1 ? 's' : ''} matched for ${filePath}:\n`));
|
|
2184
|
+
for (const r of result.results) {
|
|
2185
|
+
console.log(` ${chalk.cyan(r.id.padEnd(16))} ${r.title.padEnd(32)} ${chalk.green(r.status.padEnd(12))} v${r.version} ${chalk.dim(r.filePath)}`);
|
|
2186
|
+
}
|
|
2187
|
+
console.log();
|
|
2188
|
+
if (result.truncated) {
|
|
2189
|
+
console.log(chalk.yellow(` ... and more (showing first ${result.matched_count} of >${result.matched_count} results)`));
|
|
2190
|
+
console.log();
|
|
2191
|
+
}
|
|
2192
|
+
if (result.matched_count === 0) {
|
|
2193
|
+
info(`No specs found for file path "${filePath}". Try a partial path (e.g. "src/auth" instead of "src/auth/login.ts").`);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
catch (err) {
|
|
2198
|
+
error(`Find failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2199
|
+
process.exit(1);
|
|
2200
|
+
}
|
|
2201
|
+
finally {
|
|
2202
|
+
try {
|
|
2203
|
+
db?.close();
|
|
2204
|
+
}
|
|
2205
|
+
catch { /* best effort */ }
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
/**
|
|
2209
|
+
* homegraph spec trace <symbol>
|
|
2210
|
+
*/
|
|
2211
|
+
specCommand
|
|
2212
|
+
.command('trace <symbol>')
|
|
2213
|
+
.description('Trace a code symbol back to its associated design Specs')
|
|
2214
|
+
.option('-f, --file <path>', 'File path for symbol disambiguation')
|
|
2215
|
+
.option('-l, --line <number>', 'Line number for symbol disambiguation')
|
|
2216
|
+
.option('-p, --path <path>', 'Path to the repository')
|
|
2217
|
+
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2218
|
+
.option('--top-k <number>', 'Number of results to return', '10')
|
|
2219
|
+
.option('-j, --json', 'Output as JSON')
|
|
2220
|
+
.action(async (symbol, options) => {
|
|
2221
|
+
let db;
|
|
2222
|
+
let cg;
|
|
2223
|
+
try {
|
|
2224
|
+
const repoPath = resolveSpecProjectPath(options.path);
|
|
2225
|
+
// isNaN check so --line 0 is handled correctly (parseInt('0') → 0 is falsy)
|
|
2226
|
+
const lineRaw = options.line !== undefined ? parseInt(options.line, 10) : NaN;
|
|
2227
|
+
const line = isNaN(lineRaw) ? undefined : lineRaw;
|
|
2228
|
+
// Open HomeGraph code index
|
|
2229
|
+
const { default: HomeGraph } = await Promise.resolve().then(() => __importStar(require('../index')));
|
|
2230
|
+
cg = await HomeGraph.open(repoPath);
|
|
2231
|
+
// Inline symbol resolution (mirrors findSymbolMatches in tools.ts)
|
|
2232
|
+
const isQualified = /[.\/]|::/.test(symbol);
|
|
2233
|
+
const { isGeneratedFile } = await Promise.resolve().then(() => __importStar(require('../extraction/generated-detection')));
|
|
2234
|
+
let nodes = [];
|
|
2235
|
+
if (!isQualified) {
|
|
2236
|
+
const exact = cg.getNodesByName(symbol);
|
|
2237
|
+
if (exact.length > 0) {
|
|
2238
|
+
nodes = [...exact].sort((a, b) => (isGeneratedFile(a.filePath) ? 1 : 0) - (isGeneratedFile(b.filePath) ? 1 : 0));
|
|
2239
|
+
}
|
|
2240
|
+
else {
|
|
2241
|
+
const fuzzy = cg.searchNodes(symbol, { limit: 10 });
|
|
2242
|
+
if (fuzzy[0])
|
|
2243
|
+
nodes = [fuzzy[0].node];
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
else {
|
|
2247
|
+
let results = cg.searchNodes(symbol, { limit: 50 });
|
|
2248
|
+
if (results.length === 0) {
|
|
2249
|
+
const parts = symbol.split(/::|[./]/).filter((p) => p.length > 0);
|
|
2250
|
+
const tail = parts[parts.length - 1];
|
|
2251
|
+
if (tail && tail !== symbol)
|
|
2252
|
+
results = cg.searchNodes(tail, { limit: 50 });
|
|
2253
|
+
}
|
|
2254
|
+
// Rough matchesSymbol equivalent for CLI
|
|
2255
|
+
const exactMatches = results.filter((r) => {
|
|
2256
|
+
const n = r.node;
|
|
2257
|
+
if (n.name === symbol)
|
|
2258
|
+
return true;
|
|
2259
|
+
const fileBase = n.filePath.split('/').pop()?.replace(/\.[^.]+$/, '');
|
|
2260
|
+
if (fileBase === symbol)
|
|
2261
|
+
return true;
|
|
2262
|
+
const parts = symbol.split(/::|[./]/);
|
|
2263
|
+
if (parts[parts.length - 1] === n.name) {
|
|
2264
|
+
const firstPart = parts[0];
|
|
2265
|
+
if (n.qualifiedName.includes(firstPart))
|
|
2266
|
+
return true;
|
|
2267
|
+
if (n.filePath.includes(firstPart))
|
|
2268
|
+
return true;
|
|
2269
|
+
return parts.length === 1;
|
|
2270
|
+
}
|
|
2271
|
+
return false;
|
|
2272
|
+
});
|
|
2273
|
+
if (exactMatches.length > 0) {
|
|
2274
|
+
nodes = exactMatches
|
|
2275
|
+
.sort((a, b) => (isGeneratedFile(a.node.filePath) ? 1 : 0) - (isGeneratedFile(b.node.filePath) ? 1 : 0))
|
|
2276
|
+
.map((r) => r.node);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
if (nodes.length === 0) {
|
|
2280
|
+
console.log(chalk.yellow(`No code entities found for symbol "${symbol}".`));
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
// Disambiguate
|
|
2284
|
+
if (options.file) {
|
|
2285
|
+
// Use endsWith for precise file name matching (not substring matching)
|
|
2286
|
+
const filePattern = options.file;
|
|
2287
|
+
nodes = nodes.filter((n) => n.filePath.endsWith(filePattern));
|
|
2288
|
+
}
|
|
2289
|
+
if (line !== undefined && nodes.length > 1) {
|
|
2290
|
+
const closest = nodes.reduce((best, n) => {
|
|
2291
|
+
const bestDist = Math.abs(best.startLine - line) + Math.abs(best.endLine - line);
|
|
2292
|
+
const curDist = Math.abs(n.startLine - line) + Math.abs(n.endLine - line);
|
|
2293
|
+
return curDist < bestDist ? n : best;
|
|
2294
|
+
});
|
|
2295
|
+
nodes = [closest];
|
|
2296
|
+
}
|
|
2297
|
+
const node = nodes[0];
|
|
2298
|
+
if (!node) {
|
|
2299
|
+
console.log(chalk.yellow(`Could not resolve symbol "${symbol}" to a specific code entity.`));
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
// Open Spec database
|
|
2303
|
+
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2304
|
+
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2305
|
+
const { findSpecsByCodeSymbol } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2306
|
+
const { initSpecSchema, runSpecMigrations, getCurrentSpecVersion, CURRENT_SPEC_SCHEMA_VERSION } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
|
|
2307
|
+
const dbPath = resolveDbPath(repoPath, options.dbPath);
|
|
2308
|
+
if (!fs.existsSync(dbPath)) {
|
|
2309
|
+
console.log(chalk.yellow(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`));
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
const created = createDatabase(dbPath);
|
|
2313
|
+
db = created.db;
|
|
2314
|
+
initSpecSchema(db);
|
|
2315
|
+
const currentVersion = getCurrentSpecVersion(db);
|
|
2316
|
+
if (currentVersion < CURRENT_SPEC_SCHEMA_VERSION) {
|
|
2317
|
+
runSpecMigrations(db, currentVersion);
|
|
2318
|
+
}
|
|
2319
|
+
const topKRaw = parseInt(options.topK || '10', 10);
|
|
2320
|
+
const topK = Math.max(1, Math.min(isNaN(topKRaw) ? 10 : topKRaw, 50));
|
|
2321
|
+
console.log(chalk.dim(`Resolved: ${node.name} (${node.kind}) in ${node.filePath}:${node.startLine}-${node.endLine}`));
|
|
2322
|
+
const result = findSpecsByCodeSymbol(db, {
|
|
2323
|
+
name: node.name,
|
|
2324
|
+
qualifiedName: node.qualifiedName,
|
|
2325
|
+
kind: node.kind,
|
|
2326
|
+
filePath: node.filePath,
|
|
2327
|
+
startLine: node.startLine,
|
|
2328
|
+
endLine: node.endLine,
|
|
2329
|
+
}, topK);
|
|
2330
|
+
if (options.json) {
|
|
2331
|
+
console.log(JSON.stringify({
|
|
2332
|
+
symbol,
|
|
2333
|
+
entity: {
|
|
2334
|
+
name: node.name,
|
|
2335
|
+
qualifiedName: node.qualifiedName,
|
|
2336
|
+
kind: node.kind,
|
|
2337
|
+
filePath: node.filePath,
|
|
2338
|
+
startLine: node.startLine,
|
|
2339
|
+
endLine: node.endLine,
|
|
2340
|
+
},
|
|
2341
|
+
matched_count: result.matches.length,
|
|
2342
|
+
total_candidates: result.totalCandidates,
|
|
2343
|
+
matches: result.matches.map((m) => ({
|
|
2344
|
+
spec_id: m.spec.id,
|
|
2345
|
+
title: m.spec.title,
|
|
2346
|
+
status: m.spec.status,
|
|
2347
|
+
version: m.spec.version,
|
|
2348
|
+
file_path: m.spec.filePath,
|
|
2349
|
+
score: Math.round(m.score * 1000) / 1000,
|
|
2350
|
+
score_detail: {
|
|
2351
|
+
file_path: Math.round(m.scoreDetail.filePathScore * 1000) / 1000,
|
|
2352
|
+
content: Math.round(m.scoreDetail.contentScore * 1000) / 1000,
|
|
2353
|
+
name: Math.round(m.scoreDetail.nameScore * 1000) / 1000,
|
|
2354
|
+
recency: Math.round(m.scoreDetail.recencyScore * 1000) / 1000,
|
|
2355
|
+
line_overlap: Math.round(m.scoreDetail.overlapScore * 1000) / 1000,
|
|
2356
|
+
},
|
|
2357
|
+
fragment_count: m.fragmentCount,
|
|
2358
|
+
commit_count: m.commitCount,
|
|
2359
|
+
})),
|
|
2360
|
+
}, null, 2));
|
|
2361
|
+
}
|
|
2362
|
+
else {
|
|
2363
|
+
console.log(chalk.bold(`\n${result.matches.length} spec${result.matches.length !== 1 ? 's' : ''} matched (${result.totalCandidates} candidates):\n`));
|
|
2364
|
+
for (const m of result.matches) {
|
|
2365
|
+
const scoreColor = m.score >= 0.7 ? chalk.green : m.score >= 0.4 ? chalk.yellow : chalk.red;
|
|
2366
|
+
console.log(` ${chalk.cyan(m.spec.id.padEnd(14))} ${m.spec.title.padEnd(36)} ${scoreColor((m.score * 100).toFixed(1) + '%')}`);
|
|
2367
|
+
console.log(chalk.dim(` fp=${m.scoreDetail.filePathScore.toFixed(2)} content=${m.scoreDetail.contentScore.toFixed(2)} name=${m.scoreDetail.nameScore.toFixed(2)} recency=${m.scoreDetail.recencyScore.toFixed(2)} overlap=${m.scoreDetail.overlapScore.toFixed(2)}`));
|
|
2368
|
+
console.log();
|
|
2369
|
+
}
|
|
2370
|
+
if (result.matches.length === 0) {
|
|
2371
|
+
info(`No Specs found for symbol "${symbol}". Try a more specific file path or check that specs have been mined for this project.`);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
catch (err) {
|
|
2376
|
+
error(`Trace failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2377
|
+
process.exit(1);
|
|
2378
|
+
}
|
|
2379
|
+
finally {
|
|
2380
|
+
try {
|
|
2381
|
+
await cg?.close();
|
|
2382
|
+
}
|
|
2383
|
+
catch { /* best effort */ }
|
|
2384
|
+
try {
|
|
2385
|
+
db?.close();
|
|
2386
|
+
}
|
|
2387
|
+
catch { /* best effort */ }
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
/**
|
|
2391
|
+
* homegraph spec stats
|
|
2392
|
+
*/
|
|
2393
|
+
specCommand
|
|
2394
|
+
.command('stats')
|
|
2395
|
+
.description('Show statistics about the spec knowledge graph')
|
|
2396
|
+
.option('-p, --path <path>', 'Path to the repository')
|
|
2397
|
+
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2398
|
+
.option('-j, --json', 'Output as JSON')
|
|
2399
|
+
.action(async (options) => {
|
|
2400
|
+
let db;
|
|
2401
|
+
try {
|
|
2402
|
+
const repoPath = resolveSpecProjectPath(options.path);
|
|
2403
|
+
const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
|
|
2404
|
+
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2405
|
+
const { getSpecStats } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
|
|
2406
|
+
const dbPath = resolveDbPath(repoPath, options.dbPath);
|
|
2407
|
+
if (!fs.existsSync(dbPath)) {
|
|
2408
|
+
error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
|
|
2409
|
+
process.exit(1);
|
|
2410
|
+
}
|
|
2411
|
+
const created = createDatabase(dbPath);
|
|
2412
|
+
db = created.db;
|
|
2413
|
+
const stats = getSpecStats(db);
|
|
2414
|
+
if (options.json) {
|
|
2415
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
2416
|
+
}
|
|
2417
|
+
else {
|
|
2418
|
+
console.log(chalk.bold('\nSpec Knowledge Graph Stats\n'));
|
|
2419
|
+
console.log(` Specs: ${chalk.cyan(String(stats.specCount))}`);
|
|
2420
|
+
console.log(` Active: ${chalk.green(String(stats.activeSpecCount))}`);
|
|
2421
|
+
console.log(` Deprecated: ${chalk.yellow(String(stats.deprecatedSpecCount))}`);
|
|
2422
|
+
console.log(` Commits: ${stats.commitCount}`);
|
|
2423
|
+
console.log(` Fragments: ${stats.fragmentCount}`);
|
|
2424
|
+
console.log(` Relations: ${stats.relationCount}`);
|
|
2425
|
+
console.log();
|
|
2426
|
+
if (stats.specCount === 0) {
|
|
2427
|
+
info("No specs yet. Run 'homegraph spec mine' to build the knowledge graph.");
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
catch (err) {
|
|
2432
|
+
error(`Stats failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2433
|
+
process.exit(1);
|
|
2434
|
+
}
|
|
2435
|
+
finally {
|
|
2436
|
+
try {
|
|
2437
|
+
db?.close();
|
|
2438
|
+
}
|
|
2439
|
+
catch { /* best effort */ }
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
1999
2442
|
/**
|
|
2000
2443
|
* homegraph spec evolve
|
|
2001
2444
|
*/
|
|
@@ -2069,8 +2512,12 @@ function main() {
|
|
|
2069
2512
|
'# Triggers spec self-evolution after each commit. Runs in background.',
|
|
2070
2513
|
'# Installed by: homegraph spec evolve install',
|
|
2071
2514
|
`# Logs: ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`,
|
|
2072
|
-
|
|
2073
|
-
`
|
|
2515
|
+
'# Runtime guard: skip if homegraph is not available',
|
|
2516
|
+
`HOMEGRAPH_BIN="${homegraphBin}"`,
|
|
2517
|
+
'if [ -x "$HOMEGRAPH_BIN" ] || command -v homegraph >/dev/null 2>&1; then',
|
|
2518
|
+
' "${HOMEGRAPH_BIN:-homegraph}" spec evolve process --path "$(pwd)" --json \\',
|
|
2519
|
+
` >> ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log 2>&1 &`,
|
|
2520
|
+
'fi',
|
|
2074
2521
|
MARKER_END,
|
|
2075
2522
|
].join('\n');
|
|
2076
2523
|
let content;
|
|
@@ -2111,15 +2558,85 @@ function main() {
|
|
|
2111
2558
|
process.exit(1);
|
|
2112
2559
|
}
|
|
2113
2560
|
});
|
|
2561
|
+
/**
|
|
2562
|
+
* homegraph spec evolve uninstall
|
|
2563
|
+
*/
|
|
2564
|
+
evolveCommand
|
|
2565
|
+
.command('uninstall')
|
|
2566
|
+
.description('Remove the git post-commit hook installed by spec evolve')
|
|
2567
|
+
.option('-p, --path <path>', 'Path to the repository')
|
|
2568
|
+
.action(async (options) => {
|
|
2569
|
+
try {
|
|
2570
|
+
const repoPath = resolveSpecProjectPath(options.path);
|
|
2571
|
+
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
|
|
2572
|
+
if (!isGitRepo(repoPath)) {
|
|
2573
|
+
error(`Not a git repository: ${repoPath}`);
|
|
2574
|
+
process.exit(1);
|
|
2575
|
+
}
|
|
2576
|
+
const { execFileSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
2577
|
+
const hooksDir = execFileSync('git', ['rev-parse', '--git-path', 'hooks'], {
|
|
2578
|
+
cwd: repoPath,
|
|
2579
|
+
encoding: 'utf8',
|
|
2580
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
2581
|
+
windowsHide: true,
|
|
2582
|
+
}).trim();
|
|
2583
|
+
const resolvedHooksDir = path.isAbsolute(hooksDir)
|
|
2584
|
+
? hooksDir
|
|
2585
|
+
: path.resolve(repoPath, hooksDir);
|
|
2586
|
+
const hookPath = path.join(resolvedHooksDir, 'post-commit');
|
|
2587
|
+
if (!fs.existsSync(hookPath)) {
|
|
2588
|
+
info('No post-commit hook found — nothing to uninstall.');
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
const MARKER_BEGIN = '# >>> homegraph spec evolve hook >>>';
|
|
2592
|
+
const MARKER_END = '# <<< homegraph spec evolve hook <<<';
|
|
2593
|
+
const existing = fs.readFileSync(hookPath, 'utf8');
|
|
2594
|
+
const lines = existing.split('\n');
|
|
2595
|
+
const kept = [];
|
|
2596
|
+
let inBlock = false;
|
|
2597
|
+
let removedBlock = false;
|
|
2598
|
+
for (const line of lines) {
|
|
2599
|
+
const trimmed = line.trim();
|
|
2600
|
+
if (trimmed === MARKER_BEGIN) {
|
|
2601
|
+
inBlock = true;
|
|
2602
|
+
removedBlock = true;
|
|
2603
|
+
continue;
|
|
2604
|
+
}
|
|
2605
|
+
if (trimmed === MARKER_END) {
|
|
2606
|
+
inBlock = false;
|
|
2607
|
+
continue;
|
|
2608
|
+
}
|
|
2609
|
+
if (!inBlock)
|
|
2610
|
+
kept.push(line);
|
|
2611
|
+
}
|
|
2612
|
+
if (!removedBlock) {
|
|
2613
|
+
info('No homegraph spec evolve hook block found — nothing to uninstall.');
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
const remaining = kept.join('\n').trim();
|
|
2617
|
+
if (remaining.length === 0 || /^#!\/bin\/sh\s*$/.test(remaining)) {
|
|
2618
|
+
// Hook only contained our block (or just a shebang) — delete entirely
|
|
2619
|
+
fs.unlinkSync(hookPath);
|
|
2620
|
+
success(`Removed post-commit hook at ${hookPath}`);
|
|
2621
|
+
}
|
|
2622
|
+
else {
|
|
2623
|
+
fs.writeFileSync(hookPath, remaining + '\n');
|
|
2624
|
+
success(`Removed spec evolve hook block from ${hookPath} (user content preserved)`);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
catch (err) {
|
|
2628
|
+
error(`Hook uninstall failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2629
|
+
process.exit(1);
|
|
2630
|
+
}
|
|
2631
|
+
});
|
|
2114
2632
|
/**
|
|
2115
2633
|
* homegraph spec evolve process
|
|
2116
2634
|
*/
|
|
2117
2635
|
evolveCommand
|
|
2118
2636
|
.command('process')
|
|
2119
|
-
.description('Process
|
|
2637
|
+
.description('Process commits through the spec self-evolve pipeline since last evolve')
|
|
2120
2638
|
.option('-p, --path <path>', 'Path to the repository')
|
|
2121
2639
|
.option('--db-path <path>', 'Path to the SQLite database file')
|
|
2122
|
-
.option('--commit-hash <hash>', 'Commit hash to process (default: HEAD)', 'HEAD')
|
|
2123
2640
|
.option('-j, --json', 'Output as JSON')
|
|
2124
2641
|
.action(async (options) => {
|
|
2125
2642
|
let db;
|
|
@@ -2129,7 +2646,7 @@ function main() {
|
|
|
2129
2646
|
const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
|
|
2130
2647
|
const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
|
|
2131
2648
|
const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
|
|
2132
|
-
const {
|
|
2649
|
+
const { runBatchEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
|
|
2133
2650
|
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
|
|
2134
2651
|
if (!isGitRepo(repoPath)) {
|
|
2135
2652
|
error(`Not a git repository: ${repoPath}`);
|
|
@@ -2141,25 +2658,77 @@ function main() {
|
|
|
2141
2658
|
initSpecSchema(db);
|
|
2142
2659
|
const config = loadSpecConfig(repoPath);
|
|
2143
2660
|
const llmConfig = config.llm;
|
|
2144
|
-
|
|
2145
|
-
|
|
2661
|
+
if (!llmConfig) {
|
|
2662
|
+
warn('LLM not configured — Path B (LLM-based spec evolution) will be skipped.');
|
|
2663
|
+
warn('Only Path A (commit message scope → GENERATE) will be processed.');
|
|
2664
|
+
warn('Configure LLM in .homegraph/commit4spec/configs.json for full functionality:\n' +
|
|
2665
|
+
'{\n' +
|
|
2666
|
+
' "llm": {\n' +
|
|
2667
|
+
' "provider": "openai",\n' +
|
|
2668
|
+
' "apiKeyEnv": "OPENAI_API_KEY",\n' +
|
|
2669
|
+
' "model": "gpt-4o"\n' +
|
|
2670
|
+
' }\n' +
|
|
2671
|
+
'}');
|
|
2672
|
+
}
|
|
2673
|
+
const result = await runBatchEvolvePipeline(repoPath, db, llmConfig ?? undefined);
|
|
2146
2674
|
if (options.json) {
|
|
2147
2675
|
console.log(JSON.stringify(result, null, 2));
|
|
2148
2676
|
}
|
|
2149
2677
|
else {
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
console.log(chalk.cyan(`Link Path A - GENERATE ${result.generateSpecId}`));
|
|
2678
|
+
if (result.commitsProcessed === 0) {
|
|
2679
|
+
console.log(chalk.green('No new commits to evolve.'));
|
|
2680
|
+
console.log(`Last evolved commit: ${result.fromCommit ? result.fromCommit.slice(0, 7) : 'none'}`);
|
|
2681
|
+
console.log(`Current HEAD: ${result.toCommit.slice(0, 7)}`);
|
|
2155
2682
|
}
|
|
2156
|
-
|
|
2157
|
-
console.log(chalk.
|
|
2158
|
-
|
|
2159
|
-
|
|
2683
|
+
else {
|
|
2684
|
+
console.log(chalk.bold(`Evolve complete: ${result.commitsProcessed} commit(s) processed`));
|
|
2685
|
+
console.log(`Range: ${result.fromCommit ? result.fromCommit.slice(0, 7) : 'none'} → ${result.toCommit.slice(0, 7)}`);
|
|
2686
|
+
if (result.metaUpdated) {
|
|
2687
|
+
console.log(chalk.green(`meta.json updated (currentCommitID = ${result.toCommit.slice(0, 7)})`));
|
|
2688
|
+
}
|
|
2689
|
+
else if (result.failures > 0) {
|
|
2690
|
+
console.log(chalk.yellow(`⚠ meta.json NOT updated — ${result.failures} commit(s) failed`));
|
|
2160
2691
|
}
|
|
2692
|
+
else if (result.skippedCommits > 0) {
|
|
2693
|
+
console.log(chalk.yellow('⚠ meta.json NOT updated — all commits were skipped (no Path A match, no LLM)'));
|
|
2694
|
+
}
|
|
2695
|
+
else {
|
|
2696
|
+
console.log(chalk.yellow('⚠ meta.json NOT updated'));
|
|
2697
|
+
}
|
|
2698
|
+
// Show per-commit summary
|
|
2699
|
+
for (const r of result.perCommitResults) {
|
|
2700
|
+
let prefix;
|
|
2701
|
+
if (r.skipped) {
|
|
2702
|
+
prefix = chalk.yellow(' ⚠');
|
|
2703
|
+
}
|
|
2704
|
+
else {
|
|
2705
|
+
prefix = r.persisted ? chalk.green(' ✓') : chalk.red(' ✗');
|
|
2706
|
+
}
|
|
2707
|
+
console.log(`${prefix} ${r.commitHash.slice(0, 7)}`);
|
|
2708
|
+
if (r.skipped) {
|
|
2709
|
+
console.log(` skipped: ${r.skipReason}`);
|
|
2710
|
+
}
|
|
2711
|
+
if (r.isLogicChange) {
|
|
2712
|
+
console.log(` logic change: ${r.logicCheckReason}`);
|
|
2713
|
+
}
|
|
2714
|
+
if (r.generateSpecId) {
|
|
2715
|
+
console.log(` Path A - GENERATE ${r.generateSpecId}`);
|
|
2716
|
+
}
|
|
2717
|
+
for (const ev of r.evolvedSpecs) {
|
|
2718
|
+
console.log(` ${ev.specId}: ${ev.action}`);
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
// Aggregate summary
|
|
2722
|
+
const totalFragments = result.perCommitResults.reduce((s, r) => s + r.fragmentsCount, 0);
|
|
2723
|
+
const totalRelations = result.perCommitResults.reduce((s, r) => s + r.relationsCreated, 0);
|
|
2724
|
+
if (result.skippedCommits > 0) {
|
|
2725
|
+
console.log(chalk.yellow(`${result.skippedCommits} commit(s) skipped — no LLM configured, did not match Path A`));
|
|
2726
|
+
}
|
|
2727
|
+
if (result.failures > 0) {
|
|
2728
|
+
console.log(chalk.red(`${result.failures} commit(s) failed — see details above`));
|
|
2729
|
+
}
|
|
2730
|
+
console.log(`Summary: fragments=${totalFragments}, relations=${totalRelations}`);
|
|
2161
2731
|
}
|
|
2162
|
-
console.log(`Summary: fragments=${result.fragmentsCount}, relations=${result.relationsCreated}`);
|
|
2163
2732
|
}
|
|
2164
2733
|
}
|
|
2165
2734
|
catch (err) {
|
|
@@ -2179,92 +2748,6 @@ function main() {
|
|
|
2179
2748
|
catch { /* best effort */ }
|
|
2180
2749
|
}
|
|
2181
2750
|
});
|
|
2182
|
-
/**
|
|
2183
|
-
* homegraph install
|
|
2184
|
-
*/
|
|
2185
|
-
program
|
|
2186
|
-
.command('install')
|
|
2187
|
-
.description('Install homegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
|
|
2188
|
-
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
|
|
2189
|
-
.option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
|
|
2190
|
-
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
|
|
2191
|
-
.option('--no-permissions', 'Skip writing the auto-allow permissions list (Claude Code only)')
|
|
2192
|
-
.option('--print-config <id>', 'Print MCP config snippet for the named agent and exit (no file writes)')
|
|
2193
|
-
.action(async (opts) => {
|
|
2194
|
-
if (opts.printConfig) {
|
|
2195
|
-
const { getTarget, listTargetIds } = await Promise.resolve().then(() => __importStar(require('../installer/targets/registry')));
|
|
2196
|
-
const target = getTarget(opts.printConfig);
|
|
2197
|
-
if (!target) {
|
|
2198
|
-
const known = listTargetIds().join(', ');
|
|
2199
|
-
error(`Unknown target "${opts.printConfig}". Known: ${known}.`);
|
|
2200
|
-
process.exit(1);
|
|
2201
|
-
}
|
|
2202
|
-
const loc = (opts.location === 'local' ? 'local' : 'global');
|
|
2203
|
-
process.stdout.write(target.printConfig(loc));
|
|
2204
|
-
return;
|
|
2205
|
-
}
|
|
2206
|
-
const { runInstallerWithOptions } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
2207
|
-
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
2208
|
-
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
2209
|
-
process.exit(1);
|
|
2210
|
-
}
|
|
2211
|
-
try {
|
|
2212
|
-
// Commander's `--no-permissions` makes `opts.permissions === false`;
|
|
2213
|
-
// omitting the flag leaves it `true` (the positive-form default).
|
|
2214
|
-
// We MUST treat the default-true as "user did not override — let
|
|
2215
|
-
// the orchestrator prompt" and only forward an explicit `false`
|
|
2216
|
-
// (or `true` when --yes implies it). Otherwise the auto-allow
|
|
2217
|
-
// prompt is silently skipped on every interactive run.
|
|
2218
|
-
const explicitNoPermissions = opts.permissions === false;
|
|
2219
|
-
const autoAllow = explicitNoPermissions
|
|
2220
|
-
? false
|
|
2221
|
-
: opts.yes
|
|
2222
|
-
? true
|
|
2223
|
-
: undefined;
|
|
2224
|
-
await runInstallerWithOptions({
|
|
2225
|
-
target: opts.target,
|
|
2226
|
-
location: opts.location,
|
|
2227
|
-
autoAllow,
|
|
2228
|
-
yes: opts.yes,
|
|
2229
|
-
});
|
|
2230
|
-
}
|
|
2231
|
-
catch (err) {
|
|
2232
|
-
error(err instanceof Error ? err.message : String(err));
|
|
2233
|
-
process.exit(1);
|
|
2234
|
-
}
|
|
2235
|
-
});
|
|
2236
|
-
/**
|
|
2237
|
-
* homegraph uninstall
|
|
2238
|
-
*
|
|
2239
|
-
* Inverse of `install`. Removes the homegraph MCP server entry,
|
|
2240
|
-
* instructions block, and permissions from every agent (or a
|
|
2241
|
-
* `--target` subset). Prompts global-vs-local when not given. Does NOT
|
|
2242
|
-
* delete the `.homegraph/` index — that's `homegraph uninit`.
|
|
2243
|
-
*/
|
|
2244
|
-
program
|
|
2245
|
-
.command('uninstall')
|
|
2246
|
-
.description('Remove homegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
|
|
2247
|
-
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
|
|
2248
|
-
.option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
|
|
2249
|
-
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
|
|
2250
|
-
.action(async (opts) => {
|
|
2251
|
-
const { runUninstaller } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
2252
|
-
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
2253
|
-
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
2254
|
-
process.exit(1);
|
|
2255
|
-
}
|
|
2256
|
-
try {
|
|
2257
|
-
await runUninstaller({
|
|
2258
|
-
target: opts.target,
|
|
2259
|
-
location: opts.location,
|
|
2260
|
-
yes: opts.yes,
|
|
2261
|
-
});
|
|
2262
|
-
}
|
|
2263
|
-
catch (err) {
|
|
2264
|
-
error(err instanceof Error ? err.message : String(err));
|
|
2265
|
-
process.exit(1);
|
|
2266
|
-
}
|
|
2267
|
-
});
|
|
2268
2751
|
/**
|
|
2269
2752
|
* homegraph telemetry [on|off|status]
|
|
2270
2753
|
*/
|
|
@@ -2304,6 +2787,63 @@ function main() {
|
|
|
2304
2787
|
console.log(`Config: ${s.configPath}`);
|
|
2305
2788
|
console.log(chalk.dim(`\nExactly what is collected (and never collected): ${telemetry_1.TELEMETRY_DOCS}\n`));
|
|
2306
2789
|
});
|
|
2790
|
+
/**
|
|
2791
|
+
* homegraph index-api <input> [version]
|
|
2792
|
+
* Build a standalone OHOS API database from command-line-tools SDK.
|
|
2793
|
+
*/
|
|
2794
|
+
program
|
|
2795
|
+
.command('index-api <input> [version]')
|
|
2796
|
+
.description('Index OpenHarmony SDK API declarations into a standalone SQLite db')
|
|
2797
|
+
.option('-o, --output <path>', 'Output database path')
|
|
2798
|
+
.option('-q, --quiet', 'Suppress progress output')
|
|
2799
|
+
.action(async (input, versionArg, options) => {
|
|
2800
|
+
try {
|
|
2801
|
+
const { resolveOhosSdkInput, ohosApiDbFilename, indexOhosApiDb } = await Promise.resolve().then(() => __importStar(require('../extraction/languages/arkts')));
|
|
2802
|
+
const resolved = resolveOhosSdkInput({
|
|
2803
|
+
inputPath: input,
|
|
2804
|
+
versionOverride: versionArg,
|
|
2805
|
+
});
|
|
2806
|
+
const outputPath = options.output ?? path.join(process.cwd(), ohosApiDbFilename(resolved.version));
|
|
2807
|
+
if (!options.quiet) {
|
|
2808
|
+
info(`SDK home: ${resolved.sdkHome}`);
|
|
2809
|
+
info(`API version: ${resolved.version}`);
|
|
2810
|
+
info(`Output: ${outputPath}`);
|
|
2811
|
+
}
|
|
2812
|
+
const result = await indexOhosApiDb({
|
|
2813
|
+
sdkHome: resolved.sdkHome,
|
|
2814
|
+
version: resolved.version,
|
|
2815
|
+
outputPath,
|
|
2816
|
+
onProgress: options.quiet
|
|
2817
|
+
? undefined
|
|
2818
|
+
: (progress) => {
|
|
2819
|
+
if (progress.phase === 'arkts-batch') {
|
|
2820
|
+
const label = progress.subphase === 'scene' ? 'Scene' : 'Persist';
|
|
2821
|
+
process.stdout.write(`\r${label}: ${progress.current}/${progress.total} ${progress.currentFile ?? ''}`.padEnd(80));
|
|
2822
|
+
}
|
|
2823
|
+
},
|
|
2824
|
+
});
|
|
2825
|
+
resolved.cleanup?.();
|
|
2826
|
+
if (!options.quiet) {
|
|
2827
|
+
process.stdout.write('\n');
|
|
2828
|
+
if (result.success) {
|
|
2829
|
+
info(`Indexed ${result.filesIndexed} SDK files → ${result.nodesCreated} nodes, ${result.edgesCreated} edges (${result.durationMs}ms)`);
|
|
2830
|
+
}
|
|
2831
|
+
else {
|
|
2832
|
+
error('OHOS API indexing failed');
|
|
2833
|
+
for (const err of result.errors.filter((e) => e.severity === 'error')) {
|
|
2834
|
+
error(err.message);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
if (!result.success) {
|
|
2839
|
+
process.exit(1);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
catch (err) {
|
|
2843
|
+
error(err instanceof Error ? err.message : String(err));
|
|
2844
|
+
process.exit(1);
|
|
2845
|
+
}
|
|
2846
|
+
});
|
|
2307
2847
|
/**
|
|
2308
2848
|
* homegraph upgrade [version]
|
|
2309
2849
|
*
|
|
@@ -2329,6 +2869,7 @@ function main() {
|
|
|
2329
2869
|
method,
|
|
2330
2870
|
resolveLatest: () => up.resolveLatestVersion(),
|
|
2331
2871
|
run: up.defaultRun,
|
|
2872
|
+
capture: up.defaultCapture,
|
|
2332
2873
|
hasCommand: up.hasCommand,
|
|
2333
2874
|
log: (m) => console.log(m),
|
|
2334
2875
|
warn: (m) => warn(m),
|