@bharanidev/bhgraph-win32-arm64 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bhgraph.cmd +1 -0
- package/lib/dist/bin/bhgraph.d.ts +26 -0
- package/lib/dist/bin/bhgraph.d.ts.map +1 -0
- package/lib/dist/bin/bhgraph.js +2207 -0
- package/lib/dist/bin/bhgraph.js.map +1 -0
- package/lib/dist/bin/command-supervision.d.ts +12 -0
- package/lib/dist/bin/command-supervision.d.ts.map +1 -0
- package/lib/dist/bin/command-supervision.js +89 -0
- package/lib/dist/bin/command-supervision.js.map +1 -0
- package/lib/dist/bin/fatal-handler.d.ts +20 -0
- package/lib/dist/bin/fatal-handler.d.ts.map +1 -0
- package/lib/dist/bin/fatal-handler.js +118 -0
- package/lib/dist/bin/fatal-handler.js.map +1 -0
- package/lib/dist/bin/node-version-check.d.ts +37 -0
- package/lib/dist/bin/node-version-check.d.ts.map +1 -0
- package/lib/dist/bin/node-version-check.js +79 -0
- package/lib/dist/bin/node-version-check.js.map +1 -0
- package/lib/dist/bin/uninstall.d.ts +14 -0
- package/lib/dist/bin/uninstall.d.ts.map +1 -0
- package/lib/dist/bin/uninstall.js +36 -0
- package/lib/dist/bin/uninstall.js.map +1 -0
- package/lib/dist/context/formatter.d.ts +30 -0
- package/lib/dist/context/formatter.d.ts.map +1 -0
- package/lib/dist/context/formatter.js +263 -0
- package/lib/dist/context/formatter.js.map +1 -0
- package/lib/dist/context/index.d.ts +119 -0
- package/lib/dist/context/index.d.ts.map +1 -0
- package/lib/dist/context/index.js +1296 -0
- package/lib/dist/context/index.js.map +1 -0
- package/lib/dist/context/markers.d.ts +19 -0
- package/lib/dist/context/markers.d.ts.map +1 -0
- package/lib/dist/context/markers.js +22 -0
- package/lib/dist/context/markers.js.map +1 -0
- package/lib/dist/db/index.d.ts +142 -0
- package/lib/dist/db/index.d.ts.map +1 -0
- package/lib/dist/db/index.js +335 -0
- package/lib/dist/db/index.js.map +1 -0
- package/lib/dist/db/migrations.d.ts +44 -0
- package/lib/dist/db/migrations.d.ts.map +1 -0
- package/lib/dist/db/migrations.js +182 -0
- package/lib/dist/db/migrations.js.map +1 -0
- package/lib/dist/db/queries.d.ts +452 -0
- package/lib/dist/db/queries.d.ts.map +1 -0
- package/lib/dist/db/queries.js +1740 -0
- package/lib/dist/db/queries.js.map +1 -0
- package/lib/dist/db/schema.sql +182 -0
- package/lib/dist/db/sqlite-adapter.d.ts +53 -0
- package/lib/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/lib/dist/db/sqlite-adapter.js +117 -0
- package/lib/dist/db/sqlite-adapter.js.map +1 -0
- package/lib/dist/directory.d.ts +183 -0
- package/lib/dist/directory.d.ts.map +1 -0
- package/lib/dist/directory.js +797 -0
- package/lib/dist/directory.js.map +1 -0
- package/lib/dist/errors.d.ts +136 -0
- package/lib/dist/errors.d.ts.map +1 -0
- package/lib/dist/errors.js +219 -0
- package/lib/dist/errors.js.map +1 -0
- package/lib/dist/extraction/astro-extractor.d.ts +79 -0
- package/lib/dist/extraction/astro-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/astro-extractor.js +320 -0
- package/lib/dist/extraction/astro-extractor.js.map +1 -0
- package/lib/dist/extraction/cfml-extractor.d.ts +107 -0
- package/lib/dist/extraction/cfml-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/cfml-extractor.js +494 -0
- package/lib/dist/extraction/cfml-extractor.js.map +1 -0
- package/lib/dist/extraction/dfm-extractor.d.ts +31 -0
- package/lib/dist/extraction/dfm-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/dfm-extractor.js +151 -0
- package/lib/dist/extraction/dfm-extractor.js.map +1 -0
- package/lib/dist/extraction/extraction-version.d.ts +25 -0
- package/lib/dist/extraction/extraction-version.d.ts.map +1 -0
- package/lib/dist/extraction/extraction-version.js +28 -0
- package/lib/dist/extraction/extraction-version.js.map +1 -0
- package/lib/dist/extraction/function-ref.d.ts +118 -0
- package/lib/dist/extraction/function-ref.d.ts.map +1 -0
- package/lib/dist/extraction/function-ref.js +727 -0
- package/lib/dist/extraction/function-ref.js.map +1 -0
- package/lib/dist/extraction/generated-detection.d.ts +30 -0
- package/lib/dist/extraction/generated-detection.d.ts.map +1 -0
- package/lib/dist/extraction/generated-detection.js +83 -0
- package/lib/dist/extraction/generated-detection.js.map +1 -0
- package/lib/dist/extraction/grammars.d.ts +123 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -0
- package/lib/dist/extraction/grammars.js +589 -0
- package/lib/dist/extraction/grammars.js.map +1 -0
- package/lib/dist/extraction/index.d.ts +232 -0
- package/lib/dist/extraction/index.d.ts.map +1 -0
- package/lib/dist/extraction/index.js +2269 -0
- package/lib/dist/extraction/index.js.map +1 -0
- package/lib/dist/extraction/languages/arkts.d.ts +3 -0
- package/lib/dist/extraction/languages/arkts.d.ts.map +1 -0
- package/lib/dist/extraction/languages/arkts.js +127 -0
- package/lib/dist/extraction/languages/arkts.js.map +1 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts +110 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -0
- package/lib/dist/extraction/languages/c-cpp.js +837 -0
- package/lib/dist/extraction/languages/c-cpp.js.map +1 -0
- package/lib/dist/extraction/languages/cfquery.d.ts +12 -0
- package/lib/dist/extraction/languages/cfquery.d.ts.map +1 -0
- package/lib/dist/extraction/languages/cfquery.js +28 -0
- package/lib/dist/extraction/languages/cfquery.js.map +1 -0
- package/lib/dist/extraction/languages/cfscript.d.ts +3 -0
- package/lib/dist/extraction/languages/cfscript.d.ts.map +1 -0
- package/lib/dist/extraction/languages/cfscript.js +73 -0
- package/lib/dist/extraction/languages/cfscript.js.map +1 -0
- package/lib/dist/extraction/languages/cobol.d.ts +33 -0
- package/lib/dist/extraction/languages/cobol.d.ts.map +1 -0
- package/lib/dist/extraction/languages/cobol.js +499 -0
- package/lib/dist/extraction/languages/cobol.js.map +1 -0
- package/lib/dist/extraction/languages/csharp.d.ts +25 -0
- package/lib/dist/extraction/languages/csharp.d.ts.map +1 -0
- package/lib/dist/extraction/languages/csharp.js +175 -0
- package/lib/dist/extraction/languages/csharp.js.map +1 -0
- package/lib/dist/extraction/languages/dart.d.ts +3 -0
- package/lib/dist/extraction/languages/dart.d.ts.map +1 -0
- package/lib/dist/extraction/languages/dart.js +374 -0
- package/lib/dist/extraction/languages/dart.js.map +1 -0
- package/lib/dist/extraction/languages/erlang.d.ts +3 -0
- package/lib/dist/extraction/languages/erlang.d.ts.map +1 -0
- package/lib/dist/extraction/languages/erlang.js +350 -0
- package/lib/dist/extraction/languages/erlang.js.map +1 -0
- package/lib/dist/extraction/languages/go.d.ts +3 -0
- package/lib/dist/extraction/languages/go.d.ts.map +1 -0
- package/lib/dist/extraction/languages/go.js +111 -0
- package/lib/dist/extraction/languages/go.js.map +1 -0
- package/lib/dist/extraction/languages/index.d.ts +10 -0
- package/lib/dist/extraction/languages/index.d.ts.map +1 -0
- package/lib/dist/extraction/languages/index.js +71 -0
- package/lib/dist/extraction/languages/index.js.map +1 -0
- package/lib/dist/extraction/languages/java.d.ts +3 -0
- package/lib/dist/extraction/languages/java.d.ts.map +1 -0
- package/lib/dist/extraction/languages/java.js +315 -0
- package/lib/dist/extraction/languages/java.js.map +1 -0
- package/lib/dist/extraction/languages/javascript.d.ts +3 -0
- package/lib/dist/extraction/languages/javascript.d.ts.map +1 -0
- package/lib/dist/extraction/languages/javascript.js +106 -0
- package/lib/dist/extraction/languages/javascript.js.map +1 -0
- package/lib/dist/extraction/languages/kotlin.d.ts +3 -0
- package/lib/dist/extraction/languages/kotlin.d.ts.map +1 -0
- package/lib/dist/extraction/languages/kotlin.js +379 -0
- package/lib/dist/extraction/languages/kotlin.js.map +1 -0
- package/lib/dist/extraction/languages/lua.d.ts +3 -0
- package/lib/dist/extraction/languages/lua.d.ts.map +1 -0
- package/lib/dist/extraction/languages/lua.js +150 -0
- package/lib/dist/extraction/languages/lua.js.map +1 -0
- package/lib/dist/extraction/languages/luau.d.ts +3 -0
- package/lib/dist/extraction/languages/luau.d.ts.map +1 -0
- package/lib/dist/extraction/languages/luau.js +37 -0
- package/lib/dist/extraction/languages/luau.js.map +1 -0
- package/lib/dist/extraction/languages/nix.d.ts +3 -0
- package/lib/dist/extraction/languages/nix.d.ts.map +1 -0
- package/lib/dist/extraction/languages/nix.js +294 -0
- package/lib/dist/extraction/languages/nix.js.map +1 -0
- package/lib/dist/extraction/languages/objc.d.ts +3 -0
- package/lib/dist/extraction/languages/objc.d.ts.map +1 -0
- package/lib/dist/extraction/languages/objc.js +175 -0
- package/lib/dist/extraction/languages/objc.js.map +1 -0
- package/lib/dist/extraction/languages/pascal.d.ts +3 -0
- package/lib/dist/extraction/languages/pascal.d.ts.map +1 -0
- package/lib/dist/extraction/languages/pascal.js +77 -0
- package/lib/dist/extraction/languages/pascal.js.map +1 -0
- package/lib/dist/extraction/languages/php.d.ts +3 -0
- package/lib/dist/extraction/languages/php.d.ts.map +1 -0
- package/lib/dist/extraction/languages/php.js +196 -0
- package/lib/dist/extraction/languages/php.js.map +1 -0
- package/lib/dist/extraction/languages/python.d.ts +3 -0
- package/lib/dist/extraction/languages/python.d.ts.map +1 -0
- package/lib/dist/extraction/languages/python.js +56 -0
- package/lib/dist/extraction/languages/python.js.map +1 -0
- package/lib/dist/extraction/languages/r.d.ts +3 -0
- package/lib/dist/extraction/languages/r.d.ts.map +1 -0
- package/lib/dist/extraction/languages/r.js +314 -0
- package/lib/dist/extraction/languages/r.js.map +1 -0
- package/lib/dist/extraction/languages/ruby.d.ts +3 -0
- package/lib/dist/extraction/languages/ruby.d.ts.map +1 -0
- package/lib/dist/extraction/languages/ruby.js +149 -0
- package/lib/dist/extraction/languages/ruby.js.map +1 -0
- package/lib/dist/extraction/languages/rust.d.ts +3 -0
- package/lib/dist/extraction/languages/rust.d.ts.map +1 -0
- package/lib/dist/extraction/languages/rust.js +142 -0
- package/lib/dist/extraction/languages/rust.js.map +1 -0
- package/lib/dist/extraction/languages/scala.d.ts +3 -0
- package/lib/dist/extraction/languages/scala.d.ts.map +1 -0
- package/lib/dist/extraction/languages/scala.js +209 -0
- package/lib/dist/extraction/languages/scala.js.map +1 -0
- package/lib/dist/extraction/languages/solidity.d.ts +3 -0
- package/lib/dist/extraction/languages/solidity.d.ts.map +1 -0
- package/lib/dist/extraction/languages/solidity.js +293 -0
- package/lib/dist/extraction/languages/solidity.js.map +1 -0
- package/lib/dist/extraction/languages/swift.d.ts +3 -0
- package/lib/dist/extraction/languages/swift.d.ts.map +1 -0
- package/lib/dist/extraction/languages/swift.js +152 -0
- package/lib/dist/extraction/languages/swift.js.map +1 -0
- package/lib/dist/extraction/languages/terraform.d.ts +3 -0
- package/lib/dist/extraction/languages/terraform.d.ts.map +1 -0
- package/lib/dist/extraction/languages/terraform.js +641 -0
- package/lib/dist/extraction/languages/terraform.js.map +1 -0
- package/lib/dist/extraction/languages/typescript.d.ts +16 -0
- package/lib/dist/extraction/languages/typescript.d.ts.map +1 -0
- package/lib/dist/extraction/languages/typescript.js +167 -0
- package/lib/dist/extraction/languages/typescript.js.map +1 -0
- package/lib/dist/extraction/languages/vbnet.d.ts +11 -0
- package/lib/dist/extraction/languages/vbnet.d.ts.map +1 -0
- package/lib/dist/extraction/languages/vbnet.js +141 -0
- package/lib/dist/extraction/languages/vbnet.js.map +1 -0
- package/lib/dist/extraction/liquid-extractor.d.ts +59 -0
- package/lib/dist/extraction/liquid-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/liquid-extractor.js +357 -0
- package/lib/dist/extraction/liquid-extractor.js.map +1 -0
- package/lib/dist/extraction/mybatis-extractor.d.ts +68 -0
- package/lib/dist/extraction/mybatis-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/mybatis-extractor.js +300 -0
- package/lib/dist/extraction/mybatis-extractor.js.map +1 -0
- package/lib/dist/extraction/parse-pool.d.ts +126 -0
- package/lib/dist/extraction/parse-pool.d.ts.map +1 -0
- package/lib/dist/extraction/parse-pool.js +319 -0
- package/lib/dist/extraction/parse-pool.js.map +1 -0
- package/lib/dist/extraction/parse-worker.d.ts +8 -0
- package/lib/dist/extraction/parse-worker.d.ts.map +1 -0
- package/lib/dist/extraction/parse-worker.js +97 -0
- package/lib/dist/extraction/parse-worker.js.map +1 -0
- package/lib/dist/extraction/razor-extractor.d.ts +42 -0
- package/lib/dist/extraction/razor-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/razor-extractor.js +285 -0
- package/lib/dist/extraction/razor-extractor.js.map +1 -0
- package/lib/dist/extraction/svelte-extractor.d.ts +56 -0
- package/lib/dist/extraction/svelte-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/svelte-extractor.js +275 -0
- package/lib/dist/extraction/svelte-extractor.js.map +1 -0
- package/lib/dist/extraction/tree-sitter-helpers.d.ts +28 -0
- package/lib/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
- package/lib/dist/extraction/tree-sitter-helpers.js +153 -0
- package/lib/dist/extraction/tree-sitter-helpers.js.map +1 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts +258 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -0
- package/lib/dist/extraction/tree-sitter-types.js +10 -0
- package/lib/dist/extraction/tree-sitter-types.js.map +1 -0
- package/lib/dist/extraction/tree-sitter.d.ts +706 -0
- package/lib/dist/extraction/tree-sitter.d.ts.map +1 -0
- package/lib/dist/extraction/tree-sitter.js +6506 -0
- package/lib/dist/extraction/tree-sitter.js.map +1 -0
- package/lib/dist/extraction/vue-extractor.d.ts +51 -0
- package/lib/dist/extraction/vue-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/vue-extractor.js +254 -0
- package/lib/dist/extraction/vue-extractor.js.map +1 -0
- package/lib/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
- package/lib/dist/extraction/wasm-runtime-flags.d.ts +38 -0
- package/lib/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/lib/dist/extraction/wasm-runtime-flags.js +106 -0
- package/lib/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/lib/dist/graph/index.d.ts +8 -0
- package/lib/dist/graph/index.d.ts.map +1 -0
- package/lib/dist/graph/index.js +13 -0
- package/lib/dist/graph/index.js.map +1 -0
- package/lib/dist/graph/queries.d.ts +106 -0
- package/lib/dist/graph/queries.d.ts.map +1 -0
- package/lib/dist/graph/queries.js +339 -0
- package/lib/dist/graph/queries.js.map +1 -0
- package/lib/dist/graph/traversal.d.ts +127 -0
- package/lib/dist/graph/traversal.d.ts.map +1 -0
- package/lib/dist/graph/traversal.js +599 -0
- package/lib/dist/graph/traversal.js.map +1 -0
- package/lib/dist/index.d.ts +642 -0
- package/lib/dist/index.d.ts.map +1 -0
- package/lib/dist/index.js +1387 -0
- package/lib/dist/index.js.map +1 -0
- package/lib/dist/installer/config-writer.d.ts +28 -0
- package/lib/dist/installer/config-writer.d.ts.map +1 -0
- package/lib/dist/installer/config-writer.js +91 -0
- package/lib/dist/installer/config-writer.js.map +1 -0
- package/lib/dist/installer/index.d.ts +100 -0
- package/lib/dist/installer/index.d.ts.map +1 -0
- package/lib/dist/installer/index.js +526 -0
- package/lib/dist/installer/index.js.map +1 -0
- package/lib/dist/installer/instructions-template.d.ts +41 -0
- package/lib/dist/installer/instructions-template.d.ts.map +1 -0
- package/lib/dist/installer/instructions-template.js +53 -0
- package/lib/dist/installer/instructions-template.js.map +1 -0
- package/lib/dist/installer/targets/antigravity.d.ts +57 -0
- package/lib/dist/installer/targets/antigravity.d.ts.map +1 -0
- package/lib/dist/installer/targets/antigravity.js +308 -0
- package/lib/dist/installer/targets/antigravity.js.map +1 -0
- package/lib/dist/installer/targets/claude.d.ts +62 -0
- package/lib/dist/installer/targets/claude.d.ts.map +1 -0
- package/lib/dist/installer/targets/claude.js +454 -0
- package/lib/dist/installer/targets/claude.js.map +1 -0
- package/lib/dist/installer/targets/codex.d.ts +18 -0
- package/lib/dist/installer/targets/codex.d.ts.map +1 -0
- package/lib/dist/installer/targets/codex.js +185 -0
- package/lib/dist/installer/targets/codex.js.map +1 -0
- package/lib/dist/installer/targets/cursor.d.ts +35 -0
- package/lib/dist/installer/targets/cursor.d.ts.map +1 -0
- package/lib/dist/installer/targets/cursor.js +254 -0
- package/lib/dist/installer/targets/cursor.js.map +1 -0
- package/lib/dist/installer/targets/gemini.d.ts +26 -0
- package/lib/dist/installer/targets/gemini.d.ts.map +1 -0
- package/lib/dist/installer/targets/gemini.js +165 -0
- package/lib/dist/installer/targets/gemini.js.map +1 -0
- package/lib/dist/installer/targets/hermes.d.ts +18 -0
- package/lib/dist/installer/targets/hermes.d.ts.map +1 -0
- package/lib/dist/installer/targets/hermes.js +359 -0
- package/lib/dist/installer/targets/hermes.js.map +1 -0
- package/lib/dist/installer/targets/kiro.d.ts +27 -0
- package/lib/dist/installer/targets/kiro.d.ts.map +1 -0
- package/lib/dist/installer/targets/kiro.js +178 -0
- package/lib/dist/installer/targets/kiro.js.map +1 -0
- package/lib/dist/installer/targets/opencode.d.ts +38 -0
- package/lib/dist/installer/targets/opencode.d.ts.map +1 -0
- package/lib/dist/installer/targets/opencode.js +288 -0
- package/lib/dist/installer/targets/opencode.js.map +1 -0
- package/lib/dist/installer/targets/registry.d.ts +35 -0
- package/lib/dist/installer/targets/registry.d.ts.map +1 -0
- package/lib/dist/installer/targets/registry.js +91 -0
- package/lib/dist/installer/targets/registry.js.map +1 -0
- package/lib/dist/installer/targets/shared.d.ts +101 -0
- package/lib/dist/installer/targets/shared.d.ts.map +1 -0
- package/lib/dist/installer/targets/shared.js +264 -0
- package/lib/dist/installer/targets/shared.js.map +1 -0
- package/lib/dist/installer/targets/toml.d.ts +52 -0
- package/lib/dist/installer/targets/toml.d.ts.map +1 -0
- package/lib/dist/installer/targets/toml.js +147 -0
- package/lib/dist/installer/targets/toml.js.map +1 -0
- package/lib/dist/installer/targets/types.d.ts +108 -0
- package/lib/dist/installer/targets/types.d.ts.map +1 -0
- package/lib/dist/installer/targets/types.js +16 -0
- package/lib/dist/installer/targets/types.js.map +1 -0
- package/lib/dist/mcp/daemon-manager.d.ts +42 -0
- package/lib/dist/mcp/daemon-manager.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-manager.js +129 -0
- package/lib/dist/mcp/daemon-manager.js.map +1 -0
- package/lib/dist/mcp/daemon-paths.d.ts +73 -0
- package/lib/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-paths.js +165 -0
- package/lib/dist/mcp/daemon-paths.js.map +1 -0
- package/lib/dist/mcp/daemon-registry.d.ts +47 -0
- package/lib/dist/mcp/daemon-registry.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-registry.js +233 -0
- package/lib/dist/mcp/daemon-registry.js.map +1 -0
- package/lib/dist/mcp/daemon.d.ts +290 -0
- package/lib/dist/mcp/daemon.d.ts.map +1 -0
- package/lib/dist/mcp/daemon.js +840 -0
- package/lib/dist/mcp/daemon.js.map +1 -0
- package/lib/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
- package/lib/dist/mcp/dynamic-boundaries.js +360 -0
- package/lib/dist/mcp/dynamic-boundaries.js.map +1 -0
- package/lib/dist/mcp/early-ppid.d.ts +26 -0
- package/lib/dist/mcp/early-ppid.d.ts.map +1 -0
- package/lib/dist/mcp/early-ppid.js +29 -0
- package/lib/dist/mcp/early-ppid.js.map +1 -0
- package/lib/dist/mcp/engine.d.ts +122 -0
- package/lib/dist/mcp/engine.d.ts.map +1 -0
- package/lib/dist/mcp/engine.js +350 -0
- package/lib/dist/mcp/engine.js.map +1 -0
- package/lib/dist/mcp/index.d.ts +113 -0
- package/lib/dist/mcp/index.d.ts.map +1 -0
- package/lib/dist/mcp/index.js +498 -0
- package/lib/dist/mcp/index.js.map +1 -0
- package/lib/dist/mcp/liveness-watchdog.d.ts +18 -0
- package/lib/dist/mcp/liveness-watchdog.d.ts.map +1 -0
- package/lib/dist/mcp/liveness-watchdog.js +207 -0
- package/lib/dist/mcp/liveness-watchdog.js.map +1 -0
- package/lib/dist/mcp/ppid-watchdog.d.ts +62 -0
- package/lib/dist/mcp/ppid-watchdog.d.ts.map +1 -0
- package/lib/dist/mcp/ppid-watchdog.js +64 -0
- package/lib/dist/mcp/ppid-watchdog.js.map +1 -0
- package/lib/dist/mcp/proxy.d.ts +87 -0
- package/lib/dist/mcp/proxy.d.ts.map +1 -0
- package/lib/dist/mcp/proxy.js +659 -0
- package/lib/dist/mcp/proxy.js.map +1 -0
- package/lib/dist/mcp/query-pool.d.ts +94 -0
- package/lib/dist/mcp/query-pool.d.ts.map +1 -0
- package/lib/dist/mcp/query-pool.js +297 -0
- package/lib/dist/mcp/query-pool.js.map +1 -0
- package/lib/dist/mcp/query-worker.d.ts +24 -0
- package/lib/dist/mcp/query-worker.d.ts.map +1 -0
- package/lib/dist/mcp/query-worker.js +87 -0
- package/lib/dist/mcp/query-worker.js.map +1 -0
- package/lib/dist/mcp/server-instructions.d.ts +34 -0
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -0
- package/lib/dist/mcp/server-instructions.js +106 -0
- package/lib/dist/mcp/server-instructions.js.map +1 -0
- package/lib/dist/mcp/session.d.ts +79 -0
- package/lib/dist/mcp/session.d.ts.map +1 -0
- package/lib/dist/mcp/session.js +330 -0
- package/lib/dist/mcp/session.js.map +1 -0
- package/lib/dist/mcp/startup-handshake.d.ts +44 -0
- package/lib/dist/mcp/startup-handshake.d.ts.map +1 -0
- package/lib/dist/mcp/startup-handshake.js +73 -0
- package/lib/dist/mcp/startup-handshake.js.map +1 -0
- package/lib/dist/mcp/stdin-teardown.d.ts +27 -0
- package/lib/dist/mcp/stdin-teardown.d.ts.map +1 -0
- package/lib/dist/mcp/stdin-teardown.js +49 -0
- package/lib/dist/mcp/stdin-teardown.js.map +1 -0
- package/lib/dist/mcp/tools.d.ts +613 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -0
- package/lib/dist/mcp/tools.js +4424 -0
- package/lib/dist/mcp/tools.js.map +1 -0
- package/lib/dist/mcp/transport.d.ts +188 -0
- package/lib/dist/mcp/transport.d.ts.map +1 -0
- package/lib/dist/mcp/transport.js +359 -0
- package/lib/dist/mcp/transport.js.map +1 -0
- package/lib/dist/mcp/version.d.ts +19 -0
- package/lib/dist/mcp/version.d.ts.map +1 -0
- package/lib/dist/mcp/version.js +71 -0
- package/lib/dist/mcp/version.js.map +1 -0
- package/lib/dist/project-config.d.ts +94 -0
- package/lib/dist/project-config.d.ts.map +1 -0
- package/lib/dist/project-config.js +374 -0
- package/lib/dist/project-config.js.map +1 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.d.ts +5 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.js +1038 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
- package/lib/dist/resolution/callback-synthesizer.d.ts +15 -0
- package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/lib/dist/resolution/callback-synthesizer.js +3658 -0
- package/lib/dist/resolution/callback-synthesizer.js.map +1 -0
- package/lib/dist/resolution/cooperative-yield.d.ts +32 -0
- package/lib/dist/resolution/cooperative-yield.d.ts.map +1 -0
- package/lib/dist/resolution/cooperative-yield.js +42 -0
- package/lib/dist/resolution/cooperative-yield.js.map +1 -0
- package/lib/dist/resolution/frameworks/astro.d.ts +9 -0
- package/lib/dist/resolution/frameworks/astro.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/astro.js +169 -0
- package/lib/dist/resolution/frameworks/astro.js.map +1 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.js +225 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
- package/lib/dist/resolution/frameworks/cics.d.ts +20 -0
- package/lib/dist/resolution/frameworks/cics.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/cics.js +90 -0
- package/lib/dist/resolution/frameworks/cics.js.map +1 -0
- package/lib/dist/resolution/frameworks/csharp.d.ts +8 -0
- package/lib/dist/resolution/frameworks/csharp.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/csharp.js +241 -0
- package/lib/dist/resolution/frameworks/csharp.js.map +1 -0
- package/lib/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/lib/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/drupal.js +367 -0
- package/lib/dist/resolution/frameworks/drupal.js.map +1 -0
- package/lib/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/lib/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/expo-modules.js +148 -0
- package/lib/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/lib/dist/resolution/frameworks/express.d.ts +8 -0
- package/lib/dist/resolution/frameworks/express.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/express.js +308 -0
- package/lib/dist/resolution/frameworks/express.js.map +1 -0
- package/lib/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/lib/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/fabric.js +354 -0
- package/lib/dist/resolution/frameworks/fabric.js.map +1 -0
- package/lib/dist/resolution/frameworks/go.d.ts +8 -0
- package/lib/dist/resolution/frameworks/go.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/go.js +161 -0
- package/lib/dist/resolution/frameworks/go.js.map +1 -0
- package/lib/dist/resolution/frameworks/goframe.d.ts +41 -0
- package/lib/dist/resolution/frameworks/goframe.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/goframe.js +112 -0
- package/lib/dist/resolution/frameworks/goframe.js.map +1 -0
- package/lib/dist/resolution/frameworks/index.d.ts +50 -0
- package/lib/dist/resolution/frameworks/index.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/index.js +175 -0
- package/lib/dist/resolution/frameworks/index.js.map +1 -0
- package/lib/dist/resolution/frameworks/java.d.ts +8 -0
- package/lib/dist/resolution/frameworks/java.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/java.js +517 -0
- package/lib/dist/resolution/frameworks/java.js.map +1 -0
- package/lib/dist/resolution/frameworks/laravel.d.ts +13 -0
- package/lib/dist/resolution/frameworks/laravel.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/laravel.js +257 -0
- package/lib/dist/resolution/frameworks/laravel.js.map +1 -0
- package/lib/dist/resolution/frameworks/nestjs.d.ts +26 -0
- package/lib/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/nestjs.js +698 -0
- package/lib/dist/resolution/frameworks/nestjs.js.map +1 -0
- package/lib/dist/resolution/frameworks/play.d.ts +19 -0
- package/lib/dist/resolution/frameworks/play.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/play.js +111 -0
- package/lib/dist/resolution/frameworks/play.js.map +1 -0
- package/lib/dist/resolution/frameworks/python.d.ts +10 -0
- package/lib/dist/resolution/frameworks/python.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/python.js +400 -0
- package/lib/dist/resolution/frameworks/python.js.map +1 -0
- package/lib/dist/resolution/frameworks/react-native.d.ts +3 -0
- package/lib/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/react-native.js +410 -0
- package/lib/dist/resolution/frameworks/react-native.js.map +1 -0
- package/lib/dist/resolution/frameworks/react.d.ts +8 -0
- package/lib/dist/resolution/frameworks/react.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/react.js +334 -0
- package/lib/dist/resolution/frameworks/react.js.map +1 -0
- package/lib/dist/resolution/frameworks/ruby.d.ts +8 -0
- package/lib/dist/resolution/frameworks/ruby.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/ruby.js +302 -0
- package/lib/dist/resolution/frameworks/ruby.js.map +1 -0
- package/lib/dist/resolution/frameworks/rust.d.ts +8 -0
- package/lib/dist/resolution/frameworks/rust.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/rust.js +304 -0
- package/lib/dist/resolution/frameworks/rust.js.map +1 -0
- package/lib/dist/resolution/frameworks/svelte.d.ts +9 -0
- package/lib/dist/resolution/frameworks/svelte.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/svelte.js +253 -0
- package/lib/dist/resolution/frameworks/svelte.js.map +1 -0
- package/lib/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/lib/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/swift-objc.js +252 -0
- package/lib/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/lib/dist/resolution/frameworks/swift.d.ts +10 -0
- package/lib/dist/resolution/frameworks/swift.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/swift.js +400 -0
- package/lib/dist/resolution/frameworks/swift.js.map +1 -0
- package/lib/dist/resolution/frameworks/terraform.d.ts +38 -0
- package/lib/dist/resolution/frameworks/terraform.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/terraform.js +277 -0
- package/lib/dist/resolution/frameworks/terraform.js.map +1 -0
- package/lib/dist/resolution/frameworks/vue.d.ts +9 -0
- package/lib/dist/resolution/frameworks/vue.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/vue.js +303 -0
- package/lib/dist/resolution/frameworks/vue.js.map +1 -0
- package/lib/dist/resolution/go-module.d.ts +26 -0
- package/lib/dist/resolution/go-module.d.ts.map +1 -0
- package/lib/dist/resolution/go-module.js +78 -0
- package/lib/dist/resolution/go-module.js.map +1 -0
- package/lib/dist/resolution/goframe-synthesizer.d.ts +28 -0
- package/lib/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
- package/lib/dist/resolution/goframe-synthesizer.js +158 -0
- package/lib/dist/resolution/goframe-synthesizer.js.map +1 -0
- package/lib/dist/resolution/import-resolver.d.ts +85 -0
- package/lib/dist/resolution/import-resolver.d.ts.map +1 -0
- package/lib/dist/resolution/import-resolver.js +1974 -0
- package/lib/dist/resolution/import-resolver.js.map +1 -0
- package/lib/dist/resolution/index.d.ts +238 -0
- package/lib/dist/resolution/index.d.ts.map +1 -0
- package/lib/dist/resolution/index.js +1633 -0
- package/lib/dist/resolution/index.js.map +1 -0
- package/lib/dist/resolution/lru-cache.d.ts +24 -0
- package/lib/dist/resolution/lru-cache.d.ts.map +1 -0
- package/lib/dist/resolution/lru-cache.js +62 -0
- package/lib/dist/resolution/lru-cache.js.map +1 -0
- package/lib/dist/resolution/name-matcher.d.ts +112 -0
- package/lib/dist/resolution/name-matcher.d.ts.map +1 -0
- package/lib/dist/resolution/name-matcher.js +1701 -0
- package/lib/dist/resolution/name-matcher.js.map +1 -0
- package/lib/dist/resolution/path-aliases.d.ts +68 -0
- package/lib/dist/resolution/path-aliases.d.ts.map +1 -0
- package/lib/dist/resolution/path-aliases.js +238 -0
- package/lib/dist/resolution/path-aliases.js.map +1 -0
- package/lib/dist/resolution/strip-comments.d.ts +27 -0
- package/lib/dist/resolution/strip-comments.d.ts.map +1 -0
- package/lib/dist/resolution/strip-comments.js +492 -0
- package/lib/dist/resolution/strip-comments.js.map +1 -0
- package/lib/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/lib/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/lib/dist/resolution/swift-objc-bridge.js +256 -0
- package/lib/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/lib/dist/resolution/types.d.ts +253 -0
- package/lib/dist/resolution/types.d.ts.map +1 -0
- package/lib/dist/resolution/types.js +8 -0
- package/lib/dist/resolution/types.js.map +1 -0
- package/lib/dist/resolution/workspace-packages.d.ts +58 -0
- package/lib/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/lib/dist/resolution/workspace-packages.js +346 -0
- package/lib/dist/resolution/workspace-packages.js.map +1 -0
- package/lib/dist/search/identifier-segments.d.ts +60 -0
- package/lib/dist/search/identifier-segments.d.ts.map +1 -0
- package/lib/dist/search/identifier-segments.js +176 -0
- package/lib/dist/search/identifier-segments.js.map +1 -0
- package/lib/dist/search/query-parser.d.ts +57 -0
- package/lib/dist/search/query-parser.d.ts.map +1 -0
- package/lib/dist/search/query-parser.js +177 -0
- package/lib/dist/search/query-parser.js.map +1 -0
- package/lib/dist/search/query-utils.d.ts +87 -0
- package/lib/dist/search/query-utils.d.ts.map +1 -0
- package/lib/dist/search/query-utils.js +449 -0
- package/lib/dist/search/query-utils.js.map +1 -0
- package/lib/dist/sync/git-hooks.d.ts +45 -0
- package/lib/dist/sync/git-hooks.d.ts.map +1 -0
- package/lib/dist/sync/git-hooks.js +227 -0
- package/lib/dist/sync/git-hooks.js.map +1 -0
- package/lib/dist/sync/index.d.ts +19 -0
- package/lib/dist/sync/index.d.ts.map +1 -0
- package/lib/dist/sync/index.js +35 -0
- package/lib/dist/sync/index.js.map +1 -0
- package/lib/dist/sync/watch-policy.d.ts +48 -0
- package/lib/dist/sync/watch-policy.d.ts.map +1 -0
- package/lib/dist/sync/watch-policy.js +124 -0
- package/lib/dist/sync/watch-policy.js.map +1 -0
- package/lib/dist/sync/watcher.d.ts +355 -0
- package/lib/dist/sync/watcher.d.ts.map +1 -0
- package/lib/dist/sync/watcher.js +848 -0
- package/lib/dist/sync/watcher.js.map +1 -0
- package/lib/dist/sync/worktree.d.ts +63 -0
- package/lib/dist/sync/worktree.d.ts.map +1 -0
- package/lib/dist/sync/worktree.js +182 -0
- package/lib/dist/sync/worktree.js.map +1 -0
- package/lib/dist/telemetry/index.d.ts +143 -0
- package/lib/dist/telemetry/index.d.ts.map +1 -0
- package/lib/dist/telemetry/index.js +541 -0
- package/lib/dist/telemetry/index.js.map +1 -0
- package/lib/dist/types.d.ts +415 -0
- package/lib/dist/types.d.ts.map +1 -0
- package/lib/dist/types.js +91 -0
- package/lib/dist/types.js.map +1 -0
- package/lib/dist/ui/glyphs.d.ts +42 -0
- package/lib/dist/ui/glyphs.d.ts.map +1 -0
- package/lib/dist/ui/glyphs.js +78 -0
- package/lib/dist/ui/glyphs.js.map +1 -0
- package/lib/dist/ui/shimmer-progress.d.ts +11 -0
- package/lib/dist/ui/shimmer-progress.d.ts.map +1 -0
- package/lib/dist/ui/shimmer-progress.js +90 -0
- package/lib/dist/ui/shimmer-progress.js.map +1 -0
- package/lib/dist/ui/shimmer-worker.d.ts +2 -0
- package/lib/dist/ui/shimmer-worker.d.ts.map +1 -0
- package/lib/dist/ui/shimmer-worker.js +118 -0
- package/lib/dist/ui/shimmer-worker.js.map +1 -0
- package/lib/dist/ui/types.d.ts +17 -0
- package/lib/dist/ui/types.d.ts.map +1 -0
- package/lib/dist/ui/types.js +3 -0
- package/lib/dist/ui/types.js.map +1 -0
- package/lib/dist/upgrade/index.d.ts +132 -0
- package/lib/dist/upgrade/index.d.ts.map +1 -0
- package/lib/dist/upgrade/index.js +498 -0
- package/lib/dist/upgrade/index.js.map +1 -0
- package/lib/dist/utils.d.ts +224 -0
- package/lib/dist/utils.d.ts.map +1 -0
- package/lib/dist/utils.js +583 -0
- package/lib/dist/utils.js.map +1 -0
- package/lib/node_modules/.package-lock.json +125 -0
- package/lib/node_modules/@clack/core/CHANGELOG.md +348 -0
- package/lib/node_modules/@clack/core/LICENSE +9 -0
- package/lib/node_modules/@clack/core/README.md +22 -0
- package/lib/node_modules/@clack/core/dist/index.d.mts +363 -0
- package/lib/node_modules/@clack/core/dist/index.mjs +17 -0
- package/lib/node_modules/@clack/core/dist/index.mjs.map +1 -0
- package/lib/node_modules/@clack/core/package.json +63 -0
- package/lib/node_modules/@clack/prompts/CHANGELOG.md +591 -0
- package/lib/node_modules/@clack/prompts/LICENSE +9 -0
- package/lib/node_modules/@clack/prompts/README.md +375 -0
- package/lib/node_modules/@clack/prompts/dist/index.d.mts +396 -0
- package/lib/node_modules/@clack/prompts/dist/index.mjs +144 -0
- package/lib/node_modules/@clack/prompts/dist/index.mjs.map +1 -0
- package/lib/node_modules/@clack/prompts/package.json +68 -0
- package/lib/node_modules/commander/LICENSE +22 -0
- package/lib/node_modules/commander/Readme.md +1176 -0
- package/lib/node_modules/commander/esm.mjs +16 -0
- package/lib/node_modules/commander/index.js +24 -0
- package/lib/node_modules/commander/lib/argument.js +150 -0
- package/lib/node_modules/commander/lib/command.js +2777 -0
- package/lib/node_modules/commander/lib/error.js +39 -0
- package/lib/node_modules/commander/lib/help.js +747 -0
- package/lib/node_modules/commander/lib/option.js +380 -0
- package/lib/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/lib/node_modules/commander/package-support.json +19 -0
- package/lib/node_modules/commander/package.json +82 -0
- package/lib/node_modules/commander/typings/esm.d.mts +3 -0
- package/lib/node_modules/commander/typings/index.d.ts +1113 -0
- package/lib/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
- package/lib/node_modules/fast-string-truncated-width/dist/index.js +111 -0
- package/lib/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
- package/lib/node_modules/fast-string-truncated-width/dist/types.js +2 -0
- package/lib/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
- package/lib/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
- package/lib/node_modules/fast-string-truncated-width/license +21 -0
- package/lib/node_modules/fast-string-truncated-width/package.json +35 -0
- package/lib/node_modules/fast-string-truncated-width/readme.md +59 -0
- package/lib/node_modules/fast-string-width/dist/index.d.ts +4 -0
- package/lib/node_modules/fast-string-width/dist/index.js +14 -0
- package/lib/node_modules/fast-string-width/license +21 -0
- package/lib/node_modules/fast-string-width/package.json +34 -0
- package/lib/node_modules/fast-string-width/readme.md +42 -0
- package/lib/node_modules/fast-wrap-ansi/LICENSE +23 -0
- package/lib/node_modules/fast-wrap-ansi/README.md +26 -0
- package/lib/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
- package/lib/node_modules/fast-wrap-ansi/lib/main.js +219 -0
- package/lib/node_modules/fast-wrap-ansi/lib/main.js.map +1 -0
- package/lib/node_modules/fast-wrap-ansi/package.json +51 -0
- package/lib/node_modules/ignore/LICENSE-MIT +21 -0
- package/lib/node_modules/ignore/README.md +452 -0
- package/lib/node_modules/ignore/index.d.ts +81 -0
- package/lib/node_modules/ignore/index.js +784 -0
- package/lib/node_modules/ignore/legacy.js +681 -0
- package/lib/node_modules/ignore/package.json +87 -0
- package/lib/node_modules/jsonc-parser/CHANGELOG.md +76 -0
- package/lib/node_modules/jsonc-parser/LICENSE.md +21 -0
- package/lib/node_modules/jsonc-parser/README.md +364 -0
- package/lib/node_modules/jsonc-parser/SECURITY.md +41 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
- package/lib/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
- package/lib/node_modules/jsonc-parser/lib/esm/main.js +178 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
- package/lib/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
- package/lib/node_modules/jsonc-parser/lib/umd/main.js +194 -0
- package/lib/node_modules/jsonc-parser/package.json +37 -0
- package/lib/node_modules/picomatch/LICENSE +21 -0
- package/lib/node_modules/picomatch/README.md +738 -0
- package/lib/node_modules/picomatch/index.js +17 -0
- package/lib/node_modules/picomatch/lib/constants.js +180 -0
- package/lib/node_modules/picomatch/lib/parse.js +1085 -0
- package/lib/node_modules/picomatch/lib/picomatch.js +341 -0
- package/lib/node_modules/picomatch/lib/scan.js +391 -0
- package/lib/node_modules/picomatch/lib/utils.js +72 -0
- package/lib/node_modules/picomatch/package.json +83 -0
- package/lib/node_modules/picomatch/posix.js +3 -0
- package/lib/node_modules/sisteransi/license +21 -0
- package/lib/node_modules/sisteransi/package.json +34 -0
- package/lib/node_modules/sisteransi/readme.md +113 -0
- package/lib/node_modules/sisteransi/src/index.js +58 -0
- package/lib/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
- package/lib/node_modules/tree-sitter-wasms/LICENSE +24 -0
- package/lib/node_modules/tree-sitter-wasms/README.md +23 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-bash.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-c.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-cpp.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-css.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-dart.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-elisp.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-elixir.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-elm.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-embedded_template.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-html.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-json.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-lua.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-objc.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-ocaml.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-ql.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-rescript.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-solidity.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-swift.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-systemrdl.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-tlaplus.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-toml.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-vue.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-yaml.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-zig.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/package.json +64 -0
- package/lib/node_modules/web-tree-sitter/LICENSE +21 -0
- package/lib/node_modules/web-tree-sitter/README.md +269 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.cjs +4558 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.cjs.map +7 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.js +4516 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.js.map +7 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.wasm +0 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.wasm.map +31 -0
- package/lib/node_modules/web-tree-sitter/lib/alloc.c +48 -0
- package/lib/node_modules/web-tree-sitter/lib/alloc.h +41 -0
- package/lib/node_modules/web-tree-sitter/lib/array.h +291 -0
- package/lib/node_modules/web-tree-sitter/lib/atomic.h +68 -0
- package/lib/node_modules/web-tree-sitter/lib/clock.h +146 -0
- package/lib/node_modules/web-tree-sitter/lib/error_costs.h +11 -0
- package/lib/node_modules/web-tree-sitter/lib/get_changed_ranges.c +523 -0
- package/lib/node_modules/web-tree-sitter/lib/get_changed_ranges.h +36 -0
- package/lib/node_modules/web-tree-sitter/lib/host.h +21 -0
- package/lib/node_modules/web-tree-sitter/lib/language.c +293 -0
- package/lib/node_modules/web-tree-sitter/lib/language.h +293 -0
- package/lib/node_modules/web-tree-sitter/lib/length.h +52 -0
- package/lib/node_modules/web-tree-sitter/lib/lexer.c +483 -0
- package/lib/node_modules/web-tree-sitter/lib/lexer.h +54 -0
- package/lib/node_modules/web-tree-sitter/lib/lib.c +12 -0
- package/lib/node_modules/web-tree-sitter/lib/node.c +875 -0
- package/lib/node_modules/web-tree-sitter/lib/parser.c +2297 -0
- package/lib/node_modules/web-tree-sitter/lib/parser.h +286 -0
- package/lib/node_modules/web-tree-sitter/lib/point.h +48 -0
- package/lib/node_modules/web-tree-sitter/lib/query.c +4347 -0
- package/lib/node_modules/web-tree-sitter/lib/reduce_action.h +34 -0
- package/lib/node_modules/web-tree-sitter/lib/reusable_node.h +95 -0
- package/lib/node_modules/web-tree-sitter/lib/stack.c +912 -0
- package/lib/node_modules/web-tree-sitter/lib/stack.h +133 -0
- package/lib/node_modules/web-tree-sitter/lib/subtree.c +1034 -0
- package/lib/node_modules/web-tree-sitter/lib/subtree.h +399 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.c +987 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.cjs +2988 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.wasm +0 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map +1 -0
- package/lib/node_modules/web-tree-sitter/lib/tree.c +170 -0
- package/lib/node_modules/web-tree-sitter/lib/tree.h +31 -0
- package/lib/node_modules/web-tree-sitter/lib/tree_cursor.c +716 -0
- package/lib/node_modules/web-tree-sitter/lib/tree_cursor.h +48 -0
- package/lib/node_modules/web-tree-sitter/lib/ts_assert.h +11 -0
- package/lib/node_modules/web-tree-sitter/lib/unicode.h +75 -0
- package/lib/node_modules/web-tree-sitter/lib/wasm_store.c +1937 -0
- package/lib/node_modules/web-tree-sitter/lib/wasm_store.h +31 -0
- package/lib/node_modules/web-tree-sitter/package.json +98 -0
- package/lib/node_modules/web-tree-sitter/src/bindings.ts +26 -0
- package/lib/node_modules/web-tree-sitter/src/constants.ts +133 -0
- package/lib/node_modules/web-tree-sitter/src/index.ts +31 -0
- package/lib/node_modules/web-tree-sitter/src/language.ts +291 -0
- package/lib/node_modules/web-tree-sitter/src/lookahead_iterator.ts +75 -0
- package/lib/node_modules/web-tree-sitter/src/marshal.ts +176 -0
- package/lib/node_modules/web-tree-sitter/src/node.ts +646 -0
- package/lib/node_modules/web-tree-sitter/src/parser.ts +325 -0
- package/lib/node_modules/web-tree-sitter/src/query.ts +973 -0
- package/lib/node_modules/web-tree-sitter/src/tree.ts +145 -0
- package/lib/node_modules/web-tree-sitter/src/tree_cursor.ts +318 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.cjs +4031 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.cjs.map +7 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.js +3980 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.js.map +7 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.wasm.map +30 -0
- package/lib/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1030 -0
- package/lib/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +56 -0
- package/lib/package.json +64 -0
- package/node.exe +0 -0
- package/package.json +17 -0
|
@@ -0,0 +1,2207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* BHGraph CLI
|
|
5
|
+
*
|
|
6
|
+
* Command-line interface for BHGraph code intelligence.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bhgraph Run interactive installer (when no args)
|
|
10
|
+
* bhgraph install Run interactive installer
|
|
11
|
+
* bhgraph uninstall Remove BHGraph from your agents
|
|
12
|
+
* bhgraph init [path] Initialize BHGraph in a project
|
|
13
|
+
* bhgraph uninit [path] Remove BHGraph from a project
|
|
14
|
+
* bhgraph index [path] Index all files in the project
|
|
15
|
+
* bhgraph sync [path] Sync changes since last index
|
|
16
|
+
* bhgraph status [path] Show index status
|
|
17
|
+
* bhgraph query <search> Search for symbols
|
|
18
|
+
* bhgraph files [options] Show project file structure
|
|
19
|
+
* bhgraph context <task> Build context for a task
|
|
20
|
+
* bhgraph callers <symbol> Find what calls a function/method
|
|
21
|
+
* bhgraph callees <symbol> Find what a function/method calls
|
|
22
|
+
* bhgraph impact <symbol> Analyze what code is affected by changing a symbol
|
|
23
|
+
* bhgraph affected [files] Find test files affected by changes
|
|
24
|
+
* bhgraph upgrade [version] Update BHGraph to the latest release
|
|
25
|
+
*/
|
|
26
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
29
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
30
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
31
|
+
}
|
|
32
|
+
Object.defineProperty(o, k2, desc);
|
|
33
|
+
}) : (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
o[k2] = m[k];
|
|
36
|
+
}));
|
|
37
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
38
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
39
|
+
}) : function(o, v) {
|
|
40
|
+
o["default"] = v;
|
|
41
|
+
});
|
|
42
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
43
|
+
var ownKeys = function(o) {
|
|
44
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
45
|
+
var ar = [];
|
|
46
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
47
|
+
return ar;
|
|
48
|
+
};
|
|
49
|
+
return ownKeys(o);
|
|
50
|
+
};
|
|
51
|
+
return function (mod) {
|
|
52
|
+
if (mod && mod.__esModule) return mod;
|
|
53
|
+
var result = {};
|
|
54
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
55
|
+
__setModuleDefault(result, mod);
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
})();
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
// FIRST import, before anything else loads: capture process.ppid while our
|
|
61
|
+
// launcher is (almost certainly) still alive. A launcher killed mid-startup
|
|
62
|
+
// otherwise blinds the PPID watchdog forever (#1185) — see early-ppid.ts.
|
|
63
|
+
require("../mcp/early-ppid");
|
|
64
|
+
const commander_1 = require("commander");
|
|
65
|
+
const path = __importStar(require("path"));
|
|
66
|
+
const fs = __importStar(require("fs"));
|
|
67
|
+
const directory_1 = require("../directory");
|
|
68
|
+
const identifier_segments_1 = require("../search/identifier-segments");
|
|
69
|
+
const worktree_1 = require("../sync/worktree");
|
|
70
|
+
const shimmer_progress_1 = require("../ui/shimmer-progress");
|
|
71
|
+
const glyphs_1 = require("../ui/glyphs");
|
|
72
|
+
const node_version_check_1 = require("./node-version-check");
|
|
73
|
+
const fatal_handler_1 = require("./fatal-handler");
|
|
74
|
+
const wasm_runtime_flags_1 = require("../extraction/wasm-runtime-flags");
|
|
75
|
+
const command_supervision_1 = require("./command-supervision");
|
|
76
|
+
const extraction_version_1 = require("../extraction/extraction-version");
|
|
77
|
+
const telemetry_1 = require("../telemetry");
|
|
78
|
+
// Lazy-load heavy modules (BHGraph, runInstaller) to keep CLI startup fast.
|
|
79
|
+
async function loadBHGraph() {
|
|
80
|
+
try {
|
|
81
|
+
return await Promise.resolve().then(() => __importStar(require('../index')));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
85
|
+
console.error(`\x1b[31m${(0, glyphs_1.getGlyphs)().err}\x1b[0m Failed to load BHGraph modules.`);
|
|
86
|
+
console.error(`\n Node: ${process.version} Platform: ${process.platform} ${process.arch}`);
|
|
87
|
+
console.error(`\n Error: ${msg}`);
|
|
88
|
+
console.error('\n Try reinstalling with: npm install -g bhgraph\n');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Dynamic import helper — tsc compiles import() to require() in CJS mode,
|
|
93
|
+
// which fails for ESM-only packages. This bypasses the transformation.
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
95
|
+
const importESM = new Function('specifier', 'return import(specifier)');
|
|
96
|
+
// Block BHGraph on Node.js 25.x — V8's turboshaft WASM JIT has a Zone
|
|
97
|
+
// allocator bug that reliably crashes when compiling tree-sitter
|
|
98
|
+
// grammars (see #54, #81, #140). The previous behaviour was a soft
|
|
99
|
+
// console.warn that scrolls off-screen before the OOM crash 30 seconds
|
|
100
|
+
// later, leading to a steady stream of "what is this OOM" reports.
|
|
101
|
+
// Hard-exit before any WASM work; allow override via env var for users
|
|
102
|
+
// who patched V8 themselves or want to test a future fix.
|
|
103
|
+
const nodeVersion = process.versions.node;
|
|
104
|
+
const nodeMajor = parseInt(nodeVersion.split('.')[0] ?? '0', 10);
|
|
105
|
+
if (nodeMajor >= 25) {
|
|
106
|
+
process.stderr.write((0, node_version_check_1.buildNode25BlockBanner)(nodeVersion) + '\n');
|
|
107
|
+
if (!process.env.BHGRAPH_ALLOW_UNSAFE_NODE) {
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
// Override active — banner shown for visibility, continuing.
|
|
111
|
+
}
|
|
112
|
+
// Enforce the supported Node floor. `engines` in package.json only *warns* on
|
|
113
|
+
// install (unless engine-strict), so hard-block here to actually keep users off
|
|
114
|
+
// unsupported versions. Mirrors the 25+ block above. See package.json `engines`.
|
|
115
|
+
if (nodeMajor < node_version_check_1.MIN_NODE_MAJOR) {
|
|
116
|
+
process.stderr.write((0, node_version_check_1.buildNodeTooOldBanner)(nodeVersion) + '\n');
|
|
117
|
+
if (!process.env.BHGRAPH_ALLOW_UNSAFE_NODE) {
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
// Override active — banner shown for visibility, continuing.
|
|
121
|
+
}
|
|
122
|
+
// Re-exec with V8's `--liftoff-only` if it isn't already set, so tree-sitter's
|
|
123
|
+
// large WASM grammars never hit the turboshaft Zone OOM (`Fatal process out of
|
|
124
|
+
// memory: Zone`) on Node >= 22. No-op under the bundled launcher, which already
|
|
125
|
+
// passes the flag. Must run before any grammar (in the parse worker, which
|
|
126
|
+
// inherits this process's flags) is compiled. See ../extraction/wasm-runtime-flags.
|
|
127
|
+
(0, wasm_runtime_flags_1.relaunchWithWasmRuntimeFlagsIfNeeded)(__filename);
|
|
128
|
+
// Last-resort fatal handlers: log a bounded line and exit non-zero. A fault
|
|
129
|
+
// that reaches here escaped every boundary, so the process is in an undefined
|
|
130
|
+
// state — keeping it alive is what let the detached MCP daemon orphan and pin a
|
|
131
|
+
// CPU core with no recovery (#799, #850). Installed before the command branch
|
|
132
|
+
// so it also covers a synchronous throw during startup. See ./fatal-handler.
|
|
133
|
+
(0, fatal_handler_1.installFatalHandlers)();
|
|
134
|
+
// Check if running with no arguments - run installer
|
|
135
|
+
if (process.argv.length === 2) {
|
|
136
|
+
Promise.resolve().then(() => __importStar(require('../installer'))).then(({ runInstaller }) => runInstaller()).catch((err) => {
|
|
137
|
+
console.error('Installation failed:', err instanceof Error ? err.message : String(err));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Normal CLI flow
|
|
143
|
+
main();
|
|
144
|
+
}
|
|
145
|
+
function main() {
|
|
146
|
+
const program = new commander_1.Command();
|
|
147
|
+
// Version from package.json
|
|
148
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8'));
|
|
149
|
+
// Make the version trivial to reach. commander's `.version()` (below) wires up
|
|
150
|
+
// `--version` and `-V`; intercept the spellings it can't — lowercase `-v` and
|
|
151
|
+
// single-dash `-version` — before any parsing. (commander's version short flag
|
|
152
|
+
// is the capital `-V`, and its parser rejects a multi-character single-dash
|
|
153
|
+
// flag.) The bare `bhgraph version` subcommand is registered further down so
|
|
154
|
+
// the affordance also shows up in `bhgraph --help`.
|
|
155
|
+
const firstArg = process.argv[2];
|
|
156
|
+
if (firstArg === '-v' || firstArg === '-version') {
|
|
157
|
+
console.log(packageJson.version);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// =============================================================================
|
|
161
|
+
// ANSI Color Helpers (avoid chalk ESM issues)
|
|
162
|
+
// =============================================================================
|
|
163
|
+
const colors = {
|
|
164
|
+
reset: '\x1b[0m',
|
|
165
|
+
bold: '\x1b[1m',
|
|
166
|
+
dim: '\x1b[2m',
|
|
167
|
+
red: '\x1b[31m',
|
|
168
|
+
green: '\x1b[32m',
|
|
169
|
+
yellow: '\x1b[33m',
|
|
170
|
+
blue: '\x1b[34m',
|
|
171
|
+
cyan: '\x1b[36m',
|
|
172
|
+
white: '\x1b[37m',
|
|
173
|
+
gray: '\x1b[90m',
|
|
174
|
+
};
|
|
175
|
+
const chalk = {
|
|
176
|
+
bold: (s) => `${colors.bold}${s}${colors.reset}`,
|
|
177
|
+
dim: (s) => `${colors.dim}${s}${colors.reset}`,
|
|
178
|
+
red: (s) => `${colors.red}${s}${colors.reset}`,
|
|
179
|
+
green: (s) => `${colors.green}${s}${colors.reset}`,
|
|
180
|
+
yellow: (s) => `${colors.yellow}${s}${colors.reset}`,
|
|
181
|
+
blue: (s) => `${colors.blue}${s}${colors.reset}`,
|
|
182
|
+
cyan: (s) => `${colors.cyan}${s}${colors.reset}`,
|
|
183
|
+
white: (s) => `${colors.white}${s}${colors.reset}`,
|
|
184
|
+
gray: (s) => `${colors.gray}${s}${colors.reset}`,
|
|
185
|
+
};
|
|
186
|
+
program
|
|
187
|
+
.name('bhgraph')
|
|
188
|
+
.description('Code intelligence and knowledge graph for any codebase')
|
|
189
|
+
.version(packageJson.version);
|
|
190
|
+
// Anonymous usage telemetry (see TELEMETRY.md): record the invoked subcommand
|
|
191
|
+
// NAME only — never arguments or paths. Counts buffer locally; network sends
|
|
192
|
+
// piggyback on commands that run long anyway (quick commands only append to
|
|
193
|
+
// the local buffer at exit, costing nothing).
|
|
194
|
+
// install/uninstall are absent on purpose: the installer flushes at its own
|
|
195
|
+
// end, AFTER its consent prompt — a flush here would fire the first-run
|
|
196
|
+
// notice before the user ever sees the toggle.
|
|
197
|
+
const TELEMETRY_FLUSH_COMMANDS = new Set(['init', 'uninit', 'index', 'sync', 'upgrade']);
|
|
198
|
+
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
199
|
+
try {
|
|
200
|
+
// The detached daemon re-invokes `serve --mcp` internally — not a user action.
|
|
201
|
+
if (process.env.BHGRAPH_DAEMON_INTERNAL)
|
|
202
|
+
return;
|
|
203
|
+
const name = actionCommand.name();
|
|
204
|
+
if (name === 'telemetry')
|
|
205
|
+
return; // managing telemetry is not usage
|
|
206
|
+
(0, telemetry_1.getTelemetry)().recordUsage('cli_command', name, true);
|
|
207
|
+
if (TELEMETRY_FLUSH_COMMANDS.has(name))
|
|
208
|
+
(0, telemetry_1.getTelemetry)().maybeFlush();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
/* telemetry must never break the CLI */
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
// =============================================================================
|
|
215
|
+
// Helper Functions
|
|
216
|
+
// =============================================================================
|
|
217
|
+
/**
|
|
218
|
+
* Resolve project path from argument or current directory
|
|
219
|
+
* Walks up parent directories to find nearest initialized BHGraph project
|
|
220
|
+
* (must have .bhgraph/bhgraph.db, not just .bhgraph/lessons.db)
|
|
221
|
+
*/
|
|
222
|
+
function resolveProjectPath(pathArg) {
|
|
223
|
+
const absolutePath = path.resolve(pathArg || process.cwd());
|
|
224
|
+
// If exact path is initialized (has bhgraph.db), use it
|
|
225
|
+
if ((0, directory_1.isInitialized)(absolutePath)) {
|
|
226
|
+
return absolutePath;
|
|
227
|
+
}
|
|
228
|
+
// Walk up to find nearest parent with BHGraph initialized
|
|
229
|
+
// Note: findNearestBHGraphRoot finds any .bhgraph folder, but we need one with bhgraph.db
|
|
230
|
+
let current = absolutePath;
|
|
231
|
+
const root = path.parse(current).root;
|
|
232
|
+
while (current !== root) {
|
|
233
|
+
const parent = path.dirname(current);
|
|
234
|
+
if (parent === current)
|
|
235
|
+
break;
|
|
236
|
+
current = parent;
|
|
237
|
+
if ((0, directory_1.isInitialized)(current)) {
|
|
238
|
+
return current;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Not found - return original path (will fail later with helpful error)
|
|
242
|
+
return absolutePath;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Format a number with commas
|
|
246
|
+
*/
|
|
247
|
+
function formatNumber(n) {
|
|
248
|
+
return n.toLocaleString();
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Format duration in milliseconds to human readable
|
|
252
|
+
*/
|
|
253
|
+
function formatDuration(ms) {
|
|
254
|
+
if (ms < 1000) {
|
|
255
|
+
return `${ms}ms`;
|
|
256
|
+
}
|
|
257
|
+
const seconds = ms / 1000;
|
|
258
|
+
if (seconds < 60) {
|
|
259
|
+
return `${seconds.toFixed(1)}s`;
|
|
260
|
+
}
|
|
261
|
+
const minutes = Math.floor(seconds / 60);
|
|
262
|
+
const remainingSeconds = seconds % 60;
|
|
263
|
+
return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
|
|
264
|
+
}
|
|
265
|
+
// Shimmer progress renderer (runs in a worker thread for smooth animation)
|
|
266
|
+
// Imported at top of file from '../ui/shimmer-progress'
|
|
267
|
+
/**
|
|
268
|
+
* Create a plain-text progress callback for --verbose mode.
|
|
269
|
+
* No animations, no ANSI tricks — just timestamped lines to stdout.
|
|
270
|
+
*/
|
|
271
|
+
function createVerboseProgress() {
|
|
272
|
+
let lastPhase = '';
|
|
273
|
+
let lastPct = -1;
|
|
274
|
+
const startTime = Date.now();
|
|
275
|
+
return (progress) => {
|
|
276
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
277
|
+
if (progress.phase !== lastPhase) {
|
|
278
|
+
lastPhase = progress.phase;
|
|
279
|
+
lastPct = -1;
|
|
280
|
+
console.log(`[${elapsed}s] Phase: ${progress.phase}`);
|
|
281
|
+
}
|
|
282
|
+
if (progress.total > 0) {
|
|
283
|
+
const pct = Math.floor((progress.current / progress.total) * 100);
|
|
284
|
+
// Log every 5% to keep output manageable
|
|
285
|
+
if (pct >= lastPct + 5 || progress.current === progress.total) {
|
|
286
|
+
lastPct = pct;
|
|
287
|
+
console.log(`[${elapsed}s] ${progress.current}/${progress.total} (${pct}%)${progress.currentFile ? ` ${(0, glyphs_1.getGlyphs)().dash} ${progress.currentFile}` : ''}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else if (progress.current > 0) {
|
|
291
|
+
// Scanning phase (no total yet) — log periodically
|
|
292
|
+
if (progress.current % 1000 === 0 || progress.current === 1) {
|
|
293
|
+
console.log(`[${elapsed}s] ${formatNumber(progress.current)} files found`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Print success message
|
|
300
|
+
*/
|
|
301
|
+
function success(message) {
|
|
302
|
+
console.log(chalk.green((0, glyphs_1.getGlyphs)().ok) + ' ' + message);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Print error message
|
|
306
|
+
*/
|
|
307
|
+
function error(message) {
|
|
308
|
+
console.error(chalk.red((0, glyphs_1.getGlyphs)().err) + ' ' + message);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Print info message
|
|
312
|
+
*/
|
|
313
|
+
function info(message) {
|
|
314
|
+
console.log(chalk.blue((0, glyphs_1.getGlyphs)().info) + ' ' + message);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Print warning message
|
|
318
|
+
*/
|
|
319
|
+
function warn(message) {
|
|
320
|
+
console.log(chalk.yellow((0, glyphs_1.getGlyphs)().warn) + ' ' + message);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Print indexing results using clack log methods
|
|
324
|
+
*/
|
|
325
|
+
function printIndexResult(clack, result, projectPath) {
|
|
326
|
+
const hasErrors = result.filesErrored > 0;
|
|
327
|
+
// Surface non-file-level failures (e.g. lock-acquisition failure
|
|
328
|
+
// when another indexer is running) before the file-count branches.
|
|
329
|
+
// Without this the CLI falls through to "No files found to index",
|
|
330
|
+
// which is actively misleading — the index DID run, it just couldn't
|
|
331
|
+
// get the lock.
|
|
332
|
+
//
|
|
333
|
+
// If success is false but no severity:'error' entry exists in
|
|
334
|
+
// `result.errors` (degenerate case — shouldn't happen in practice
|
|
335
|
+
// but worth guarding because the result shape is plumbed through
|
|
336
|
+
// multiple call sites), fall back to a generic message rather than
|
|
337
|
+
// continuing to the misleading "No files found" branch or throwing.
|
|
338
|
+
if (!result.success && !hasErrors && result.filesIndexed === 0) {
|
|
339
|
+
const generic = result.errors.find((e) => e.severity === 'error');
|
|
340
|
+
clack.log.error(generic?.message ?? `Indexing failed ${(0, glyphs_1.getGlyphs)().dash} no further details available`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (result.filesIndexed > 0) {
|
|
344
|
+
if (hasErrors) {
|
|
345
|
+
clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files (${formatNumber(result.filesErrored)} could not be parsed)`);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files`);
|
|
349
|
+
}
|
|
350
|
+
clack.log.info(`${formatNumber(result.nodesCreated)} nodes, ${formatNumber(result.edgesCreated)} edges in ${formatDuration(result.durationMs)}`);
|
|
351
|
+
// A PARTIAL index (files silently dropped mid-pipeline) must not pass
|
|
352
|
+
// as a clean run — it's the difference between "indexed the repo" and
|
|
353
|
+
// "indexed most of the repo, quietly". Only the completeness
|
|
354
|
+
// reconciliation warning; per-file extractor warnings stay in the
|
|
355
|
+
// error-code summary below.
|
|
356
|
+
for (const w of result.errors.filter((e) => e.code === 'index_partial')) {
|
|
357
|
+
clack.log.warn(w.message);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
else if (hasErrors) {
|
|
361
|
+
clack.log.error(`Indexing failed ${(0, glyphs_1.getGlyphs)().dash} all ${formatNumber(result.filesErrored)} files had errors`);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
clack.log.warn('No files found to index');
|
|
365
|
+
}
|
|
366
|
+
if (hasErrors) {
|
|
367
|
+
const errorsByCode = new Map();
|
|
368
|
+
for (const err of result.errors) {
|
|
369
|
+
if (err.severity === 'error') {
|
|
370
|
+
const code = err.code || 'unknown';
|
|
371
|
+
errorsByCode.set(code, (errorsByCode.get(code) || 0) + 1);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const codeLabels = {
|
|
375
|
+
parse_error: 'files failed to parse',
|
|
376
|
+
read_error: 'files could not be read',
|
|
377
|
+
size_exceeded: 'files exceeded size limit',
|
|
378
|
+
path_traversal: 'blocked paths',
|
|
379
|
+
unsupported_language: 'unsupported language',
|
|
380
|
+
parser_error: 'parser initialization failures',
|
|
381
|
+
};
|
|
382
|
+
const breakdown = Array.from(errorsByCode)
|
|
383
|
+
.map(([code, count]) => `${formatNumber(count)} ${codeLabels[code] || code}`)
|
|
384
|
+
.join('\n');
|
|
385
|
+
clack.note(breakdown, 'Error breakdown');
|
|
386
|
+
if (projectPath) {
|
|
387
|
+
writeErrorLog(projectPath, result.errors);
|
|
388
|
+
clack.log.info('See .bhgraph/errors.log for details');
|
|
389
|
+
}
|
|
390
|
+
if (result.filesIndexed > 0) {
|
|
391
|
+
clack.log.info(`The index is fully usable ${(0, glyphs_1.getGlyphs)().dash} only the failed files are missing.`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
else if (projectPath) {
|
|
395
|
+
const logPath = path.join((0, directory_1.getBHGraphDir)(projectPath), 'errors.log');
|
|
396
|
+
if (fs.existsSync(logPath)) {
|
|
397
|
+
fs.unlinkSync(logPath);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* When an `init`/`index` produced an EMPTY graph and the reason is that the
|
|
403
|
+
* project's own `.gitignore` excludes nested git repositories — the "super-repo
|
|
404
|
+
* gitignores its child repos" layout (#1156), where `init` at the parent
|
|
405
|
+
* correctly indexes ~nothing while `init` inside each child works — name those
|
|
406
|
+
* repos and offer to index them. An interactive terminal gets a yes/no prompt
|
|
407
|
+
* that writes `includeIgnored` to bhgraph.json and re-indexes; a
|
|
408
|
+
* non-interactive run just prints the one-line opt-in snippet. The caller gates
|
|
409
|
+
* this on `nodesCreated === 0`, so a project that DID index real content is
|
|
410
|
+
* never nagged about the gitignored reference clones it deliberately keeps out
|
|
411
|
+
* (#970, #1065). Best-effort throughout: detection never breaks the command.
|
|
412
|
+
*/
|
|
413
|
+
async function offerIndexIgnoredRepos(clack, projectPath, reindex, opts) {
|
|
414
|
+
let repos;
|
|
415
|
+
try {
|
|
416
|
+
const { findUnindexedIgnoredRepos } = await Promise.resolve().then(() => __importStar(require('../extraction')));
|
|
417
|
+
repos = findUnindexedIgnoredRepos(projectPath);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
return; // detection is advisory — never let it break the command
|
|
421
|
+
}
|
|
422
|
+
if (repos.length === 0)
|
|
423
|
+
return;
|
|
424
|
+
const { PROJECT_CONFIG_FILENAME } = await Promise.resolve().then(() => __importStar(require('../project-config')));
|
|
425
|
+
const isOne = repos.length === 1;
|
|
426
|
+
const SHOWN = 6;
|
|
427
|
+
const names = repos.slice(0, SHOWN).map((r) => r.replace(/\/$/, ''));
|
|
428
|
+
const extra = repos.length > SHOWN ? ` (+${formatNumber(repos.length - SHOWN)} more)` : '';
|
|
429
|
+
const snippet = `{ "includeIgnored": [${repos.map((p) => JSON.stringify(p)).join(', ')}] }`;
|
|
430
|
+
clack.log.warn(`Your .gitignore excludes ${isOne ? 'a nested git repository' : `${formatNumber(repos.length)} nested git repositories`} here, ` +
|
|
431
|
+
`so ${isOne ? 'it was' : 'they were'} not indexed: ${names.join(', ')}${extra}.`);
|
|
432
|
+
const manualHint = () => {
|
|
433
|
+
clack.log.info(`If ${isOne ? "it's" : "they're"} your code, add ${isOne ? 'it' : 'them'} to ${PROJECT_CONFIG_FILENAME} and re-index:`);
|
|
434
|
+
clack.log.info(` ${snippet}`);
|
|
435
|
+
};
|
|
436
|
+
if (!opts.interactive || !process.stdin.isTTY) {
|
|
437
|
+
manualHint();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const yes = await clack.confirm({
|
|
441
|
+
message: `Index ${isOne ? 'it' : `these ${formatNumber(repos.length)}`} now? Adds ${isOne ? 'it' : 'them'} to ${PROJECT_CONFIG_FILENAME}.`,
|
|
442
|
+
initialValue: true,
|
|
443
|
+
});
|
|
444
|
+
if (clack.isCancel(yes) || !yes) {
|
|
445
|
+
manualHint();
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
let added;
|
|
449
|
+
try {
|
|
450
|
+
const { addIncludeIgnoredPatterns } = await Promise.resolve().then(() => __importStar(require('../project-config')));
|
|
451
|
+
added = addIncludeIgnoredPatterns(projectPath, repos);
|
|
452
|
+
}
|
|
453
|
+
catch (err) {
|
|
454
|
+
clack.log.error(`Could not update ${PROJECT_CONFIG_FILENAME}: ${err instanceof Error ? err.message : String(err)}`);
|
|
455
|
+
manualHint();
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
clack.log.success(`Added ${formatNumber(added)} ${added === 1 ? 'entry' : 'entries'} to ${PROJECT_CONFIG_FILENAME} ${(0, glyphs_1.getGlyphs)().dash} re-indexing…`);
|
|
459
|
+
const result = await reindex();
|
|
460
|
+
printIndexResult(clack, result, projectPath);
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Write detailed error log to .bhgraph/errors.log
|
|
465
|
+
*/
|
|
466
|
+
function writeErrorLog(projectPath, errors) {
|
|
467
|
+
const cgDir = (0, directory_1.getBHGraphDir)(projectPath);
|
|
468
|
+
if (!fs.existsSync(cgDir))
|
|
469
|
+
return;
|
|
470
|
+
const logPath = path.join(cgDir, 'errors.log');
|
|
471
|
+
// Group errors by file path
|
|
472
|
+
const errorsByFile = new Map();
|
|
473
|
+
const noFileErrors = [];
|
|
474
|
+
for (const err of errors) {
|
|
475
|
+
if (err.severity !== 'error')
|
|
476
|
+
continue;
|
|
477
|
+
if (err.filePath) {
|
|
478
|
+
let list = errorsByFile.get(err.filePath);
|
|
479
|
+
if (!list) {
|
|
480
|
+
list = [];
|
|
481
|
+
errorsByFile.set(err.filePath, list);
|
|
482
|
+
}
|
|
483
|
+
list.push({ message: err.message, code: err.code });
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
noFileErrors.push({ message: err.message, code: err.code });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const lines = [
|
|
490
|
+
`BHGraph Error Log - ${new Date().toISOString()}`,
|
|
491
|
+
`${errorsByFile.size} files with errors`,
|
|
492
|
+
'',
|
|
493
|
+
];
|
|
494
|
+
for (const [filePath, fileErrors] of errorsByFile) {
|
|
495
|
+
for (const err of fileErrors) {
|
|
496
|
+
lines.push(`${filePath}: ${err.message}`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
for (const err of noFileErrors) {
|
|
500
|
+
lines.push(err.message);
|
|
501
|
+
}
|
|
502
|
+
fs.writeFileSync(logPath, lines.join('\n') + '\n');
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Telemetry for a completed full index (see TELEMETRY.md). The bounded flush
|
|
506
|
+
* keeps init/index responsive (these commands just ran for seconds anyway)
|
|
507
|
+
* while delivering the event promptly.
|
|
508
|
+
*/
|
|
509
|
+
async function recordIndexTelemetry(cg, result) {
|
|
510
|
+
(0, telemetry_1.recordIndexEvent)(cg, result);
|
|
511
|
+
await (0, telemetry_1.getTelemetry)().flushNow();
|
|
512
|
+
}
|
|
513
|
+
// =============================================================================
|
|
514
|
+
// Commands
|
|
515
|
+
// =============================================================================
|
|
516
|
+
/**
|
|
517
|
+
* bhgraph init [path]
|
|
518
|
+
*/
|
|
519
|
+
program
|
|
520
|
+
.command('init [path]')
|
|
521
|
+
.description('Initialize BHGraph in a project directory and build the initial index')
|
|
522
|
+
.option('-i, --index', 'Deprecated: indexing now runs by default; flag accepted for backward compatibility')
|
|
523
|
+
.option('-f, --force', 'Initialize even if the path looks like your home directory or a filesystem root')
|
|
524
|
+
.option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
|
|
525
|
+
.action(async (pathArg, options) => {
|
|
526
|
+
const projectPath = path.resolve(pathArg || process.cwd());
|
|
527
|
+
const clack = await importESM('@clack/prompts');
|
|
528
|
+
clack.intro('Initializing BHGraph');
|
|
529
|
+
try {
|
|
530
|
+
// Refuse to index your home directory / a filesystem root — it pulls in
|
|
531
|
+
// caches, other projects, and your whole tree (a multi-GB index + watcher
|
|
532
|
+
// churn, and on pre-1.0 macOS a machine-crashing fd blowup, #845).
|
|
533
|
+
const unsafe = (0, directory_1.unsafeIndexRootReason)(projectPath);
|
|
534
|
+
if (unsafe && !options.force) {
|
|
535
|
+
clack.log.error(`Refusing to initialize in ${projectPath} — it looks like ${unsafe}.`);
|
|
536
|
+
clack.log.info('Run this inside a specific project directory, or pass --force if you really mean to index everything under it.');
|
|
537
|
+
clack.outro('');
|
|
538
|
+
process.exitCode = 1;
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if ((0, directory_1.isInitialized)(projectPath)) {
|
|
542
|
+
clack.log.warn(`Already initialized in ${projectPath}`);
|
|
543
|
+
clack.log.info('Use "bhgraph index" to re-index or "bhgraph sync" to update');
|
|
544
|
+
try {
|
|
545
|
+
const { offerWatchFallback } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
546
|
+
await offerWatchFallback(clack, projectPath);
|
|
547
|
+
}
|
|
548
|
+
catch { /* non-fatal */ }
|
|
549
|
+
clack.outro('');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
553
|
+
const cg = await BHGraph.init(projectPath, { index: false });
|
|
554
|
+
clack.log.success(`Initialized in ${projectPath}`);
|
|
555
|
+
// Indexing runs by default now. The legacy -i/--index flag is still
|
|
556
|
+
// accepted (so existing muscle memory and scripts don't break) but is a
|
|
557
|
+
// no-op — initializing always builds the initial index.
|
|
558
|
+
// Supervise the index: self-terminate if orphaned or wedged (#999).
|
|
559
|
+
// A closure so we can re-run the exact same supervised, progress-rendered
|
|
560
|
+
// index if the user opts gitignored child repos in below (#1156).
|
|
561
|
+
const runIndex = async () => {
|
|
562
|
+
const supervision = (0, command_supervision_1.installCommandSupervision)('init');
|
|
563
|
+
try {
|
|
564
|
+
if (options.verbose) {
|
|
565
|
+
return await cg.indexAll({ onProgress: createVerboseProgress(), verbose: true });
|
|
566
|
+
}
|
|
567
|
+
process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
|
|
568
|
+
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
569
|
+
const r = await cg.indexAll({ onProgress: progress.onProgress });
|
|
570
|
+
await progress.stop();
|
|
571
|
+
return r;
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
supervision.stop();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
const result = await runIndex();
|
|
578
|
+
printIndexResult(clack, result, projectPath);
|
|
579
|
+
await recordIndexTelemetry(cg, result);
|
|
580
|
+
// An empty graph at a git super-repo usually means `.gitignore` excludes
|
|
581
|
+
// the child repos that hold the code — surface them and offer to opt in
|
|
582
|
+
// rather than leaving the user with a silent 0-node "Done". (#1156)
|
|
583
|
+
if (result.nodesCreated === 0) {
|
|
584
|
+
await offerIndexIgnoredRepos(clack, projectPath, runIndex, { interactive: true });
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
const { offerWatchFallback } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
588
|
+
await offerWatchFallback(clack, projectPath);
|
|
589
|
+
}
|
|
590
|
+
catch { /* non-fatal */ }
|
|
591
|
+
clack.outro('Done');
|
|
592
|
+
cg.destroy();
|
|
593
|
+
}
|
|
594
|
+
catch (err) {
|
|
595
|
+
clack.log.error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
596
|
+
process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
/**
|
|
600
|
+
* bhgraph uninit [path]
|
|
601
|
+
*/
|
|
602
|
+
program
|
|
603
|
+
.command('uninit [path]')
|
|
604
|
+
.description('Remove BHGraph from a project (deletes .bhgraph/ directory)')
|
|
605
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
606
|
+
.action(async (pathArg, options) => {
|
|
607
|
+
const projectPath = resolveProjectPath(pathArg);
|
|
608
|
+
try {
|
|
609
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
610
|
+
warn(`BHGraph is not initialized in ${projectPath}`);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (!options.force) {
|
|
614
|
+
// Confirm with user
|
|
615
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
616
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
617
|
+
const answer = await new Promise((resolve) => {
|
|
618
|
+
rl.question(chalk.yellow(`${(0, glyphs_1.getGlyphs)().warn} This will permanently delete all BHGraph data. Continue? (y/N) `), resolve);
|
|
619
|
+
});
|
|
620
|
+
rl.close();
|
|
621
|
+
if (answer.toLowerCase() !== 'y') {
|
|
622
|
+
info('Cancelled');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
627
|
+
const cg = BHGraph.openSync(projectPath);
|
|
628
|
+
cg.uninitialize();
|
|
629
|
+
// Clean up any git sync hooks we installed (no-op if none / not a repo).
|
|
630
|
+
try {
|
|
631
|
+
const { removeGitSyncHook } = await Promise.resolve().then(() => __importStar(require('../sync/git-hooks')));
|
|
632
|
+
const removed = removeGitSyncHook(projectPath);
|
|
633
|
+
if (removed.installed.length > 0) {
|
|
634
|
+
info(`Removed git ${removed.installed.join(', ')} sync hook${removed.installed.length > 1 ? 's' : ''}`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
catch { /* non-fatal */ }
|
|
638
|
+
success(`Removed BHGraph from ${projectPath}`);
|
|
639
|
+
// Churn signal — and flush now, since after an uninit there may be no
|
|
640
|
+
// "next run" to deliver it.
|
|
641
|
+
try {
|
|
642
|
+
(0, telemetry_1.getTelemetry)().recordLifecycle('uninstall', {});
|
|
643
|
+
await (0, telemetry_1.getTelemetry)().flushNow();
|
|
644
|
+
}
|
|
645
|
+
catch { /* non-fatal */ }
|
|
646
|
+
}
|
|
647
|
+
catch (err) {
|
|
648
|
+
error(`Failed to uninitialize: ${err instanceof Error ? err.message : String(err)}`);
|
|
649
|
+
process.exit(1);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
/**
|
|
653
|
+
* bhgraph index [path]
|
|
654
|
+
*/
|
|
655
|
+
program
|
|
656
|
+
.command('index [path]')
|
|
657
|
+
.description('Rebuild the full index from scratch (same result as a fresh init)')
|
|
658
|
+
.option('-f, --force', 'Index even if the path looks like your home directory or a filesystem root')
|
|
659
|
+
.option('-q, --quiet', 'Suppress progress output')
|
|
660
|
+
.option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
|
|
661
|
+
.action(async (pathArg, options) => {
|
|
662
|
+
const projectPath = resolveProjectPath(pathArg);
|
|
663
|
+
try {
|
|
664
|
+
// Don't (re)index your home directory / a filesystem root (#845). --force
|
|
665
|
+
// doubles as the override.
|
|
666
|
+
const unsafe = (0, directory_1.unsafeIndexRootReason)(projectPath);
|
|
667
|
+
if (unsafe && !options.force) {
|
|
668
|
+
error(`Refusing to index ${projectPath} — it looks like ${unsafe}. Pass --force to override.`);
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
671
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
672
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
673
|
+
info('Run "bhgraph init" first');
|
|
674
|
+
process.exit(1);
|
|
675
|
+
}
|
|
676
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
677
|
+
// `index` is a FULL re-index — identical to a fresh `init`. RECREATE the
|
|
678
|
+
// database from scratch (discard .bhgraph/bhgraph.db + its WAL) rather
|
|
679
|
+
// than opening the old graph and DELETE-ing every row. The clear-then-index
|
|
680
|
+
// approach reported "0 nodes" without the clear (#874); the recreate keeps
|
|
681
|
+
// that fixed AND avoids the failure mode where, on a large or pre-fix
|
|
682
|
+
// poisoned index, the per-row FTS delete churn wedged the main thread long
|
|
683
|
+
// enough to trip the liveness watchdog before scanning even began (#1067).
|
|
684
|
+
// recreate() hands back a fresh, empty instance — no clear() needed. For
|
|
685
|
+
// fast incremental updates use `sync`.
|
|
686
|
+
const cg = await BHGraph.recreate(projectPath);
|
|
687
|
+
// Supervise the indexer: self-terminate if orphaned (parent shim killed)
|
|
688
|
+
// or if the main thread wedges — neither was guarded on this path (#999).
|
|
689
|
+
const supervision = (0, command_supervision_1.installCommandSupervision)('index');
|
|
690
|
+
try {
|
|
691
|
+
if (options.quiet) {
|
|
692
|
+
// Quiet mode: no UI, just run against the freshly-recreated graph.
|
|
693
|
+
const result = await cg.indexAll();
|
|
694
|
+
if (!result.success)
|
|
695
|
+
process.exit(1);
|
|
696
|
+
cg.destroy();
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const clack = await importESM('@clack/prompts');
|
|
700
|
+
clack.intro('Indexing project');
|
|
701
|
+
// A closure so a re-index (after opting gitignored child repos in, #1156)
|
|
702
|
+
// renders identically. Supervision already wraps the whole command.
|
|
703
|
+
const renderIndex = async () => {
|
|
704
|
+
if (options.verbose) {
|
|
705
|
+
return await cg.indexAll({ onProgress: createVerboseProgress(), verbose: true });
|
|
706
|
+
}
|
|
707
|
+
process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
|
|
708
|
+
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
709
|
+
const r = await cg.indexAll({ onProgress: progress.onProgress });
|
|
710
|
+
await progress.stop();
|
|
711
|
+
return r;
|
|
712
|
+
};
|
|
713
|
+
const result = await renderIndex();
|
|
714
|
+
printIndexResult(clack, result, projectPath);
|
|
715
|
+
await recordIndexTelemetry(cg, result);
|
|
716
|
+
// Empty graph at a git super-repo → likely `.gitignore`d child repos;
|
|
717
|
+
// name them and offer to opt in instead of a silent 0-node result (#1156).
|
|
718
|
+
let finalResult = result;
|
|
719
|
+
if (result.nodesCreated === 0) {
|
|
720
|
+
finalResult = (await offerIndexIgnoredRepos(clack, projectPath, renderIndex, { interactive: true })) ?? result;
|
|
721
|
+
}
|
|
722
|
+
if (!finalResult.success) {
|
|
723
|
+
process.exit(1);
|
|
724
|
+
}
|
|
725
|
+
clack.outro('Done');
|
|
726
|
+
cg.destroy();
|
|
727
|
+
}
|
|
728
|
+
finally {
|
|
729
|
+
supervision.stop();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
catch (err) {
|
|
733
|
+
error(`Failed to index: ${err instanceof Error ? err.message : String(err)}`);
|
|
734
|
+
process.exit(1);
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
/**
|
|
738
|
+
* bhgraph sync [path]
|
|
739
|
+
*/
|
|
740
|
+
program
|
|
741
|
+
.command('sync [path]')
|
|
742
|
+
.description('Sync changes since last index')
|
|
743
|
+
.option('-q, --quiet', 'Suppress output (for git hooks)')
|
|
744
|
+
.action(async (pathArg, options) => {
|
|
745
|
+
const projectPath = resolveProjectPath(pathArg);
|
|
746
|
+
try {
|
|
747
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
748
|
+
if (!options.quiet) {
|
|
749
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
750
|
+
}
|
|
751
|
+
process.exit(1);
|
|
752
|
+
}
|
|
753
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
754
|
+
const cg = await BHGraph.open(projectPath);
|
|
755
|
+
if (options.quiet) {
|
|
756
|
+
await cg.sync();
|
|
757
|
+
cg.destroy();
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const clack = await importESM('@clack/prompts');
|
|
761
|
+
clack.intro('Syncing BHGraph');
|
|
762
|
+
process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
|
|
763
|
+
const progress = (0, shimmer_progress_1.createShimmerProgress)();
|
|
764
|
+
const result = await cg.sync({
|
|
765
|
+
onProgress: progress.onProgress,
|
|
766
|
+
});
|
|
767
|
+
await progress.stop();
|
|
768
|
+
const totalChanges = result.filesAdded + result.filesModified + result.filesRemoved;
|
|
769
|
+
if (totalChanges === 0) {
|
|
770
|
+
clack.log.info('Already up to date');
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
clack.log.success(`Synced ${formatNumber(totalChanges)} changed files`);
|
|
774
|
+
const details = [];
|
|
775
|
+
if (result.filesAdded > 0)
|
|
776
|
+
details.push(`Added: ${result.filesAdded}`);
|
|
777
|
+
if (result.filesModified > 0)
|
|
778
|
+
details.push(`Modified: ${result.filesModified}`);
|
|
779
|
+
if (result.filesRemoved > 0)
|
|
780
|
+
details.push(`Removed: ${result.filesRemoved}`);
|
|
781
|
+
clack.log.info(`${details.join(', ')} ${(0, glyphs_1.getGlyphs)().dash} ${formatNumber(result.nodesUpdated)} nodes in ${formatDuration(result.durationMs)}`);
|
|
782
|
+
}
|
|
783
|
+
clack.outro('Done');
|
|
784
|
+
cg.destroy();
|
|
785
|
+
}
|
|
786
|
+
catch (err) {
|
|
787
|
+
if (!options.quiet) {
|
|
788
|
+
error(`Failed to sync: ${err instanceof Error ? err.message : String(err)}`);
|
|
789
|
+
}
|
|
790
|
+
process.exit(1);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
/**
|
|
794
|
+
* bhgraph status [path]
|
|
795
|
+
*/
|
|
796
|
+
program
|
|
797
|
+
.command('status [path]')
|
|
798
|
+
.description('Show index status and statistics')
|
|
799
|
+
.option('-j, --json', 'Output as JSON')
|
|
800
|
+
.action(async (pathArg, options) => {
|
|
801
|
+
const projectPath = resolveProjectPath(pathArg);
|
|
802
|
+
// The directory the user actually ran from, before walking up to the index
|
|
803
|
+
// root. Used to detect when the resolved index lives in a different git
|
|
804
|
+
// working tree (e.g. a nested worktree borrowing the main checkout's index).
|
|
805
|
+
const startPath = path.resolve(pathArg || process.cwd());
|
|
806
|
+
const worktreeMismatch = (0, worktree_1.detectWorktreeIndexMismatch)(startPath, projectPath);
|
|
807
|
+
try {
|
|
808
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
809
|
+
if (options.json) {
|
|
810
|
+
console.log(JSON.stringify({
|
|
811
|
+
initialized: false,
|
|
812
|
+
version: packageJson.version,
|
|
813
|
+
projectPath,
|
|
814
|
+
indexPath: (0, directory_1.getBHGraphDir)(projectPath),
|
|
815
|
+
lastIndexed: null,
|
|
816
|
+
}));
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
console.log(chalk.bold('\nBHGraph Status\n'));
|
|
820
|
+
info(`Project: ${projectPath}`);
|
|
821
|
+
warn('Not initialized');
|
|
822
|
+
info('Run "bhgraph init" to initialize');
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
826
|
+
const cg = await BHGraph.open(projectPath);
|
|
827
|
+
const stats = cg.getStats();
|
|
828
|
+
const changes = cg.getChangedFiles();
|
|
829
|
+
const backend = cg.getBackend();
|
|
830
|
+
const journalMode = cg.getJournalMode();
|
|
831
|
+
const buildInfo = cg.getIndexBuildInfo();
|
|
832
|
+
const reindexRecommended = cg.isIndexStale();
|
|
833
|
+
const indexState = cg.getIndexState();
|
|
834
|
+
// Zero on a healthy index; non-zero at rest means a resolution pass was
|
|
835
|
+
// interrupted, so some files' call edges are missing (#1187).
|
|
836
|
+
const pendingRefs = cg.getPendingReferenceCount();
|
|
837
|
+
// JSON output mode
|
|
838
|
+
if (options.json) {
|
|
839
|
+
const lastIndexedMs = cg.getLastIndexedAt();
|
|
840
|
+
console.log(JSON.stringify({
|
|
841
|
+
initialized: true,
|
|
842
|
+
version: packageJson.version,
|
|
843
|
+
projectPath,
|
|
844
|
+
indexPath: (0, directory_1.getBHGraphDir)(projectPath),
|
|
845
|
+
lastIndexed: lastIndexedMs != null ? new Date(lastIndexedMs).toISOString() : null,
|
|
846
|
+
fileCount: stats.fileCount,
|
|
847
|
+
nodeCount: stats.nodeCount,
|
|
848
|
+
edgeCount: stats.edgeCount,
|
|
849
|
+
dbSizeBytes: stats.dbSizeBytes,
|
|
850
|
+
backend,
|
|
851
|
+
journalMode,
|
|
852
|
+
nodesByKind: stats.nodesByKind,
|
|
853
|
+
languages: Object.entries(stats.filesByLanguage).filter(([, count]) => count > 0).map(([lang]) => lang),
|
|
854
|
+
pendingChanges: {
|
|
855
|
+
added: changes.added.length,
|
|
856
|
+
modified: changes.modified.length,
|
|
857
|
+
removed: changes.removed.length,
|
|
858
|
+
},
|
|
859
|
+
worktreeMismatch: worktreeMismatch
|
|
860
|
+
? { worktreeRoot: worktreeMismatch.worktreeRoot, indexRoot: worktreeMismatch.indexRoot }
|
|
861
|
+
: null,
|
|
862
|
+
index: {
|
|
863
|
+
builtWithVersion: buildInfo.version,
|
|
864
|
+
builtWithExtractionVersion: buildInfo.extractionVersion,
|
|
865
|
+
currentExtractionVersion: extraction_version_1.EXTRACTION_VERSION,
|
|
866
|
+
reindexRecommended,
|
|
867
|
+
// 'complete' | 'partial' (files silently dropped) | 'indexing'
|
|
868
|
+
// (a run was killed mid-index — the index is truncated) |
|
|
869
|
+
// 'failed' | null (predates the marker).
|
|
870
|
+
state: indexState,
|
|
871
|
+
// References awaiting resolution. Non-zero at rest means an
|
|
872
|
+
// interrupted resolution pass left edges missing; the next
|
|
873
|
+
// sync sweeps them (#1187).
|
|
874
|
+
pendingRefs,
|
|
875
|
+
},
|
|
876
|
+
}));
|
|
877
|
+
cg.destroy();
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
console.log(chalk.bold('\nBHGraph Status\n'));
|
|
881
|
+
// Project info
|
|
882
|
+
console.log(chalk.cyan('Project:'), projectPath);
|
|
883
|
+
if (worktreeMismatch) {
|
|
884
|
+
warn((0, worktree_1.worktreeMismatchWarning)(worktreeMismatch));
|
|
885
|
+
}
|
|
886
|
+
if (indexState === 'indexing') {
|
|
887
|
+
warn('The last index run never finished (killed mid-index?) — the index is truncated. Re-run "bhgraph index".');
|
|
888
|
+
}
|
|
889
|
+
else if (indexState === 'partial') {
|
|
890
|
+
warn('The last index run silently dropped files — the index is partial. Re-run "bhgraph index".');
|
|
891
|
+
}
|
|
892
|
+
else if (indexState === 'failed') {
|
|
893
|
+
warn('The last index run failed — results may be incomplete. Re-run "bhgraph index".');
|
|
894
|
+
}
|
|
895
|
+
if (pendingRefs > 0) {
|
|
896
|
+
warn(`${formatNumber(pendingRefs)} references from an interrupted run are awaiting resolution — some callers/impact edges are missing. Run "bhgraph sync" to resolve them.`);
|
|
897
|
+
}
|
|
898
|
+
console.log();
|
|
899
|
+
// Index stats
|
|
900
|
+
console.log(chalk.bold('Index Statistics:'));
|
|
901
|
+
console.log(` Files: ${formatNumber(stats.fileCount)}`);
|
|
902
|
+
console.log(` Nodes: ${formatNumber(stats.nodeCount)}`);
|
|
903
|
+
console.log(` Edges: ${formatNumber(stats.edgeCount)}`);
|
|
904
|
+
console.log(` DB Size: ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
905
|
+
// Surface the active SQLite backend (node:sqlite — Node's built-in real
|
|
906
|
+
// SQLite, full WAL + FTS5, no native build).
|
|
907
|
+
const backendLabel = chalk.green(`node:sqlite ${(0, glyphs_1.getGlyphs)().dash} built-in (full WAL)`);
|
|
908
|
+
console.log(` Backend: ${backendLabel}`);
|
|
909
|
+
// Effective journal mode: 'wal' means concurrent reads never block on a
|
|
910
|
+
// writer; anything else means they can ("database is locked"). node:sqlite
|
|
911
|
+
// supports WAL everywhere, so a non-wal mode means the filesystem can't
|
|
912
|
+
// (network mounts, WSL2 /mnt). See issue #238.
|
|
913
|
+
const journalLabel = journalMode === 'wal'
|
|
914
|
+
? chalk.green('wal')
|
|
915
|
+
: chalk.yellow(`${journalMode || 'unknown'} ${(0, glyphs_1.getGlyphs)().dash} WAL inactive; reads can block on writes`);
|
|
916
|
+
console.log(` Journal: ${journalLabel}`);
|
|
917
|
+
console.log();
|
|
918
|
+
// Node breakdown
|
|
919
|
+
console.log(chalk.bold('Nodes by Kind:'));
|
|
920
|
+
const nodesByKind = Object.entries(stats.nodesByKind)
|
|
921
|
+
.filter(([, count]) => count > 0)
|
|
922
|
+
.sort((a, b) => b[1] - a[1]);
|
|
923
|
+
for (const [kind, count] of nodesByKind) {
|
|
924
|
+
console.log(` ${kind.padEnd(15)} ${formatNumber(count)}`);
|
|
925
|
+
}
|
|
926
|
+
console.log();
|
|
927
|
+
// Language breakdown
|
|
928
|
+
console.log(chalk.bold('Files by Language:'));
|
|
929
|
+
const filesByLang = Object.entries(stats.filesByLanguage)
|
|
930
|
+
.filter(([, count]) => count > 0)
|
|
931
|
+
.sort((a, b) => b[1] - a[1]);
|
|
932
|
+
for (const [lang, count] of filesByLang) {
|
|
933
|
+
console.log(` ${lang.padEnd(15)} ${formatNumber(count)}`);
|
|
934
|
+
}
|
|
935
|
+
console.log();
|
|
936
|
+
// Pending changes
|
|
937
|
+
const totalChanges = changes.added.length + changes.modified.length + changes.removed.length;
|
|
938
|
+
if (totalChanges > 0) {
|
|
939
|
+
console.log(chalk.bold('Pending Changes:'));
|
|
940
|
+
if (changes.added.length > 0) {
|
|
941
|
+
console.log(` Added: ${changes.added.length} files`);
|
|
942
|
+
}
|
|
943
|
+
if (changes.modified.length > 0) {
|
|
944
|
+
console.log(` Modified: ${changes.modified.length} files`);
|
|
945
|
+
}
|
|
946
|
+
if (changes.removed.length > 0) {
|
|
947
|
+
console.log(` Removed: ${changes.removed.length} files`);
|
|
948
|
+
}
|
|
949
|
+
info('Run "bhgraph sync" to update the index');
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
success('Index is up to date');
|
|
953
|
+
}
|
|
954
|
+
console.log();
|
|
955
|
+
// Re-index hint: the index was built by an older engine than the one now
|
|
956
|
+
// running, so a rebuild would add data a migration can't backfill.
|
|
957
|
+
if (reindexRecommended) {
|
|
958
|
+
const builtWith = buildInfo.version ? `v${buildInfo.version.replace(/^v/, '')}` : 'an earlier version';
|
|
959
|
+
warn(`Index was built by ${builtWith}; re-index to pick up this engine's improvements.`);
|
|
960
|
+
info('Run "bhgraph index" (full rebuild) or "bhgraph sync"');
|
|
961
|
+
console.log();
|
|
962
|
+
}
|
|
963
|
+
cg.destroy();
|
|
964
|
+
}
|
|
965
|
+
catch (err) {
|
|
966
|
+
error(`Failed to get status: ${err instanceof Error ? err.message : String(err)}`);
|
|
967
|
+
process.exit(1);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
/**
|
|
971
|
+
* bhgraph query <search>
|
|
972
|
+
*/
|
|
973
|
+
program
|
|
974
|
+
.command('query <search>')
|
|
975
|
+
.description('Search for symbols in the codebase')
|
|
976
|
+
.option('-p, --path <path>', 'Project path')
|
|
977
|
+
.option('-l, --limit <number>', 'Maximum results', '10')
|
|
978
|
+
.option('-k, --kind <kind>', 'Filter by node kind (function, class, etc.)')
|
|
979
|
+
.option('-j, --json', 'Output as JSON')
|
|
980
|
+
.action(async (search, options) => {
|
|
981
|
+
const projectPath = resolveProjectPath(options.path);
|
|
982
|
+
try {
|
|
983
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
984
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
985
|
+
process.exit(1);
|
|
986
|
+
}
|
|
987
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
988
|
+
const cg = await BHGraph.open(projectPath);
|
|
989
|
+
const limit = parseInt(options.limit || '10', 10);
|
|
990
|
+
const rawResults = cg.searchNodes(search, {
|
|
991
|
+
limit,
|
|
992
|
+
kinds: options.kind ? [options.kind] : undefined,
|
|
993
|
+
});
|
|
994
|
+
// Mirror the MCP search down-rank so the CLI also surfaces the
|
|
995
|
+
// hand-written implementation before protobuf/gRPC scaffolding
|
|
996
|
+
// when both share a name. See extraction/generated-detection.ts.
|
|
997
|
+
const { isGeneratedFile } = await Promise.resolve().then(() => __importStar(require('../extraction/generated-detection')));
|
|
998
|
+
const results = [...rawResults].sort((a, b) => {
|
|
999
|
+
const aGen = isGeneratedFile(a.node.filePath) ? 1 : 0;
|
|
1000
|
+
const bGen = isGeneratedFile(b.node.filePath) ? 1 : 0;
|
|
1001
|
+
return aGen - bGen;
|
|
1002
|
+
});
|
|
1003
|
+
if (options.json) {
|
|
1004
|
+
console.log(JSON.stringify(results, null, 2));
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
if (results.length === 0) {
|
|
1008
|
+
info(`No results found for "${search}"`);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
console.log(chalk.bold(`\nSearch Results for "${search}":\n`));
|
|
1012
|
+
// Results arrive already ranked by relevance, so the order conveys
|
|
1013
|
+
// it. We don't print the raw score: it's an unbounded BM25/FTS value
|
|
1014
|
+
// (relative-ranking only), and the old `(score * 100)%` rendered it
|
|
1015
|
+
// as nonsensical percentages like "12042%" (#1045). The MCP search
|
|
1016
|
+
// tool likewise shows no score. Raw `score` stays in --json output.
|
|
1017
|
+
for (const result of results) {
|
|
1018
|
+
const node = result.node;
|
|
1019
|
+
const location = `${node.filePath}:${node.startLine}`;
|
|
1020
|
+
console.log(chalk.cyan(node.kind.padEnd(12)) +
|
|
1021
|
+
chalk.white(node.name));
|
|
1022
|
+
console.log(chalk.dim(` ${location}`));
|
|
1023
|
+
if (node.signature) {
|
|
1024
|
+
console.log(chalk.dim(` ${node.signature}`));
|
|
1025
|
+
}
|
|
1026
|
+
console.log();
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
cg.destroy();
|
|
1031
|
+
}
|
|
1032
|
+
catch (err) {
|
|
1033
|
+
error(`Search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1034
|
+
process.exit(1);
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
/**
|
|
1038
|
+
* bhgraph explore <query...>
|
|
1039
|
+
*
|
|
1040
|
+
* The CLI face of the MCP bhgraph_explore tool — same handler, same
|
|
1041
|
+
* output (source of the relevant symbols grouped by file + the call path
|
|
1042
|
+
* among them). Exists so agents WITHOUT the MCP tools — Task-tool
|
|
1043
|
+
* subagents (which don't inherit MCP tools, #704) and non-MCP harnesses —
|
|
1044
|
+
* can reach the graph through a plain shell command.
|
|
1045
|
+
*/
|
|
1046
|
+
program
|
|
1047
|
+
.command('explore <query...>')
|
|
1048
|
+
.description('Explore an area: relevant symbols\' source + call paths in one shot (same output as the bhgraph_explore MCP tool)')
|
|
1049
|
+
.option('-p, --path <path>', 'Project path')
|
|
1050
|
+
.option('--max-files <number>', 'Maximum number of files to include source from')
|
|
1051
|
+
.action(async (queryParts, options) => {
|
|
1052
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1053
|
+
try {
|
|
1054
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1055
|
+
error(`BHGraph isn't available here — no .bhgraph/ index exists in ${projectPath}. If you are an AI agent: continue with your usual tools; indexing is the user's decision, do not run it yourself. (The project owner can enable BHGraph with 'bhgraph init'.)`);
|
|
1056
|
+
process.exit(1);
|
|
1057
|
+
}
|
|
1058
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1059
|
+
const cg = await BHGraph.open(projectPath);
|
|
1060
|
+
const { ToolHandler } = await Promise.resolve().then(() => __importStar(require('../mcp/tools')));
|
|
1061
|
+
const handler = new ToolHandler(cg);
|
|
1062
|
+
const args = { query: queryParts.join(' ') };
|
|
1063
|
+
if (options.maxFiles)
|
|
1064
|
+
args.maxFiles = parseInt(options.maxFiles, 10);
|
|
1065
|
+
const result = await handler.execute('bhgraph_explore', args);
|
|
1066
|
+
console.log(result.content[0]?.text ?? '');
|
|
1067
|
+
cg.destroy();
|
|
1068
|
+
if (result.isError)
|
|
1069
|
+
process.exit(1);
|
|
1070
|
+
}
|
|
1071
|
+
catch (err) {
|
|
1072
|
+
error(`Explore failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1073
|
+
process.exit(1);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
/**
|
|
1077
|
+
* bhgraph prompt-hook (hidden)
|
|
1078
|
+
*
|
|
1079
|
+
* A Claude Code `UserPromptSubmit` hook entry point. Reads `{prompt, cwd}` JSON
|
|
1080
|
+
* on stdin; for a structural/flow/impact prompt it runs `bhgraph_explore` on
|
|
1081
|
+
* the indexed project and prints the result to stdout, which Claude injects into
|
|
1082
|
+
* the agent's context — so the agent's reflex grep/read has nothing left to find
|
|
1083
|
+
* and reliably uses BHGraph (the adoption problem). Installed by the installer
|
|
1084
|
+
* into Claude's settings.json (opt-in, default-yes).
|
|
1085
|
+
*
|
|
1086
|
+
* LOAD-BEARING: this must NEVER break the user's prompt. Every failure path —
|
|
1087
|
+
* kill-switch, non-structural prompt, no index, engine error — exits 0 with no
|
|
1088
|
+
* output. The only effect is additive context when it can confidently provide it.
|
|
1089
|
+
*/
|
|
1090
|
+
program
|
|
1091
|
+
.command('prompt-hook', { hidden: true })
|
|
1092
|
+
.description('Claude UserPromptSubmit hook: inject BHGraph context for structural prompts (reads {prompt,cwd} JSON on stdin)')
|
|
1093
|
+
.action(async () => {
|
|
1094
|
+
try {
|
|
1095
|
+
// Kill-switch: lets a user disable the nudge without uninstalling /
|
|
1096
|
+
// editing settings.json (CI, low-power machines, personal preference).
|
|
1097
|
+
if (process.env.BHGRAPH_NO_PROMPT_HOOK === '1' || process.env.BHGRAPH_PROMPT_HOOK === '0')
|
|
1098
|
+
return;
|
|
1099
|
+
if (process.stdin.isTTY)
|
|
1100
|
+
return; // invoked by hand, no piped payload
|
|
1101
|
+
const raw = await new Promise((resolve) => {
|
|
1102
|
+
let data = '';
|
|
1103
|
+
process.stdin.setEncoding('utf8');
|
|
1104
|
+
process.stdin.on('data', (c) => { data += c; });
|
|
1105
|
+
process.stdin.on('end', () => resolve(data));
|
|
1106
|
+
process.stdin.on('error', () => resolve(data));
|
|
1107
|
+
});
|
|
1108
|
+
let input = {};
|
|
1109
|
+
try {
|
|
1110
|
+
input = JSON.parse(raw);
|
|
1111
|
+
}
|
|
1112
|
+
catch {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const prompt = String(input.prompt || '');
|
|
1116
|
+
// Gate telemetry: how often each tier fires vs. no-ops — counter names
|
|
1117
|
+
// only, NEVER prompt content (see TELEMETRY.md). This is the data that
|
|
1118
|
+
// turns "is the gate any good" from vibes into a measured recall rate.
|
|
1119
|
+
const gate = (outcome) => {
|
|
1120
|
+
try {
|
|
1121
|
+
(0, telemetry_1.getTelemetry)().recordUsage('cli_command', `prompt-hook-gate-${outcome}`, true);
|
|
1122
|
+
}
|
|
1123
|
+
catch { /* never break the hook */ }
|
|
1124
|
+
};
|
|
1125
|
+
// Gate, tiered by confidence (#994, #1126):
|
|
1126
|
+
// HIGH — a structural keyword (any covered language), or a code-shaped
|
|
1127
|
+
// token verified in the index → full explore injection.
|
|
1128
|
+
// MEDIUM — no keyword/token, but prose words match indexed symbol-name
|
|
1129
|
+
// SEGMENTS ("state machine" → OrderStateMachine, in any
|
|
1130
|
+
// language): inject a short list of the matching symbols and
|
|
1131
|
+
// let the AGENT write the explore query — the graph-derived
|
|
1132
|
+
// tier, no vocabulary involved.
|
|
1133
|
+
// silent — nothing verified. Every other prompt ("fix this typo")
|
|
1134
|
+
// stays a zero-cost no-op.
|
|
1135
|
+
// Keywords fire on their own; a token or prose word is only a CANDIDATE
|
|
1136
|
+
// verified against the graph below, so a tech brand ("JavaScript") that
|
|
1137
|
+
// merely looks like code doesn't inject spurious context.
|
|
1138
|
+
const keyworded = (0, directory_1.hasStructuralKeyword)(prompt);
|
|
1139
|
+
const codeTokens = keyworded ? [] : (0, directory_1.extractCodeTokens)(prompt);
|
|
1140
|
+
const proseWords = keyworded ? [] : (0, identifier_segments_1.extractProseCandidates)(prompt);
|
|
1141
|
+
if (!keyworded && codeTokens.length === 0 && proseWords.length === 0) {
|
|
1142
|
+
gate('noop-shape');
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
// Decide what to inject, shaped by WHERE the index(es) are: the nearest
|
|
1146
|
+
// indexed ancestor of cwd, or — when cwd is an un-indexed workspace root
|
|
1147
|
+
// whose indexed project(s) live in sub-dirs (the monorepo case, #964) —
|
|
1148
|
+
// the sub-project the prompt points at, plus a `projectPath` nudge for any
|
|
1149
|
+
// others. Without the down-scan the hook injected nothing at a monorepo
|
|
1150
|
+
// root (it only walked up), so the validated adoption lever never fired
|
|
1151
|
+
// exactly where the agent most needs it.
|
|
1152
|
+
const plan = (0, directory_1.planFrontload)(String(input.cwd || process.cwd()), prompt);
|
|
1153
|
+
if (!plan.exploreRoot && plan.nudgeProjects.length === 0) {
|
|
1154
|
+
gate('noop-no-index');
|
|
1155
|
+
return;
|
|
1156
|
+
} // nothing reachable — the agent's normal tools apply
|
|
1157
|
+
// A "pass projectPath" line for indexed sub-projects we did NOT front-load.
|
|
1158
|
+
// Follow-up bhgraph_explore calls against a sub-project (cwd isn't its
|
|
1159
|
+
// index root) need an explicit projectPath, so spell it out.
|
|
1160
|
+
const nudge = (projects, lead) => `${lead}\n${projects.map((p) => ` - projectPath: "${p}"`).join('\n')}\n`;
|
|
1161
|
+
if (plan.exploreRoot) {
|
|
1162
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1163
|
+
const cg = await BHGraph.open(plan.exploreRoot);
|
|
1164
|
+
try {
|
|
1165
|
+
const others = plan.nudgeProjects.length
|
|
1166
|
+
? `\n${nudge(plan.nudgeProjects, 'Other indexed projects in this workspace — pass projectPath to query them:')}`
|
|
1167
|
+
: '';
|
|
1168
|
+
// Tier decision against THIS index (issue #994 follow-up: candidates
|
|
1169
|
+
// must be real here — a brand name or prose about another domain
|
|
1170
|
+
// must not inject). Keyword-bearing prompts skip verification — the
|
|
1171
|
+
// keyword is signal enough.
|
|
1172
|
+
const tokenVerified = !keyworded && codeTokens.some((t) => cg.getNodesByName(t).length > 0);
|
|
1173
|
+
if (keyworded || tokenVerified) {
|
|
1174
|
+
const { ToolHandler } = await Promise.resolve().then(() => __importStar(require('../mcp/tools')));
|
|
1175
|
+
const handler = new ToolHandler(cg);
|
|
1176
|
+
const result = await handler.execute('bhgraph_explore', { query: prompt });
|
|
1177
|
+
const text = result.content[0]?.text ?? '';
|
|
1178
|
+
if (!result.isError && text.trim()) {
|
|
1179
|
+
// Cap the injection so a large-repo explore can't flood the prompt.
|
|
1180
|
+
const MAX = 16000;
|
|
1181
|
+
const body = text.length > MAX ? `${text.slice(0, MAX)}\n…(truncated; call bhgraph_explore for the rest)` : text;
|
|
1182
|
+
// For a front-loaded SUB-project, a follow-up explore needs its path.
|
|
1183
|
+
const more = plan.viaSubScan
|
|
1184
|
+
? `call bhgraph_explore with projectPath: "${plan.exploreRoot}" for more`
|
|
1185
|
+
: 'call bhgraph_explore for more';
|
|
1186
|
+
process.stdout.write(`<bhgraph_context note="Structural context from BHGraph for this prompt — treat returned source as already read; ${more}.">\n${body}${others}\n</bhgraph_context>\n`);
|
|
1187
|
+
gate(keyworded ? 'high-keyword' : 'high-token');
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
// A high-* outcome must mean context was actually delivered —
|
|
1191
|
+
// the funnel's noop-vs-high split is how gate recall is
|
|
1192
|
+
// measured (#1143). An explore error or empty result is a
|
|
1193
|
+
// delivery failure, not a gate success.
|
|
1194
|
+
gate(keyworded ? 'noop-explore-keyword' : 'noop-explore-token');
|
|
1195
|
+
}
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
// MEDIUM: prose words → symbol-name segments, co-occurrence/rarity
|
|
1199
|
+
// scored, each hit re-verified to exist (see getSegmentMatches). The
|
|
1200
|
+
// payload names the symbols but does NOT run explore — the agent owns
|
|
1201
|
+
// the query where the hook's confidence is only "these are related".
|
|
1202
|
+
//
|
|
1203
|
+
// A database indexed before the vocab table existed starts with it
|
|
1204
|
+
// EMPTY, and only sync() backfills it — which this hook never runs
|
|
1205
|
+
// (#1142). Heal it here: on a populated vocab this is one SELECT;
|
|
1206
|
+
// the actual backfill is a one-time batched pass whose cost the MCP
|
|
1207
|
+
// server's own catch-up sync usually pays first (it runs at every
|
|
1208
|
+
// session start). A distinct noop outcome keeps a dormant vocab
|
|
1209
|
+
// from polluting the noop-unverified recall signal.
|
|
1210
|
+
const vocabReady = await cg.healSegmentVocabIfEmpty().catch(() => false);
|
|
1211
|
+
if (!vocabReady) {
|
|
1212
|
+
gate('noop-vocab-empty');
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
const related = cg.getSegmentMatches(proseWords);
|
|
1216
|
+
if (related.length === 0) {
|
|
1217
|
+
gate('noop-unverified');
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
const lines = related
|
|
1221
|
+
.map((m) => ` - ${m.name} (${m.kind} — ${m.filePath}:${m.startLine})`)
|
|
1222
|
+
.join('\n');
|
|
1223
|
+
const exampleQuery = related.slice(0, 3).map((m) => m.name).join(' ');
|
|
1224
|
+
const projectHint = plan.viaSubScan ? ` with projectPath: "${plan.exploreRoot}"` : '';
|
|
1225
|
+
process.stdout.write(`<bhgraph_context note="BHGraph found indexed symbols matching this prompt — query the graph before searching files.">\n` +
|
|
1226
|
+
`This project's BHGraph index contains symbols matching this request:\n${lines}\n` +
|
|
1227
|
+
`Call bhgraph_explore ONCE${projectHint} with the relevant names in one query (e.g. "${exampleQuery}") ` +
|
|
1228
|
+
`to get their source, call paths, and blast radius — cheaper and more complete than Read/Grep.\n${others}` +
|
|
1229
|
+
`</bhgraph_context>\n`);
|
|
1230
|
+
gate('medium-segment');
|
|
1231
|
+
}
|
|
1232
|
+
finally {
|
|
1233
|
+
cg.destroy();
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
// Several indexed sub-projects, none a clear match — don't guess; tell
|
|
1238
|
+
// the agent they exist and how to query one.
|
|
1239
|
+
process.stdout.write(`<bhgraph_context note="BHGraph is available for this workspace's indexed sub-projects — query one by passing projectPath to bhgraph_explore.">\n` +
|
|
1240
|
+
nudge(plan.nudgeProjects, "This workspace's BHGraph indexes live in sub-projects. To use BHGraph, call bhgraph_explore with the projectPath of the relevant one:") +
|
|
1241
|
+
`</bhgraph_context>\n`);
|
|
1242
|
+
gate('nudge-projects');
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
catch {
|
|
1246
|
+
// Degradable by contract: never surface an error to the prompt pipeline.
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
/**
|
|
1250
|
+
* bhgraph node [name]
|
|
1251
|
+
*
|
|
1252
|
+
* The CLI face of the MCP bhgraph_node tool: one symbol's source +
|
|
1253
|
+
* caller/callee trail, or a whole file with line numbers + dependents
|
|
1254
|
+
* (Read-parity). Same subagent/non-MCP rationale as `explore`.
|
|
1255
|
+
*
|
|
1256
|
+
* `name` is OPTIONAL because `--file` (file-read mode) carries no symbol —
|
|
1257
|
+
* a required `<name>` made `bhgraph node -f <file>` unreachable (#1044).
|
|
1258
|
+
*/
|
|
1259
|
+
program
|
|
1260
|
+
.command('node [name]')
|
|
1261
|
+
.description('One symbol\'s source + caller/callee trail, or read a file with line numbers + dependents (same output as the bhgraph_node MCP tool)')
|
|
1262
|
+
.option('-p, --path <path>', 'Project path')
|
|
1263
|
+
.option('-f, --file <file>', 'Treat as file mode (or disambiguate a symbol to this file)')
|
|
1264
|
+
.option('--offset <number>', 'File mode: 1-based start line')
|
|
1265
|
+
.option('--limit <number>', 'File mode: maximum lines')
|
|
1266
|
+
.option('--symbols-only', 'File mode: just the symbol map + dependents')
|
|
1267
|
+
.action(async (name, options) => {
|
|
1268
|
+
// Need a symbol (positional) OR a file (--file / a path-like positional).
|
|
1269
|
+
// With [name] optional, a bare `bhgraph node` reaches here with neither
|
|
1270
|
+
// and must be told what to pass, rather than crashing downstream.
|
|
1271
|
+
if (!name && !options.file) {
|
|
1272
|
+
error("Pass a symbol name (e.g. 'bhgraph node parseToken') or a file (e.g. 'bhgraph node -f src/auth.ts', or 'bhgraph node src/auth.ts').");
|
|
1273
|
+
process.exit(1);
|
|
1274
|
+
}
|
|
1275
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1276
|
+
try {
|
|
1277
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1278
|
+
error(`BHGraph isn't available here — no .bhgraph/ index exists in ${projectPath}. If you are an AI agent: continue with your usual tools; indexing is the user's decision, do not run it yourself. (The project owner can enable BHGraph with 'bhgraph init'.)`);
|
|
1279
|
+
process.exit(1);
|
|
1280
|
+
}
|
|
1281
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1282
|
+
const cg = await BHGraph.open(projectPath);
|
|
1283
|
+
const { ToolHandler } = await Promise.resolve().then(() => __importStar(require('../mcp/tools')));
|
|
1284
|
+
const handler = new ToolHandler(cg);
|
|
1285
|
+
// A name with a path separator is a file read; otherwise a symbol
|
|
1286
|
+
// (use --file for basename-only file reads or to pin an overload).
|
|
1287
|
+
// Both separators: Windows users type src\auth\session.ts. Symbols
|
|
1288
|
+
// never contain either ('/' isn't an identifier char anywhere we
|
|
1289
|
+
// index; C++ scope is '::', JS members '.').
|
|
1290
|
+
const args = {};
|
|
1291
|
+
if (options.file) {
|
|
1292
|
+
args.file = options.file;
|
|
1293
|
+
if (name && name !== options.file)
|
|
1294
|
+
args.symbol = name;
|
|
1295
|
+
}
|
|
1296
|
+
else if (name && (name.includes('/') || name.includes('\\'))) {
|
|
1297
|
+
args.file = name.replace(/\\/g, '/');
|
|
1298
|
+
}
|
|
1299
|
+
else if (name) {
|
|
1300
|
+
args.symbol = name;
|
|
1301
|
+
args.includeCode = true;
|
|
1302
|
+
}
|
|
1303
|
+
if (options.offset)
|
|
1304
|
+
args.offset = parseInt(options.offset, 10);
|
|
1305
|
+
if (options.limit)
|
|
1306
|
+
args.limit = parseInt(options.limit, 10);
|
|
1307
|
+
if (options.symbolsOnly)
|
|
1308
|
+
args.symbolsOnly = true;
|
|
1309
|
+
const result = await handler.execute('bhgraph_node', args);
|
|
1310
|
+
console.log(result.content[0]?.text ?? '');
|
|
1311
|
+
cg.destroy();
|
|
1312
|
+
if (result.isError)
|
|
1313
|
+
process.exit(1);
|
|
1314
|
+
}
|
|
1315
|
+
catch (err) {
|
|
1316
|
+
error(`Node lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1317
|
+
process.exit(1);
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
/**
|
|
1321
|
+
* bhgraph files [path]
|
|
1322
|
+
*/
|
|
1323
|
+
program
|
|
1324
|
+
.command('files')
|
|
1325
|
+
.description('Show project file structure from the index')
|
|
1326
|
+
.option('-p, --path <path>', 'Project path')
|
|
1327
|
+
.option('--filter <dir>', 'Filter to files under this directory')
|
|
1328
|
+
.option('--pattern <glob>', 'Filter files matching this glob pattern')
|
|
1329
|
+
.option('--format <format>', 'Output format (tree, flat, grouped)', 'tree')
|
|
1330
|
+
.option('--max-depth <number>', 'Maximum directory depth for tree format')
|
|
1331
|
+
.option('--no-metadata', 'Hide file metadata (language, symbol count)')
|
|
1332
|
+
.option('-j, --json', 'Output as JSON')
|
|
1333
|
+
.action(async (options) => {
|
|
1334
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1335
|
+
try {
|
|
1336
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1337
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
1338
|
+
process.exit(1);
|
|
1339
|
+
}
|
|
1340
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1341
|
+
const cg = await BHGraph.open(projectPath);
|
|
1342
|
+
let files = cg.getFiles();
|
|
1343
|
+
if (files.length === 0) {
|
|
1344
|
+
info('No files indexed. Run "bhgraph index" first.');
|
|
1345
|
+
cg.destroy();
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
// Filter by path prefix
|
|
1349
|
+
if (options.filter) {
|
|
1350
|
+
const filter = options.filter;
|
|
1351
|
+
files = files.filter(f => f.path.startsWith(filter) || f.path.startsWith('./' + filter));
|
|
1352
|
+
}
|
|
1353
|
+
// Filter by glob pattern
|
|
1354
|
+
if (options.pattern) {
|
|
1355
|
+
const regex = globToRegex(options.pattern);
|
|
1356
|
+
files = files.filter(f => regex.test(f.path));
|
|
1357
|
+
}
|
|
1358
|
+
if (files.length === 0) {
|
|
1359
|
+
info('No files found matching the criteria.');
|
|
1360
|
+
cg.destroy();
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
// JSON output
|
|
1364
|
+
if (options.json) {
|
|
1365
|
+
const output = files.map(f => ({
|
|
1366
|
+
path: f.path,
|
|
1367
|
+
language: f.language,
|
|
1368
|
+
nodeCount: f.nodeCount,
|
|
1369
|
+
size: f.size,
|
|
1370
|
+
}));
|
|
1371
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1372
|
+
cg.destroy();
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
const includeMetadata = options.metadata !== false;
|
|
1376
|
+
const format = options.format || 'tree';
|
|
1377
|
+
const maxDepth = options.maxDepth ? parseInt(options.maxDepth, 10) : undefined;
|
|
1378
|
+
// Format output
|
|
1379
|
+
switch (format) {
|
|
1380
|
+
case 'flat':
|
|
1381
|
+
console.log(chalk.bold(`\nFiles (${files.length}):\n`));
|
|
1382
|
+
for (const file of files.sort((a, b) => a.path.localeCompare(b.path))) {
|
|
1383
|
+
if (includeMetadata) {
|
|
1384
|
+
console.log(` ${file.path} ${chalk.dim(`(${file.language}, ${file.nodeCount} symbols)`)}`);
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
console.log(` ${file.path}`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
break;
|
|
1391
|
+
case 'grouped':
|
|
1392
|
+
console.log(chalk.bold(`\nFiles by Language (${files.length} total):\n`));
|
|
1393
|
+
const byLang = new Map();
|
|
1394
|
+
for (const file of files) {
|
|
1395
|
+
const existing = byLang.get(file.language) || [];
|
|
1396
|
+
existing.push(file);
|
|
1397
|
+
byLang.set(file.language, existing);
|
|
1398
|
+
}
|
|
1399
|
+
const sortedLangs = [...byLang.entries()].sort((a, b) => b[1].length - a[1].length);
|
|
1400
|
+
for (const [lang, langFiles] of sortedLangs) {
|
|
1401
|
+
console.log(chalk.cyan(`${lang} (${langFiles.length}):`));
|
|
1402
|
+
for (const file of langFiles.sort((a, b) => a.path.localeCompare(b.path))) {
|
|
1403
|
+
if (includeMetadata) {
|
|
1404
|
+
console.log(` ${file.path} ${chalk.dim(`(${file.nodeCount} symbols)`)}`);
|
|
1405
|
+
}
|
|
1406
|
+
else {
|
|
1407
|
+
console.log(` ${file.path}`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
console.log();
|
|
1411
|
+
}
|
|
1412
|
+
break;
|
|
1413
|
+
case 'tree':
|
|
1414
|
+
default:
|
|
1415
|
+
console.log(chalk.bold(`\nProject Structure (${files.length} files):\n`));
|
|
1416
|
+
printFileTree(files, includeMetadata, maxDepth, chalk);
|
|
1417
|
+
break;
|
|
1418
|
+
}
|
|
1419
|
+
console.log();
|
|
1420
|
+
cg.destroy();
|
|
1421
|
+
}
|
|
1422
|
+
catch (err) {
|
|
1423
|
+
error(`Failed to list files: ${err instanceof Error ? err.message : String(err)}`);
|
|
1424
|
+
process.exit(1);
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
/**
|
|
1428
|
+
* Normalize a user-supplied file path to the project-relative, forward-slash
|
|
1429
|
+
* form BHGraph stores in the index. Accepts an absolute path, a `./`-prefixed
|
|
1430
|
+
* path, or Windows back-slashes; an empty string when the input is blank. Used
|
|
1431
|
+
* by `bhgraph affected` so `./src/x.ts`, `/abs/repo/src/x.ts`, and
|
|
1432
|
+
* `src/x.ts` all match the same indexed file. (#825)
|
|
1433
|
+
*/
|
|
1434
|
+
function normalizeIndexPath(filePath, projectPath) {
|
|
1435
|
+
let f = filePath.trim();
|
|
1436
|
+
if (!f)
|
|
1437
|
+
return '';
|
|
1438
|
+
if (path.isAbsolute(f))
|
|
1439
|
+
f = path.relative(projectPath, f);
|
|
1440
|
+
// Collapse `.`/`..` segments, then force forward slashes and drop a leading
|
|
1441
|
+
// `./` (path.normalize already strips it on POSIX; explicit for Windows).
|
|
1442
|
+
f = path.normalize(f).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
1443
|
+
return f;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Convert glob pattern to regex
|
|
1447
|
+
*/
|
|
1448
|
+
function globToRegex(pattern) {
|
|
1449
|
+
const escaped = pattern
|
|
1450
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
1451
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
1452
|
+
.replace(/\*/g, '[^/]*')
|
|
1453
|
+
.replace(/\?/g, '[^/]')
|
|
1454
|
+
.replace(/\{\{GLOBSTAR\}\}/g, '.*');
|
|
1455
|
+
return new RegExp(escaped);
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Print files as a tree
|
|
1459
|
+
*/
|
|
1460
|
+
function printFileTree(files, includeMetadata, maxDepth, chalk) {
|
|
1461
|
+
const root = { name: '', children: new Map() };
|
|
1462
|
+
for (const file of files) {
|
|
1463
|
+
const parts = file.path.split('/');
|
|
1464
|
+
let current = root;
|
|
1465
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1466
|
+
const part = parts[i];
|
|
1467
|
+
if (!part)
|
|
1468
|
+
continue;
|
|
1469
|
+
if (!current.children.has(part)) {
|
|
1470
|
+
current.children.set(part, { name: part, children: new Map() });
|
|
1471
|
+
}
|
|
1472
|
+
current = current.children.get(part);
|
|
1473
|
+
if (i === parts.length - 1) {
|
|
1474
|
+
current.file = { language: file.language, nodeCount: file.nodeCount };
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
const renderNode = (node, prefix, isLast, depth) => {
|
|
1479
|
+
if (maxDepth !== undefined && depth > maxDepth)
|
|
1480
|
+
return;
|
|
1481
|
+
const glyphs = (0, glyphs_1.getGlyphs)();
|
|
1482
|
+
const connector = isLast ? glyphs.treeLast : glyphs.treeBranch;
|
|
1483
|
+
const childPrefix = isLast ? ' ' : glyphs.treePipe;
|
|
1484
|
+
if (node.name) {
|
|
1485
|
+
let line = prefix + connector + node.name;
|
|
1486
|
+
if (node.file && includeMetadata) {
|
|
1487
|
+
line += chalk.dim(` (${node.file.language}, ${node.file.nodeCount} symbols)`);
|
|
1488
|
+
}
|
|
1489
|
+
console.log(line);
|
|
1490
|
+
}
|
|
1491
|
+
const children = [...node.children.values()];
|
|
1492
|
+
children.sort((a, b) => {
|
|
1493
|
+
const aIsDir = a.children.size > 0 && !a.file;
|
|
1494
|
+
const bIsDir = b.children.size > 0 && !b.file;
|
|
1495
|
+
if (aIsDir !== bIsDir)
|
|
1496
|
+
return aIsDir ? -1 : 1;
|
|
1497
|
+
return a.name.localeCompare(b.name);
|
|
1498
|
+
});
|
|
1499
|
+
for (let i = 0; i < children.length; i++) {
|
|
1500
|
+
const child = children[i];
|
|
1501
|
+
const nextPrefix = node.name ? prefix + childPrefix : prefix;
|
|
1502
|
+
renderNode(child, nextPrefix, i === children.length - 1, depth + 1);
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
renderNode(root, '', true, 0);
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* bhgraph daemon — interactive manager for the background daemons. Arrow keys
|
|
1509
|
+
* to pick one (the current project's daemon floats to the top, auto-selected),
|
|
1510
|
+
* enter to stop it. Falls back to a plain list when output isn't a TTY.
|
|
1511
|
+
*/
|
|
1512
|
+
program
|
|
1513
|
+
.command('daemon')
|
|
1514
|
+
.aliases(['daemons'])
|
|
1515
|
+
.description('Manage running BHGraph background daemons — pick one and press enter to stop it')
|
|
1516
|
+
.action(async () => {
|
|
1517
|
+
const { listDaemons, stopDaemonAt, stopAllDaemons } = await Promise.resolve().then(() => __importStar(require('../mcp/daemon-registry')));
|
|
1518
|
+
const { runDaemonPicker } = await Promise.resolve().then(() => __importStar(require('../mcp/daemon-manager')));
|
|
1519
|
+
const daemons = listDaemons();
|
|
1520
|
+
if (daemons.length === 0) {
|
|
1521
|
+
info('No BHGraph daemons running.');
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
// No TTY (piped / CI / non-interactive) — can't do arrow-key selection, so
|
|
1525
|
+
// just print what's running instead of crashing on a prompt with no input.
|
|
1526
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
1527
|
+
for (const d of daemons) {
|
|
1528
|
+
console.log(`pid ${d.pid} v${d.version} up ${formatDuration(Date.now() - d.startedAt)} ${d.root}`);
|
|
1529
|
+
}
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
// The current project's daemon floats to the top and is pre-selected.
|
|
1533
|
+
let cwdRoot = null;
|
|
1534
|
+
const found = (0, directory_1.findNearestBHGraphRoot)(process.cwd());
|
|
1535
|
+
if (found) {
|
|
1536
|
+
try {
|
|
1537
|
+
cwdRoot = fs.realpathSync(found);
|
|
1538
|
+
}
|
|
1539
|
+
catch {
|
|
1540
|
+
cwdRoot = found;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
const clack = await importESM('@clack/prompts');
|
|
1544
|
+
clack.intro('BHGraph daemons');
|
|
1545
|
+
await runDaemonPicker({
|
|
1546
|
+
list: listDaemons,
|
|
1547
|
+
stop: stopDaemonAt,
|
|
1548
|
+
stopAll: stopAllDaemons,
|
|
1549
|
+
cwdRoot,
|
|
1550
|
+
now: () => Date.now(),
|
|
1551
|
+
select: (opts) => clack.select(opts),
|
|
1552
|
+
isCancel: (v) => clack.isCancel(v),
|
|
1553
|
+
note: (m) => clack.log.success(m),
|
|
1554
|
+
done: (m) => clack.outro(m),
|
|
1555
|
+
});
|
|
1556
|
+
});
|
|
1557
|
+
/**
|
|
1558
|
+
* bhgraph serve
|
|
1559
|
+
*/
|
|
1560
|
+
program
|
|
1561
|
+
// Hidden from `--help`: this is the stdio entry point an AI agent launches
|
|
1562
|
+
// for itself (the installer wires `args: ['serve','--mcp']` into every
|
|
1563
|
+
// agent's MCP config), not a command a human runs. It still works when
|
|
1564
|
+
// invoked — hiding only removes it from the listing. See the interactive-TTY
|
|
1565
|
+
// guard below, which explains this to anyone who runs it by hand.
|
|
1566
|
+
.command('serve', { hidden: true })
|
|
1567
|
+
.description('Start BHGraph as an MCP server for AI assistants')
|
|
1568
|
+
.option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
|
|
1569
|
+
.option('--mcp', 'Run as MCP server (stdio transport)')
|
|
1570
|
+
.option('--no-watch', 'Disable the file watcher (no auto-sync; useful on slow filesystems like WSL2 /mnt drives)')
|
|
1571
|
+
.action(async (options) => {
|
|
1572
|
+
const projectPath = options.path ? resolveProjectPath(options.path) : undefined;
|
|
1573
|
+
// Commander sets watch=false when --no-watch is passed. Route it through
|
|
1574
|
+
// the same env-var chokepoint the watcher and MCP server already honor.
|
|
1575
|
+
if (options.watch === false) {
|
|
1576
|
+
process.env.BHGRAPH_NO_WATCH = '1';
|
|
1577
|
+
}
|
|
1578
|
+
try {
|
|
1579
|
+
if (options.mcp) {
|
|
1580
|
+
// `serve --mcp` is the stdio MCP server an AI agent launches for itself,
|
|
1581
|
+
// not a command to run by hand. A human in a terminal would otherwise
|
|
1582
|
+
// see it hang waiting for JSON-RPC on stdin, which reads as broken. If
|
|
1583
|
+
// stdin is an interactive TTY, explain instead of hanging. The agent's
|
|
1584
|
+
// pipe and the detached daemon both have a non-TTY stdin, so this only
|
|
1585
|
+
// ever fires for a person who typed it.
|
|
1586
|
+
if (process.stdin.isTTY && !process.env.BHGRAPH_DAEMON_INTERNAL) {
|
|
1587
|
+
console.error(chalk.bold('\nBHGraph MCP server\n'));
|
|
1588
|
+
console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, …)");
|
|
1589
|
+
console.error("starts automatically — you don't run it yourself.");
|
|
1590
|
+
console.error(`\nIt's already wired up by ${chalk.cyan('bhgraph install')}. To check on things:`);
|
|
1591
|
+
console.error(` ${chalk.cyan('bhgraph status')} ${chalk.dim('— is this project indexed and healthy?')}`);
|
|
1592
|
+
console.error(` ${chalk.cyan('bhgraph daemon')} ${chalk.dim('— list or stop background MCP servers')}`);
|
|
1593
|
+
console.error(chalk.dim('\n(Running it directly only does something when an MCP client drives it over stdin.)'));
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
// Start MCP server - it handles initialization lazily based on rootUri from client
|
|
1597
|
+
const { MCPServer } = await Promise.resolve().then(() => __importStar(require('../mcp/index')));
|
|
1598
|
+
const server = new MCPServer(projectPath);
|
|
1599
|
+
await server.start();
|
|
1600
|
+
// Server will run until terminated
|
|
1601
|
+
}
|
|
1602
|
+
else {
|
|
1603
|
+
// Default: show info about MCP mode.
|
|
1604
|
+
// Use stderr so stdout stays clean for any piped/stdio usage.
|
|
1605
|
+
console.error(chalk.bold('\nBHGraph MCP Server\n'));
|
|
1606
|
+
console.error(chalk.blue((0, glyphs_1.getGlyphs)().info) + ' Use --mcp flag to start the MCP server');
|
|
1607
|
+
console.error('\nTo use with Claude Code, add to your MCP configuration:');
|
|
1608
|
+
console.error(chalk.dim(`
|
|
1609
|
+
{
|
|
1610
|
+
"mcpServers": {
|
|
1611
|
+
"bhgraph": {
|
|
1612
|
+
"command": "bhgraph",
|
|
1613
|
+
"args": ["serve", "--mcp"]
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
`));
|
|
1618
|
+
console.error('Available tools:');
|
|
1619
|
+
console.error(chalk.cyan(' bhgraph_explore') + ' - Primary: source of the relevant symbols for any question');
|
|
1620
|
+
console.error(chalk.cyan(' bhgraph_search') + ' - Search for code symbols');
|
|
1621
|
+
console.error(chalk.cyan(' bhgraph_callers') + ' - Find callers of a symbol');
|
|
1622
|
+
console.error(chalk.cyan(' bhgraph_callees') + ' - Find what a symbol calls');
|
|
1623
|
+
console.error(chalk.cyan(' bhgraph_impact') + ' - Analyze impact of changes');
|
|
1624
|
+
console.error(chalk.cyan(' bhgraph_node') + ' - Get symbol details');
|
|
1625
|
+
console.error(chalk.cyan(' bhgraph_files') + ' - Get project file structure');
|
|
1626
|
+
console.error(chalk.cyan(' bhgraph_status') + ' - Get index status');
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
catch (err) {
|
|
1630
|
+
error(`Failed to start server: ${err instanceof Error ? err.message : String(err)}`);
|
|
1631
|
+
process.exit(1);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
/**
|
|
1635
|
+
* bhgraph unlock [path]
|
|
1636
|
+
*/
|
|
1637
|
+
program
|
|
1638
|
+
.command('unlock [path]')
|
|
1639
|
+
.description('Remove a stale lock file that is blocking indexing')
|
|
1640
|
+
.action(async (pathArg) => {
|
|
1641
|
+
const projectPath = resolveProjectPath(pathArg);
|
|
1642
|
+
try {
|
|
1643
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1644
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
const lockPath = path.join((0, directory_1.getBHGraphDir)(projectPath), 'bhgraph.lock');
|
|
1648
|
+
if (!fs.existsSync(lockPath)) {
|
|
1649
|
+
info(`No lock file found ${(0, glyphs_1.getGlyphs)().dash} nothing to do`);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
fs.unlinkSync(lockPath);
|
|
1653
|
+
success('Removed lock file. You can now run indexing again.');
|
|
1654
|
+
}
|
|
1655
|
+
catch (err) {
|
|
1656
|
+
error(`Failed to remove lock: ${err instanceof Error ? err.message : String(err)}`);
|
|
1657
|
+
process.exit(1);
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
/**
|
|
1661
|
+
* bhgraph callers <symbol>
|
|
1662
|
+
*
|
|
1663
|
+
* CLI parity with the MCP graph tools (bhgraph_callers/callees/impact) so the
|
|
1664
|
+
* traversal queries work in scripts, CI, and git hooks without a running MCP
|
|
1665
|
+
* server.
|
|
1666
|
+
*/
|
|
1667
|
+
program
|
|
1668
|
+
.command('callers <symbol>')
|
|
1669
|
+
.description('Find all functions/methods that call a specific symbol')
|
|
1670
|
+
.option('-p, --path <path>', 'Project path')
|
|
1671
|
+
.option('-l, --limit <number>', 'Maximum results', '20')
|
|
1672
|
+
.option('-j, --json', 'Output as JSON')
|
|
1673
|
+
.action(async (symbol, options) => {
|
|
1674
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1675
|
+
try {
|
|
1676
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1677
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
1678
|
+
process.exit(1);
|
|
1679
|
+
}
|
|
1680
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1681
|
+
const cg = await BHGraph.open(projectPath);
|
|
1682
|
+
const limit = parseInt(options.limit || '20', 10);
|
|
1683
|
+
const matches = cg.searchNodes(symbol, { limit: 50 });
|
|
1684
|
+
if (matches.length === 0) {
|
|
1685
|
+
info(`Symbol "${symbol}" not found`);
|
|
1686
|
+
cg.destroy();
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
const seen = new Set();
|
|
1690
|
+
const allCallers = [];
|
|
1691
|
+
for (const match of matches) {
|
|
1692
|
+
const exactMatch = match.node.name === symbol || match.node.name.endsWith(`.${symbol}`) || match.node.name.endsWith(`::${symbol}`);
|
|
1693
|
+
if (!exactMatch && matches.length > 1)
|
|
1694
|
+
continue;
|
|
1695
|
+
for (const c of cg.getCallers(match.node.id)) {
|
|
1696
|
+
if (!seen.has(c.node.id)) {
|
|
1697
|
+
seen.add(c.node.id);
|
|
1698
|
+
allCallers.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
// Fallback: if exact filter removed everything, use the top match
|
|
1703
|
+
if (allCallers.length === 0 && matches[0]) {
|
|
1704
|
+
for (const c of cg.getCallers(matches[0].node.id)) {
|
|
1705
|
+
if (!seen.has(c.node.id)) {
|
|
1706
|
+
seen.add(c.node.id);
|
|
1707
|
+
allCallers.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
const limited = allCallers.slice(0, limit);
|
|
1712
|
+
if (options.json) {
|
|
1713
|
+
console.log(JSON.stringify({ symbol, callers: limited }, null, 2));
|
|
1714
|
+
}
|
|
1715
|
+
else if (limited.length === 0) {
|
|
1716
|
+
info(`No callers found for "${symbol}"`);
|
|
1717
|
+
}
|
|
1718
|
+
else {
|
|
1719
|
+
console.log(chalk.bold(`\nCallers of "${symbol}" (${limited.length}):\n`));
|
|
1720
|
+
for (const node of limited) {
|
|
1721
|
+
const loc = node.startLine ? `:${node.startLine}` : '';
|
|
1722
|
+
console.log(chalk.cyan(node.kind.padEnd(12)) +
|
|
1723
|
+
chalk.white(node.name));
|
|
1724
|
+
console.log(chalk.dim(` ${node.filePath}${loc}`));
|
|
1725
|
+
console.log();
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
cg.destroy();
|
|
1729
|
+
}
|
|
1730
|
+
catch (err) {
|
|
1731
|
+
error(`callers failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1732
|
+
process.exit(1);
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1735
|
+
/**
|
|
1736
|
+
* bhgraph callees <symbol>
|
|
1737
|
+
*/
|
|
1738
|
+
program
|
|
1739
|
+
.command('callees <symbol>')
|
|
1740
|
+
.description('Find all functions/methods that a specific symbol calls')
|
|
1741
|
+
.option('-p, --path <path>', 'Project path')
|
|
1742
|
+
.option('-l, --limit <number>', 'Maximum results', '20')
|
|
1743
|
+
.option('-j, --json', 'Output as JSON')
|
|
1744
|
+
.action(async (symbol, options) => {
|
|
1745
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1746
|
+
try {
|
|
1747
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1748
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
1749
|
+
process.exit(1);
|
|
1750
|
+
}
|
|
1751
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1752
|
+
const cg = await BHGraph.open(projectPath);
|
|
1753
|
+
const limit = parseInt(options.limit || '20', 10);
|
|
1754
|
+
const matches = cg.searchNodes(symbol, { limit: 50 });
|
|
1755
|
+
if (matches.length === 0) {
|
|
1756
|
+
info(`Symbol "${symbol}" not found`);
|
|
1757
|
+
cg.destroy();
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
const seen = new Set();
|
|
1761
|
+
const allCallees = [];
|
|
1762
|
+
for (const match of matches) {
|
|
1763
|
+
const exactMatch = match.node.name === symbol || match.node.name.endsWith(`.${symbol}`) || match.node.name.endsWith(`::${symbol}`);
|
|
1764
|
+
if (!exactMatch && matches.length > 1)
|
|
1765
|
+
continue;
|
|
1766
|
+
for (const c of cg.getCallees(match.node.id)) {
|
|
1767
|
+
if (!seen.has(c.node.id)) {
|
|
1768
|
+
seen.add(c.node.id);
|
|
1769
|
+
allCallees.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
if (allCallees.length === 0 && matches[0]) {
|
|
1774
|
+
for (const c of cg.getCallees(matches[0].node.id)) {
|
|
1775
|
+
if (!seen.has(c.node.id)) {
|
|
1776
|
+
seen.add(c.node.id);
|
|
1777
|
+
allCallees.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
const limited = allCallees.slice(0, limit);
|
|
1782
|
+
if (options.json) {
|
|
1783
|
+
console.log(JSON.stringify({ symbol, callees: limited }, null, 2));
|
|
1784
|
+
}
|
|
1785
|
+
else if (limited.length === 0) {
|
|
1786
|
+
info(`No callees found for "${symbol}"`);
|
|
1787
|
+
}
|
|
1788
|
+
else {
|
|
1789
|
+
console.log(chalk.bold(`\nCallees of "${symbol}" (${limited.length}):\n`));
|
|
1790
|
+
for (const node of limited) {
|
|
1791
|
+
const loc = node.startLine ? `:${node.startLine}` : '';
|
|
1792
|
+
console.log(chalk.cyan(node.kind.padEnd(12)) +
|
|
1793
|
+
chalk.white(node.name));
|
|
1794
|
+
console.log(chalk.dim(` ${node.filePath}${loc}`));
|
|
1795
|
+
console.log();
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
cg.destroy();
|
|
1799
|
+
}
|
|
1800
|
+
catch (err) {
|
|
1801
|
+
error(`callees failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1802
|
+
process.exit(1);
|
|
1803
|
+
}
|
|
1804
|
+
});
|
|
1805
|
+
/**
|
|
1806
|
+
* bhgraph impact <symbol>
|
|
1807
|
+
*/
|
|
1808
|
+
program
|
|
1809
|
+
.command('impact <symbol>')
|
|
1810
|
+
.description('Analyze what code is affected by changing a symbol')
|
|
1811
|
+
.option('-p, --path <path>', 'Project path')
|
|
1812
|
+
.option('-d, --depth <number>', 'Traversal depth', '2')
|
|
1813
|
+
.option('-j, --json', 'Output as JSON')
|
|
1814
|
+
.action(async (symbol, options) => {
|
|
1815
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1816
|
+
try {
|
|
1817
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1818
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
1819
|
+
process.exit(1);
|
|
1820
|
+
}
|
|
1821
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1822
|
+
const cg = await BHGraph.open(projectPath);
|
|
1823
|
+
const depth = Math.min(Math.max(parseInt(options.depth || '2', 10), 1), 10);
|
|
1824
|
+
const matches = cg.searchNodes(symbol, { limit: 50 });
|
|
1825
|
+
if (matches.length === 0) {
|
|
1826
|
+
info(`Symbol "${symbol}" not found`);
|
|
1827
|
+
cg.destroy();
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
// Merge impact subgraphs across all exact-matching symbols
|
|
1831
|
+
const mergedNodes = new Map();
|
|
1832
|
+
const seenEdges = new Set();
|
|
1833
|
+
let edgeCount = 0;
|
|
1834
|
+
for (const match of matches) {
|
|
1835
|
+
const exactMatch = match.node.name === symbol || match.node.name.endsWith(`.${symbol}`) || match.node.name.endsWith(`::${symbol}`);
|
|
1836
|
+
if (!exactMatch && matches.length > 1)
|
|
1837
|
+
continue;
|
|
1838
|
+
const impact = cg.getImpactRadius(match.node.id, depth);
|
|
1839
|
+
for (const [id, n] of impact.nodes) {
|
|
1840
|
+
mergedNodes.set(id, { name: n.name, kind: n.kind, filePath: n.filePath, startLine: n.startLine });
|
|
1841
|
+
}
|
|
1842
|
+
for (const e of impact.edges) {
|
|
1843
|
+
const key = `${e.source}->${e.target}:${e.kind}`;
|
|
1844
|
+
if (!seenEdges.has(key)) {
|
|
1845
|
+
seenEdges.add(key);
|
|
1846
|
+
edgeCount++;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
// Fallback to top match if exact filter removed everything
|
|
1851
|
+
if (mergedNodes.size === 0 && matches[0]) {
|
|
1852
|
+
const impact = cg.getImpactRadius(matches[0].node.id, depth);
|
|
1853
|
+
for (const [id, n] of impact.nodes) {
|
|
1854
|
+
mergedNodes.set(id, { name: n.name, kind: n.kind, filePath: n.filePath, startLine: n.startLine });
|
|
1855
|
+
}
|
|
1856
|
+
edgeCount = impact.edges.length;
|
|
1857
|
+
}
|
|
1858
|
+
if (options.json) {
|
|
1859
|
+
console.log(JSON.stringify({
|
|
1860
|
+
symbol,
|
|
1861
|
+
depth,
|
|
1862
|
+
nodeCount: mergedNodes.size,
|
|
1863
|
+
edgeCount,
|
|
1864
|
+
affected: Array.from(mergedNodes.values()),
|
|
1865
|
+
}, null, 2));
|
|
1866
|
+
}
|
|
1867
|
+
else if (mergedNodes.size === 0) {
|
|
1868
|
+
info(`No affected symbols found for "${symbol}"`);
|
|
1869
|
+
}
|
|
1870
|
+
else {
|
|
1871
|
+
console.log(chalk.bold(`\nImpact of changing "${symbol}" — ${mergedNodes.size} affected symbols:\n`));
|
|
1872
|
+
// Group by file
|
|
1873
|
+
const byFile = new Map();
|
|
1874
|
+
for (const node of mergedNodes.values()) {
|
|
1875
|
+
const list = byFile.get(node.filePath) || [];
|
|
1876
|
+
list.push({ name: node.name, kind: node.kind, startLine: node.startLine });
|
|
1877
|
+
byFile.set(node.filePath, list);
|
|
1878
|
+
}
|
|
1879
|
+
for (const [file, nodes] of byFile) {
|
|
1880
|
+
console.log(chalk.cyan(file));
|
|
1881
|
+
for (const node of nodes) {
|
|
1882
|
+
const loc = node.startLine ? `:${node.startLine}` : '';
|
|
1883
|
+
console.log(` ${chalk.dim(node.kind.padEnd(12))}${node.name}${chalk.dim(loc)}`);
|
|
1884
|
+
}
|
|
1885
|
+
console.log();
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
cg.destroy();
|
|
1889
|
+
}
|
|
1890
|
+
catch (err) {
|
|
1891
|
+
error(`impact failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1892
|
+
process.exit(1);
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
/**
|
|
1896
|
+
* bhgraph affected [files...]
|
|
1897
|
+
*
|
|
1898
|
+
* Find test files affected by the given source files.
|
|
1899
|
+
* Traces dependency edges transitively to find test files that depend on changed code.
|
|
1900
|
+
*
|
|
1901
|
+
* Usage:
|
|
1902
|
+
* git diff --name-only | bhgraph affected --stdin
|
|
1903
|
+
* bhgraph affected src/lib/components/Editor.svelte src/routes/+page.svelte
|
|
1904
|
+
*/
|
|
1905
|
+
program
|
|
1906
|
+
.command('affected [files...]')
|
|
1907
|
+
.description('Find test files affected by changed source files')
|
|
1908
|
+
.option('-p, --path <path>', 'Project path')
|
|
1909
|
+
.option('--stdin', 'Read file list from stdin (one per line)')
|
|
1910
|
+
.option('-d, --depth <number>', 'Max dependency traversal depth', '5')
|
|
1911
|
+
.option('-f, --filter <glob>', 'Custom glob filter for test files (e.g. "e2e/*.spec.ts")')
|
|
1912
|
+
.option('-j, --json', 'Output as JSON')
|
|
1913
|
+
.option('-q, --quiet', 'Only output file paths, no decoration')
|
|
1914
|
+
.action(async (fileArgs, options) => {
|
|
1915
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1916
|
+
try {
|
|
1917
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1918
|
+
error(`BHGraph not initialized in ${projectPath}`);
|
|
1919
|
+
process.exit(1);
|
|
1920
|
+
}
|
|
1921
|
+
// Collect changed files from args or stdin
|
|
1922
|
+
let changedFiles = [...(fileArgs || [])];
|
|
1923
|
+
if (options.stdin) {
|
|
1924
|
+
const stdinData = fs.readFileSync(0, 'utf-8');
|
|
1925
|
+
const stdinFiles = stdinData.split('\n').map(f => f.trim()).filter(Boolean);
|
|
1926
|
+
changedFiles.push(...stdinFiles);
|
|
1927
|
+
}
|
|
1928
|
+
// Normalize inputs to the project-relative, forward-slash form the index
|
|
1929
|
+
// stores. Without this, `affected ./src/x.ts`, an absolute path (what a
|
|
1930
|
+
// wrapping script often passes), or a Windows back-slash path silently
|
|
1931
|
+
// matches nothing and reports 0 affected tests. (#825)
|
|
1932
|
+
changedFiles = changedFiles
|
|
1933
|
+
.map((f) => normalizeIndexPath(f, projectPath))
|
|
1934
|
+
.filter(Boolean);
|
|
1935
|
+
if (changedFiles.length === 0) {
|
|
1936
|
+
if (!options.quiet)
|
|
1937
|
+
info('No files provided. Use file arguments or --stdin.');
|
|
1938
|
+
process.exit(0);
|
|
1939
|
+
}
|
|
1940
|
+
const { default: BHGraph } = await loadBHGraph();
|
|
1941
|
+
const cg = await BHGraph.open(projectPath);
|
|
1942
|
+
const maxDepth = parseInt(options.depth || '5', 10);
|
|
1943
|
+
// Common test file patterns
|
|
1944
|
+
const defaultTestPatterns = [
|
|
1945
|
+
/\.spec\./,
|
|
1946
|
+
/\.test\./,
|
|
1947
|
+
/\/__tests__\//,
|
|
1948
|
+
/\/tests?\//,
|
|
1949
|
+
/\/e2e\//,
|
|
1950
|
+
/\/spec\//,
|
|
1951
|
+
];
|
|
1952
|
+
// Custom filter pattern
|
|
1953
|
+
let customFilter = null;
|
|
1954
|
+
if (options.filter) {
|
|
1955
|
+
// Convert glob to regex: ** → .+, * → [^/]*, . → \.
|
|
1956
|
+
const regex = options.filter
|
|
1957
|
+
.replace(/[+[\]{}()^$|\\]/g, '\\$&')
|
|
1958
|
+
.replace(/\./g, '\\.')
|
|
1959
|
+
.replace(/\*\*/g, '.+')
|
|
1960
|
+
.replace(/\*/g, '[^/]*');
|
|
1961
|
+
customFilter = new RegExp(regex);
|
|
1962
|
+
}
|
|
1963
|
+
function isTestFile(filePath) {
|
|
1964
|
+
if (customFilter)
|
|
1965
|
+
return customFilter.test(filePath);
|
|
1966
|
+
return defaultTestPatterns.some(p => p.test(filePath));
|
|
1967
|
+
}
|
|
1968
|
+
// BFS to find all transitive dependents of changed files, filtered to test files
|
|
1969
|
+
const affectedTests = new Set();
|
|
1970
|
+
const allDependents = new Set();
|
|
1971
|
+
for (const file of changedFiles) {
|
|
1972
|
+
// If the changed file is itself a test file, include it
|
|
1973
|
+
if (isTestFile(file)) {
|
|
1974
|
+
affectedTests.add(file);
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
// BFS through dependents
|
|
1978
|
+
const queue = [{ file, depth: 0 }];
|
|
1979
|
+
const visited = new Set();
|
|
1980
|
+
visited.add(file);
|
|
1981
|
+
while (queue.length > 0) {
|
|
1982
|
+
const current = queue.shift();
|
|
1983
|
+
if (current.depth >= maxDepth)
|
|
1984
|
+
continue;
|
|
1985
|
+
const dependents = cg.getFileDependents(current.file);
|
|
1986
|
+
for (const dep of dependents) {
|
|
1987
|
+
if (visited.has(dep))
|
|
1988
|
+
continue;
|
|
1989
|
+
visited.add(dep);
|
|
1990
|
+
allDependents.add(dep);
|
|
1991
|
+
if (isTestFile(dep)) {
|
|
1992
|
+
affectedTests.add(dep);
|
|
1993
|
+
}
|
|
1994
|
+
else {
|
|
1995
|
+
queue.push({ file: dep, depth: current.depth + 1 });
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
const sortedTests = Array.from(affectedTests).sort();
|
|
2001
|
+
// Output
|
|
2002
|
+
if (options.json) {
|
|
2003
|
+
console.log(JSON.stringify({
|
|
2004
|
+
changedFiles,
|
|
2005
|
+
affectedTests: sortedTests,
|
|
2006
|
+
totalDependentsTraversed: allDependents.size,
|
|
2007
|
+
}, null, 2));
|
|
2008
|
+
}
|
|
2009
|
+
else if (options.quiet) {
|
|
2010
|
+
for (const t of sortedTests)
|
|
2011
|
+
console.log(t);
|
|
2012
|
+
}
|
|
2013
|
+
else {
|
|
2014
|
+
if (sortedTests.length === 0) {
|
|
2015
|
+
info('No test files affected by the changed files.');
|
|
2016
|
+
}
|
|
2017
|
+
else {
|
|
2018
|
+
console.log(chalk.bold(`\nAffected test files (${sortedTests.length}):\n`));
|
|
2019
|
+
for (const t of sortedTests) {
|
|
2020
|
+
console.log(' ' + chalk.cyan(t));
|
|
2021
|
+
}
|
|
2022
|
+
console.log();
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
cg.destroy();
|
|
2026
|
+
}
|
|
2027
|
+
catch (err) {
|
|
2028
|
+
error(`Affected analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2029
|
+
process.exit(1);
|
|
2030
|
+
}
|
|
2031
|
+
});
|
|
2032
|
+
/**
|
|
2033
|
+
* bhgraph install
|
|
2034
|
+
*/
|
|
2035
|
+
program
|
|
2036
|
+
.command('install')
|
|
2037
|
+
.description('Install bhgraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent)')
|
|
2038
|
+
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
|
|
2039
|
+
.option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
|
|
2040
|
+
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
|
|
2041
|
+
.option('--no-permissions', 'Skip writing the auto-allow permissions list (Claude Code only)')
|
|
2042
|
+
.option('--print-config <id>', 'Print MCP config snippet for the named agent and exit (no file writes)')
|
|
2043
|
+
.action(async (opts) => {
|
|
2044
|
+
if (opts.printConfig) {
|
|
2045
|
+
const { getTarget, listTargetIds } = await Promise.resolve().then(() => __importStar(require('../installer/targets/registry')));
|
|
2046
|
+
const target = getTarget(opts.printConfig);
|
|
2047
|
+
if (!target) {
|
|
2048
|
+
const known = listTargetIds().join(', ');
|
|
2049
|
+
error(`Unknown target "${opts.printConfig}". Known: ${known}.`);
|
|
2050
|
+
process.exit(1);
|
|
2051
|
+
}
|
|
2052
|
+
const loc = (opts.location === 'local' ? 'local' : 'global');
|
|
2053
|
+
process.stdout.write(target.printConfig(loc));
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
const { runInstallerWithOptions } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
2057
|
+
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
2058
|
+
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
2059
|
+
process.exit(1);
|
|
2060
|
+
}
|
|
2061
|
+
try {
|
|
2062
|
+
// Commander's `--no-permissions` makes `opts.permissions === false`;
|
|
2063
|
+
// omitting the flag leaves it `true` (the positive-form default).
|
|
2064
|
+
// We MUST treat the default-true as "user did not override — let
|
|
2065
|
+
// the orchestrator prompt" and only forward an explicit `false`
|
|
2066
|
+
// (or `true` when --yes implies it). Otherwise the auto-allow
|
|
2067
|
+
// prompt is silently skipped on every interactive run.
|
|
2068
|
+
const explicitNoPermissions = opts.permissions === false;
|
|
2069
|
+
const autoAllow = explicitNoPermissions
|
|
2070
|
+
? false
|
|
2071
|
+
: opts.yes
|
|
2072
|
+
? true
|
|
2073
|
+
: undefined;
|
|
2074
|
+
await runInstallerWithOptions({
|
|
2075
|
+
target: opts.target,
|
|
2076
|
+
location: opts.location,
|
|
2077
|
+
autoAllow,
|
|
2078
|
+
yes: opts.yes,
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
catch (err) {
|
|
2082
|
+
error(err instanceof Error ? err.message : String(err));
|
|
2083
|
+
process.exit(1);
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
2086
|
+
/**
|
|
2087
|
+
* bhgraph uninstall
|
|
2088
|
+
*
|
|
2089
|
+
* Inverse of `install`. Removes the bhgraph MCP server entry,
|
|
2090
|
+
* instructions block, and permissions from every agent (or a
|
|
2091
|
+
* `--target` subset). Prompts global-vs-local when not given. Does NOT
|
|
2092
|
+
* delete the `.bhgraph/` index — that's `bhgraph uninit`.
|
|
2093
|
+
*/
|
|
2094
|
+
program
|
|
2095
|
+
.command('uninstall')
|
|
2096
|
+
.description('Remove bhgraph from your agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent)')
|
|
2097
|
+
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
|
|
2098
|
+
.option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
|
|
2099
|
+
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
|
|
2100
|
+
.action(async (opts) => {
|
|
2101
|
+
const { runUninstaller } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
2102
|
+
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
2103
|
+
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
2104
|
+
process.exit(1);
|
|
2105
|
+
}
|
|
2106
|
+
try {
|
|
2107
|
+
await runUninstaller({
|
|
2108
|
+
target: opts.target,
|
|
2109
|
+
location: opts.location,
|
|
2110
|
+
yes: opts.yes,
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
catch (err) {
|
|
2114
|
+
error(err instanceof Error ? err.message : String(err));
|
|
2115
|
+
process.exit(1);
|
|
2116
|
+
}
|
|
2117
|
+
});
|
|
2118
|
+
/**
|
|
2119
|
+
* bhgraph telemetry [on|off|status]
|
|
2120
|
+
*/
|
|
2121
|
+
program
|
|
2122
|
+
.command('telemetry [action]')
|
|
2123
|
+
.description('Show or change anonymous usage telemetry (status, on, off)')
|
|
2124
|
+
.action((action) => {
|
|
2125
|
+
const t = (0, telemetry_1.getTelemetry)();
|
|
2126
|
+
if (action === 'on' || action === 'off') {
|
|
2127
|
+
t.setEnabled(action === 'on', 'cli');
|
|
2128
|
+
if (action === 'on') {
|
|
2129
|
+
success('Telemetry enabled — anonymous usage stats only (no code, paths, or names).');
|
|
2130
|
+
}
|
|
2131
|
+
else {
|
|
2132
|
+
success('Telemetry disabled. Buffered, unsent data was deleted.');
|
|
2133
|
+
}
|
|
2134
|
+
const effective = t.getStatus();
|
|
2135
|
+
if (effective.decidedBy === 'DO_NOT_TRACK' || effective.decidedBy === 'BHGRAPH_TELEMETRY') {
|
|
2136
|
+
warn(`The ${effective.decidedBy} environment variable overrides this choice — ` +
|
|
2137
|
+
`effective state right now: ${effective.enabled ? 'enabled' : 'disabled'}.`);
|
|
2138
|
+
}
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
if (action !== undefined && action !== 'status') {
|
|
2142
|
+
error(`Unknown action: ${action} (expected status, on, or off)`);
|
|
2143
|
+
process.exit(1);
|
|
2144
|
+
}
|
|
2145
|
+
const s = t.getStatus();
|
|
2146
|
+
const decidedBy = {
|
|
2147
|
+
DO_NOT_TRACK: 'DO_NOT_TRACK environment variable',
|
|
2148
|
+
BHGRAPH_TELEMETRY: 'BHGRAPH_TELEMETRY environment variable',
|
|
2149
|
+
config: 'your saved choice',
|
|
2150
|
+
default: 'default',
|
|
2151
|
+
};
|
|
2152
|
+
console.log(`\nTelemetry: ${s.enabled ? chalk.green('enabled') : chalk.yellow('disabled')} ${chalk.dim(`(${decidedBy[s.decidedBy]})`)}`);
|
|
2153
|
+
console.log(`Machine ID: ${s.machineId ?? chalk.dim('(random UUID, created on first use)')}`);
|
|
2154
|
+
console.log(`Config: ${s.configPath}`);
|
|
2155
|
+
console.log(chalk.dim(`\nExactly what is collected (and never collected): ${telemetry_1.TELEMETRY_DOCS}\n`));
|
|
2156
|
+
});
|
|
2157
|
+
/**
|
|
2158
|
+
* bhgraph upgrade [version]
|
|
2159
|
+
*
|
|
2160
|
+
* Self-update, however BHGraph was installed (bundle via install.sh/.ps1,
|
|
2161
|
+
* npm-global, npx, or a source checkout). See ../upgrade for the detection and
|
|
2162
|
+
* per-method upgrade logic.
|
|
2163
|
+
*/
|
|
2164
|
+
program
|
|
2165
|
+
.command('upgrade [version]')
|
|
2166
|
+
.description('Update BHGraph to the latest release (or a specific version)')
|
|
2167
|
+
.option('--check', 'Check whether an update is available without installing')
|
|
2168
|
+
.option('-f, --force', 'Reinstall even if already on the target version')
|
|
2169
|
+
.action(async (versionArg, options) => {
|
|
2170
|
+
const up = await Promise.resolve().then(() => __importStar(require('../upgrade')));
|
|
2171
|
+
const method = up.detectInstallMethod({
|
|
2172
|
+
filename: __filename,
|
|
2173
|
+
platform: process.platform,
|
|
2174
|
+
cwd: process.cwd(),
|
|
2175
|
+
});
|
|
2176
|
+
const pin = versionArg || process.env.BHGRAPH_VERSION || undefined;
|
|
2177
|
+
const code = await up.runUpgrade({ version: pin, check: options.check, force: options.force }, {
|
|
2178
|
+
currentVersion: packageJson.version,
|
|
2179
|
+
method,
|
|
2180
|
+
resolveLatest: () => up.resolveLatestVersion(),
|
|
2181
|
+
run: up.defaultRun,
|
|
2182
|
+
hasCommand: up.hasCommand,
|
|
2183
|
+
log: (m) => console.log(m),
|
|
2184
|
+
warn: (m) => warn(m),
|
|
2185
|
+
error: (m) => error(m),
|
|
2186
|
+
platform: process.platform,
|
|
2187
|
+
});
|
|
2188
|
+
process.exit(code);
|
|
2189
|
+
});
|
|
2190
|
+
/**
|
|
2191
|
+
* bhgraph version
|
|
2192
|
+
*
|
|
2193
|
+
* The bare-noun form of `--version`. commander already provides `--version`
|
|
2194
|
+
* and `-V`, and the `-v` / `-version` spellings are intercepted before parse
|
|
2195
|
+
* (see top of main). This subcommand makes `bhgraph version` work and lists
|
|
2196
|
+
* the version affordance in `bhgraph --help`.
|
|
2197
|
+
*/
|
|
2198
|
+
program
|
|
2199
|
+
.command('version')
|
|
2200
|
+
.description('Print the installed BHGraph version (also: -v, --version)')
|
|
2201
|
+
.action(() => {
|
|
2202
|
+
console.log(packageJson.version);
|
|
2203
|
+
});
|
|
2204
|
+
// Parse and run
|
|
2205
|
+
program.parse();
|
|
2206
|
+
} // end main()
|
|
2207
|
+
//# sourceMappingURL=bhgraph.js.map
|