@selvakumaresra/specship 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +6 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/agents/specship-explorer.md +29 -0
- package/commands/cg-drifted.md +23 -0
- package/commands/cg-explore.md +13 -0
- package/commands/cg-fix.md +21 -0
- package/commands/cg-impact.md +13 -0
- package/commands/cg-implement.md +23 -0
- package/commands/cg-relink.md +21 -0
- package/commands/cg-spec.md +19 -0
- package/commands/cg-sync.md +8 -0
- package/commands/cg-trace.md +13 -0
- package/dist/bin/node-version-check.d.ts +37 -0
- package/dist/bin/node-version-check.d.ts.map +1 -0
- package/dist/bin/node-version-check.js +79 -0
- package/dist/bin/node-version-check.js.map +1 -0
- package/dist/bin/specship.d.ts +25 -0
- package/dist/bin/specship.d.ts.map +1 -0
- package/dist/bin/specship.js +2018 -0
- package/dist/bin/specship.js.map +1 -0
- package/dist/bin/uninstall.d.ts +13 -0
- package/dist/bin/uninstall.d.ts.map +1 -0
- package/dist/bin/uninstall.js +35 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/context/formatter.d.ts +30 -0
- package/dist/context/formatter.d.ts.map +1 -0
- package/dist/context/formatter.js +263 -0
- package/dist/context/formatter.js.map +1 -0
- package/dist/context/index.d.ts +119 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +1289 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/markers.d.ts +19 -0
- package/dist/context/markers.d.ts.map +1 -0
- package/dist/context/markers.js +22 -0
- package/dist/context/markers.js.map +1 -0
- package/dist/db/index.d.ts +101 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +276 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations.d.ts +44 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +427 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/queries.d.ts +357 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +1504 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.sql +410 -0
- package/dist/db/spec-queries.d.ts +101 -0
- package/dist/db/spec-queries.d.ts.map +1 -0
- package/dist/db/spec-queries.js +675 -0
- package/dist/db/spec-queries.js.map +1 -0
- package/dist/db/sqlite-adapter.d.ts +65 -0
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.js +214 -0
- package/dist/db/sqlite-adapter.js.map +1 -0
- package/dist/directory.d.ts +57 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +253 -0
- package/dist/directory.js.map +1 -0
- package/dist/errors.d.ts +136 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +219 -0
- package/dist/errors.js.map +1 -0
- package/dist/extraction/dfm-extractor.d.ts +31 -0
- package/dist/extraction/dfm-extractor.d.ts.map +1 -0
- package/dist/extraction/dfm-extractor.js +151 -0
- package/dist/extraction/dfm-extractor.js.map +1 -0
- package/dist/extraction/generated-detection.d.ts +30 -0
- package/dist/extraction/generated-detection.d.ts.map +1 -0
- package/dist/extraction/generated-detection.js +80 -0
- package/dist/extraction/generated-detection.js.map +1 -0
- package/dist/extraction/grammars.d.ts +100 -0
- package/dist/extraction/grammars.d.ts.map +1 -0
- package/dist/extraction/grammars.js +426 -0
- package/dist/extraction/grammars.js.map +1 -0
- package/dist/extraction/index.d.ts +138 -0
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +1394 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/languages/c-cpp.d.ts +4 -0
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
- package/dist/extraction/languages/c-cpp.js +171 -0
- package/dist/extraction/languages/c-cpp.js.map +1 -0
- package/dist/extraction/languages/csharp.d.ts +3 -0
- package/dist/extraction/languages/csharp.d.ts.map +1 -0
- package/dist/extraction/languages/csharp.js +73 -0
- package/dist/extraction/languages/csharp.js.map +1 -0
- package/dist/extraction/languages/dart.d.ts +3 -0
- package/dist/extraction/languages/dart.d.ts.map +1 -0
- package/dist/extraction/languages/dart.js +192 -0
- package/dist/extraction/languages/dart.js.map +1 -0
- package/dist/extraction/languages/go.d.ts +3 -0
- package/dist/extraction/languages/go.d.ts.map +1 -0
- package/dist/extraction/languages/go.js +74 -0
- package/dist/extraction/languages/go.js.map +1 -0
- package/dist/extraction/languages/index.d.ts +10 -0
- package/dist/extraction/languages/index.d.ts.map +1 -0
- package/dist/extraction/languages/index.js +51 -0
- package/dist/extraction/languages/index.js.map +1 -0
- package/dist/extraction/languages/java.d.ts +3 -0
- package/dist/extraction/languages/java.d.ts.map +1 -0
- package/dist/extraction/languages/java.js +70 -0
- package/dist/extraction/languages/java.js.map +1 -0
- package/dist/extraction/languages/javascript.d.ts +3 -0
- package/dist/extraction/languages/javascript.d.ts.map +1 -0
- package/dist/extraction/languages/javascript.js +90 -0
- package/dist/extraction/languages/javascript.js.map +1 -0
- package/dist/extraction/languages/kotlin.d.ts +3 -0
- package/dist/extraction/languages/kotlin.d.ts.map +1 -0
- package/dist/extraction/languages/kotlin.js +259 -0
- package/dist/extraction/languages/kotlin.js.map +1 -0
- package/dist/extraction/languages/lua.d.ts +3 -0
- package/dist/extraction/languages/lua.d.ts.map +1 -0
- package/dist/extraction/languages/lua.js +150 -0
- package/dist/extraction/languages/lua.js.map +1 -0
- package/dist/extraction/languages/luau.d.ts +3 -0
- package/dist/extraction/languages/luau.d.ts.map +1 -0
- package/dist/extraction/languages/luau.js +37 -0
- package/dist/extraction/languages/luau.js.map +1 -0
- package/dist/extraction/languages/objc.d.ts +3 -0
- package/dist/extraction/languages/objc.d.ts.map +1 -0
- package/dist/extraction/languages/objc.js +133 -0
- package/dist/extraction/languages/objc.js.map +1 -0
- package/dist/extraction/languages/pascal.d.ts +3 -0
- package/dist/extraction/languages/pascal.d.ts.map +1 -0
- package/dist/extraction/languages/pascal.js +66 -0
- package/dist/extraction/languages/pascal.js.map +1 -0
- package/dist/extraction/languages/php.d.ts +3 -0
- package/dist/extraction/languages/php.d.ts.map +1 -0
- package/dist/extraction/languages/php.js +107 -0
- package/dist/extraction/languages/php.js.map +1 -0
- package/dist/extraction/languages/python.d.ts +3 -0
- package/dist/extraction/languages/python.d.ts.map +1 -0
- package/dist/extraction/languages/python.js +56 -0
- package/dist/extraction/languages/python.js.map +1 -0
- package/dist/extraction/languages/ruby.d.ts +3 -0
- package/dist/extraction/languages/ruby.d.ts.map +1 -0
- package/dist/extraction/languages/ruby.js +114 -0
- package/dist/extraction/languages/ruby.js.map +1 -0
- package/dist/extraction/languages/rust.d.ts +3 -0
- package/dist/extraction/languages/rust.d.ts.map +1 -0
- package/dist/extraction/languages/rust.js +109 -0
- package/dist/extraction/languages/rust.js.map +1 -0
- package/dist/extraction/languages/scala.d.ts +3 -0
- package/dist/extraction/languages/scala.d.ts.map +1 -0
- package/dist/extraction/languages/scala.js +139 -0
- package/dist/extraction/languages/scala.js.map +1 -0
- package/dist/extraction/languages/swift.d.ts +3 -0
- package/dist/extraction/languages/swift.d.ts.map +1 -0
- package/dist/extraction/languages/swift.js +91 -0
- package/dist/extraction/languages/swift.js.map +1 -0
- package/dist/extraction/languages/typescript.d.ts +3 -0
- package/dist/extraction/languages/typescript.d.ts.map +1 -0
- package/dist/extraction/languages/typescript.js +129 -0
- package/dist/extraction/languages/typescript.js.map +1 -0
- package/dist/extraction/liquid-extractor.d.ts +52 -0
- package/dist/extraction/liquid-extractor.d.ts.map +1 -0
- package/dist/extraction/liquid-extractor.js +313 -0
- package/dist/extraction/liquid-extractor.js.map +1 -0
- package/dist/extraction/mybatis-extractor.d.ts +48 -0
- package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
- package/dist/extraction/mybatis-extractor.js +198 -0
- package/dist/extraction/mybatis-extractor.js.map +1 -0
- package/dist/extraction/parse-worker.d.ts +8 -0
- package/dist/extraction/parse-worker.d.ts.map +1 -0
- package/dist/extraction/parse-worker.js +94 -0
- package/dist/extraction/parse-worker.js.map +1 -0
- package/dist/extraction/specs/markdown-spec-extractor.d.ts +59 -0
- package/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -0
- package/dist/extraction/specs/markdown-spec-extractor.js +327 -0
- package/dist/extraction/specs/markdown-spec-extractor.js.map +1 -0
- package/dist/extraction/specs/types.d.ts +39 -0
- package/dist/extraction/specs/types.d.ts.map +1 -0
- package/dist/extraction/specs/types.js +8 -0
- package/dist/extraction/specs/types.js.map +1 -0
- package/dist/extraction/svelte-extractor.d.ts +56 -0
- package/dist/extraction/svelte-extractor.d.ts.map +1 -0
- package/dist/extraction/svelte-extractor.js +272 -0
- package/dist/extraction/svelte-extractor.js.map +1 -0
- package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
- package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
- package/dist/extraction/tree-sitter-helpers.js +103 -0
- package/dist/extraction/tree-sitter-helpers.js.map +1 -0
- package/dist/extraction/tree-sitter-types.d.ts +193 -0
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
- package/dist/extraction/tree-sitter-types.js +10 -0
- package/dist/extraction/tree-sitter-types.js.map +1 -0
- package/dist/extraction/tree-sitter.d.ts +317 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -0
- package/dist/extraction/tree-sitter.js +3092 -0
- package/dist/extraction/tree-sitter.js.map +1 -0
- package/dist/extraction/vue-extractor.d.ts +51 -0
- package/dist/extraction/vue-extractor.d.ts.map +1 -0
- package/dist/extraction/vue-extractor.js +251 -0
- package/dist/extraction/vue-extractor.js.map +1 -0
- package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/dist/extraction/wasm-runtime-flags.js +106 -0
- package/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +13 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/queries.d.ts +106 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +366 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/traversal.d.ts +127 -0
- package/dist/graph/traversal.d.ts.map +1 -0
- package/dist/graph/traversal.js +531 -0
- package/dist/graph/traversal.js.map +1 -0
- package/dist/index.d.ts +551 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1165 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/config-writer.d.ts +28 -0
- package/dist/installer/config-writer.d.ts.map +1 -0
- package/dist/installer/config-writer.js +91 -0
- package/dist/installer/config-writer.js.map +1 -0
- package/dist/installer/index.d.ts +87 -0
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +409 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/instructions-template.d.ts +18 -0
- package/dist/installer/instructions-template.d.ts.map +1 -0
- package/dist/installer/instructions-template.js +21 -0
- package/dist/installer/instructions-template.js.map +1 -0
- package/dist/installer/targets/claude.d.ts +88 -0
- package/dist/installer/targets/claude.d.ts.map +1 -0
- package/dist/installer/targets/claude.js +582 -0
- package/dist/installer/targets/claude.js.map +1 -0
- package/dist/installer/targets/registry.d.ts +19 -0
- package/dist/installer/targets/registry.d.ts.map +1 -0
- package/dist/installer/targets/registry.js +31 -0
- package/dist/installer/targets/registry.js.map +1 -0
- package/dist/installer/targets/shared.d.ts +62 -0
- package/dist/installer/targets/shared.d.ts.map +1 -0
- package/dist/installer/targets/shared.js +207 -0
- package/dist/installer/targets/shared.js.map +1 -0
- package/dist/installer/targets/types.d.ts +76 -0
- package/dist/installer/targets/types.d.ts.map +1 -0
- package/dist/installer/targets/types.js +12 -0
- package/dist/installer/targets/types.js.map +1 -0
- package/dist/isolation/worktree.d.ts +65 -0
- package/dist/isolation/worktree.d.ts.map +1 -0
- package/dist/isolation/worktree.js +231 -0
- package/dist/isolation/worktree.js.map +1 -0
- package/dist/mcp/daemon-paths.d.ts +46 -0
- package/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/dist/mcp/daemon-paths.js +125 -0
- package/dist/mcp/daemon-paths.js.map +1 -0
- package/dist/mcp/daemon.d.ts +161 -0
- package/dist/mcp/daemon.d.ts.map +1 -0
- package/dist/mcp/daemon.js +403 -0
- package/dist/mcp/daemon.js.map +1 -0
- package/dist/mcp/engine.d.ts +105 -0
- package/dist/mcp/engine.d.ts.map +1 -0
- package/dist/mcp/engine.js +270 -0
- package/dist/mcp/engine.js.map +1 -0
- package/dist/mcp/index.d.ts +112 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +477 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/proxy.d.ts +81 -0
- package/dist/mcp/proxy.d.ts.map +1 -0
- package/dist/mcp/proxy.js +510 -0
- package/dist/mcp/proxy.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +18 -0
- package/dist/mcp/server-instructions.d.ts.map +1 -0
- package/dist/mcp/server-instructions.js +77 -0
- package/dist/mcp/server-instructions.js.map +1 -0
- package/dist/mcp/session.d.ts +77 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +294 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/spec-tools.d.ts +39 -0
- package/dist/mcp/spec-tools.d.ts.map +1 -0
- package/dist/mcp/spec-tools.js +326 -0
- package/dist/mcp/spec-tools.js.map +1 -0
- package/dist/mcp/tools.d.ts +404 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +3066 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/transport.d.ts +188 -0
- package/dist/mcp/transport.d.ts.map +1 -0
- package/dist/mcp/transport.js +343 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/mcp/version.d.ts +19 -0
- package/dist/mcp/version.d.ts.map +1 -0
- package/dist/mcp/version.js +71 -0
- package/dist/mcp/version.js.map +1 -0
- package/dist/resolution/callback-synthesizer.d.ts +10 -0
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/dist/resolution/callback-synthesizer.js +1300 -0
- package/dist/resolution/callback-synthesizer.js.map +1 -0
- package/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
- package/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
- package/dist/resolution/frameworks/cargo-workspace.js +225 -0
- package/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
- package/dist/resolution/frameworks/csharp.d.ts +8 -0
- package/dist/resolution/frameworks/csharp.d.ts.map +1 -0
- package/dist/resolution/frameworks/csharp.js +241 -0
- package/dist/resolution/frameworks/csharp.js.map +1 -0
- package/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/dist/resolution/frameworks/drupal.js +367 -0
- package/dist/resolution/frameworks/drupal.js.map +1 -0
- package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/dist/resolution/frameworks/expo-modules.js +143 -0
- package/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/dist/resolution/frameworks/express.d.ts +8 -0
- package/dist/resolution/frameworks/express.d.ts.map +1 -0
- package/dist/resolution/frameworks/express.js +308 -0
- package/dist/resolution/frameworks/express.js.map +1 -0
- package/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/dist/resolution/frameworks/fabric.js +354 -0
- package/dist/resolution/frameworks/fabric.js.map +1 -0
- package/dist/resolution/frameworks/go.d.ts +8 -0
- package/dist/resolution/frameworks/go.d.ts.map +1 -0
- package/dist/resolution/frameworks/go.js +161 -0
- package/dist/resolution/frameworks/go.js.map +1 -0
- package/dist/resolution/frameworks/index.d.ts +48 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -0
- package/dist/resolution/frameworks/index.js +161 -0
- package/dist/resolution/frameworks/index.js.map +1 -0
- package/dist/resolution/frameworks/java.d.ts +8 -0
- package/dist/resolution/frameworks/java.d.ts.map +1 -0
- package/dist/resolution/frameworks/java.js +504 -0
- package/dist/resolution/frameworks/java.js.map +1 -0
- package/dist/resolution/frameworks/laravel.d.ts +13 -0
- package/dist/resolution/frameworks/laravel.d.ts.map +1 -0
- package/dist/resolution/frameworks/laravel.js +257 -0
- package/dist/resolution/frameworks/laravel.js.map +1 -0
- package/dist/resolution/frameworks/nestjs.d.ts +26 -0
- package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
- package/dist/resolution/frameworks/nestjs.js +698 -0
- package/dist/resolution/frameworks/nestjs.js.map +1 -0
- package/dist/resolution/frameworks/play.d.ts +19 -0
- package/dist/resolution/frameworks/play.d.ts.map +1 -0
- package/dist/resolution/frameworks/play.js +111 -0
- package/dist/resolution/frameworks/play.js.map +1 -0
- package/dist/resolution/frameworks/python.d.ts +10 -0
- package/dist/resolution/frameworks/python.d.ts.map +1 -0
- package/dist/resolution/frameworks/python.js +396 -0
- package/dist/resolution/frameworks/python.js.map +1 -0
- package/dist/resolution/frameworks/react-native.d.ts +3 -0
- package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/dist/resolution/frameworks/react-native.js +360 -0
- package/dist/resolution/frameworks/react-native.js.map +1 -0
- package/dist/resolution/frameworks/react.d.ts +8 -0
- package/dist/resolution/frameworks/react.d.ts.map +1 -0
- package/dist/resolution/frameworks/react.js +365 -0
- package/dist/resolution/frameworks/react.js.map +1 -0
- package/dist/resolution/frameworks/ruby.d.ts +8 -0
- package/dist/resolution/frameworks/ruby.d.ts.map +1 -0
- package/dist/resolution/frameworks/ruby.js +302 -0
- package/dist/resolution/frameworks/ruby.js.map +1 -0
- package/dist/resolution/frameworks/rust.d.ts +8 -0
- package/dist/resolution/frameworks/rust.d.ts.map +1 -0
- package/dist/resolution/frameworks/rust.js +304 -0
- package/dist/resolution/frameworks/rust.js.map +1 -0
- package/dist/resolution/frameworks/svelte.d.ts +9 -0
- package/dist/resolution/frameworks/svelte.d.ts.map +1 -0
- package/dist/resolution/frameworks/svelte.js +249 -0
- package/dist/resolution/frameworks/svelte.js.map +1 -0
- package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift-objc.js +252 -0
- package/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/dist/resolution/frameworks/swift.d.ts +10 -0
- package/dist/resolution/frameworks/swift.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift.js +400 -0
- package/dist/resolution/frameworks/swift.js.map +1 -0
- package/dist/resolution/frameworks/vue.d.ts +9 -0
- package/dist/resolution/frameworks/vue.d.ts.map +1 -0
- package/dist/resolution/frameworks/vue.js +306 -0
- package/dist/resolution/frameworks/vue.js.map +1 -0
- package/dist/resolution/go-module.d.ts +26 -0
- package/dist/resolution/go-module.d.ts.map +1 -0
- package/dist/resolution/go-module.js +78 -0
- package/dist/resolution/go-module.js.map +1 -0
- package/dist/resolution/import-resolver.d.ts +68 -0
- package/dist/resolution/import-resolver.d.ts.map +1 -0
- package/dist/resolution/import-resolver.js +1275 -0
- package/dist/resolution/import-resolver.js.map +1 -0
- package/dist/resolution/index.d.ts +117 -0
- package/dist/resolution/index.d.ts.map +1 -0
- package/dist/resolution/index.js +895 -0
- package/dist/resolution/index.js.map +1 -0
- package/dist/resolution/lru-cache.d.ts +24 -0
- package/dist/resolution/lru-cache.d.ts.map +1 -0
- package/dist/resolution/lru-cache.js +62 -0
- package/dist/resolution/lru-cache.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts +32 -0
- package/dist/resolution/name-matcher.d.ts.map +1 -0
- package/dist/resolution/name-matcher.js +596 -0
- package/dist/resolution/name-matcher.js.map +1 -0
- package/dist/resolution/path-aliases.d.ts +68 -0
- package/dist/resolution/path-aliases.d.ts.map +1 -0
- package/dist/resolution/path-aliases.js +238 -0
- package/dist/resolution/path-aliases.js.map +1 -0
- package/dist/resolution/spec-link-resolver.d.ts +103 -0
- package/dist/resolution/spec-link-resolver.d.ts.map +1 -0
- package/dist/resolution/spec-link-resolver.js +259 -0
- package/dist/resolution/spec-link-resolver.js.map +1 -0
- package/dist/resolution/strip-comments.d.ts +27 -0
- package/dist/resolution/strip-comments.d.ts.map +1 -0
- package/dist/resolution/strip-comments.js +441 -0
- package/dist/resolution/strip-comments.js.map +1 -0
- package/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/dist/resolution/swift-objc-bridge.js +256 -0
- package/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/dist/resolution/types.d.ts +216 -0
- package/dist/resolution/types.d.ts.map +1 -0
- package/dist/resolution/types.js +8 -0
- package/dist/resolution/types.js.map +1 -0
- package/dist/resolution/workspace-packages.d.ts +48 -0
- package/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/dist/resolution/workspace-packages.js +208 -0
- package/dist/resolution/workspace-packages.js.map +1 -0
- package/dist/search/query-parser.d.ts +57 -0
- package/dist/search/query-parser.d.ts.map +1 -0
- package/dist/search/query-parser.js +177 -0
- package/dist/search/query-parser.js.map +1 -0
- package/dist/search/query-utils.d.ts +71 -0
- package/dist/search/query-utils.d.ts.map +1 -0
- package/dist/search/query-utils.js +380 -0
- package/dist/search/query-utils.js.map +1 -0
- package/dist/server/cli.js +152 -0
- package/dist/server/index.js +12 -0
- package/dist/server/ingest/index.js +18 -0
- package/dist/server/ingest/ingestor.js +406 -0
- package/dist/server/ingest/parser.js +104 -0
- package/dist/server/ingest/pricing.js +78 -0
- package/dist/server/ingest/types.js +9 -0
- package/dist/server/ingest/watcher.js +77 -0
- package/dist/server/package.json +3 -0
- package/dist/server/project-registry.js +101 -0
- package/dist/server/routes/claude.js +480 -0
- package/dist/server/routes/graph.js +149 -0
- package/dist/server/routes/memory.js +272 -0
- package/dist/server/routes/projects.js +197 -0
- package/dist/server/routes/spec.js +105 -0
- package/dist/server/routes/status.js +35 -0
- package/dist/server/routes/workflow.js +184 -0
- package/dist/server/server.js +202 -0
- package/dist/sync/git-hooks.d.ts +45 -0
- package/dist/sync/git-hooks.d.ts.map +1 -0
- package/dist/sync/git-hooks.js +225 -0
- package/dist/sync/git-hooks.js.map +1 -0
- package/dist/sync/index.d.ts +19 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +35 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/watch-policy.d.ts +48 -0
- package/dist/sync/watch-policy.d.ts.map +1 -0
- package/dist/sync/watch-policy.js +124 -0
- package/dist/sync/watch-policy.js.map +1 -0
- package/dist/sync/watcher.d.ts +283 -0
- package/dist/sync/watcher.d.ts.map +1 -0
- package/dist/sync/watcher.js +606 -0
- package/dist/sync/watcher.js.map +1 -0
- package/dist/sync/worktree.d.ts +54 -0
- package/dist/sync/worktree.d.ts.map +1 -0
- package/dist/sync/worktree.js +137 -0
- package/dist/sync/worktree.js.map +1 -0
- package/dist/types.d.ts +623 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +108 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/glyphs.d.ts +42 -0
- package/dist/ui/glyphs.d.ts.map +1 -0
- package/dist/ui/glyphs.js +78 -0
- package/dist/ui/glyphs.js.map +1 -0
- package/dist/ui/shimmer-progress.d.ts +11 -0
- package/dist/ui/shimmer-progress.d.ts.map +1 -0
- package/dist/ui/shimmer-progress.js +90 -0
- package/dist/ui/shimmer-progress.js.map +1 -0
- package/dist/ui/shimmer-worker.d.ts +2 -0
- package/dist/ui/shimmer-worker.d.ts.map +1 -0
- package/dist/ui/shimmer-worker.js +118 -0
- package/dist/ui/shimmer-worker.js.map +1 -0
- package/dist/ui/types.d.ts +17 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +3 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/utils.d.ts +205 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +549 -0
- package/dist/utils.js.map +1 -0
- package/dist/web/chunk-2YZXEHZ2.js +1 -0
- package/dist/web/chunk-3GIC555L.js +18 -0
- package/dist/web/chunk-3IIIGRMT.js +1 -0
- package/dist/web/chunk-47QYKLE5.js +1 -0
- package/dist/web/chunk-4LHBWWP7.js +1 -0
- package/dist/web/chunk-4OAZLD5W.js +1 -0
- package/dist/web/chunk-5OQKAJAE.js +1 -0
- package/dist/web/chunk-7B525GKQ.js +1 -0
- package/dist/web/chunk-BPDXCOOZ.js +1 -0
- package/dist/web/chunk-DT37HTZB.js +1 -0
- package/dist/web/chunk-EIMUHJND.js +1 -0
- package/dist/web/chunk-FTESTUEO.js +1 -0
- package/dist/web/chunk-GLJZV6MU.js +1 -0
- package/dist/web/chunk-I7LS67U5.js +1 -0
- package/dist/web/chunk-L4TVIPSR.js +1 -0
- package/dist/web/chunk-MASCULC2.js +1 -0
- package/dist/web/chunk-MW7ICSRM.js +1 -0
- package/dist/web/chunk-OI5VP2A3.js +1 -0
- package/dist/web/chunk-RA6EBF6I.js +1 -0
- package/dist/web/chunk-RP3WU5Y6.js +1 -0
- package/dist/web/chunk-RQDRMTXN.js +1 -0
- package/dist/web/chunk-TQMT6UDU.js +1 -0
- package/dist/web/chunk-U7IYOV7T.js +1 -0
- package/dist/web/chunk-UE227MWF.js +1 -0
- package/dist/web/chunk-WV573J4K.js +1 -0
- package/dist/web/chunk-WVCKOJZL.js +4 -0
- package/dist/web/chunk-XZKLVPHE.js +1 -0
- package/dist/web/chunk-ZABKKHJ3.js +1 -0
- package/dist/web/favicon-16.png +0 -0
- package/dist/web/favicon-180.png +0 -0
- package/dist/web/favicon-32.png +0 -0
- package/dist/web/favicon-512.png +0 -0
- package/dist/web/favicon-small.svg +15 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/favicon.svg +20 -0
- package/dist/web/index.html +145 -0
- package/dist/web/main-RI5CO5Z4.js +1 -0
- package/dist/web/styles-CYN7IKT4.css +1 -0
- package/dist/workflows/condition-evaluator.d.ts +75 -0
- package/dist/workflows/condition-evaluator.d.ts.map +1 -0
- package/dist/workflows/condition-evaluator.js +282 -0
- package/dist/workflows/condition-evaluator.js.map +1 -0
- package/dist/workflows/defaults/index.d.ts +26 -0
- package/dist/workflows/defaults/index.d.ts.map +1 -0
- package/dist/workflows/defaults/index.js +94 -0
- package/dist/workflows/defaults/index.js.map +1 -0
- package/dist/workflows/defaults/spec-fix.yaml +110 -0
- package/dist/workflows/defaults/spec-implement.yaml +150 -0
- package/dist/workflows/defaults/spec-relink.yaml +81 -0
- package/dist/workflows/defaults/spec-verify.yaml +51 -0
- package/dist/workflows/discovery.d.ts +46 -0
- package/dist/workflows/discovery.d.ts.map +1 -0
- package/dist/workflows/discovery.js +193 -0
- package/dist/workflows/discovery.js.map +1 -0
- package/dist/workflows/executor.d.ts +83 -0
- package/dist/workflows/executor.d.ts.map +1 -0
- package/dist/workflows/executor.js +623 -0
- package/dist/workflows/executor.js.map +1 -0
- package/dist/workflows/runners/approval.d.ts +18 -0
- package/dist/workflows/runners/approval.d.ts.map +1 -0
- package/dist/workflows/runners/approval.js +34 -0
- package/dist/workflows/runners/approval.js.map +1 -0
- package/dist/workflows/runners/bash.d.ts +13 -0
- package/dist/workflows/runners/bash.d.ts.map +1 -0
- package/dist/workflows/runners/bash.js +143 -0
- package/dist/workflows/runners/bash.js.map +1 -0
- package/dist/workflows/runners/cancel.d.ts +10 -0
- package/dist/workflows/runners/cancel.d.ts.map +1 -0
- package/dist/workflows/runners/cancel.js +19 -0
- package/dist/workflows/runners/cancel.js.map +1 -0
- package/dist/workflows/runners/prompt.d.ts +28 -0
- package/dist/workflows/runners/prompt.d.ts.map +1 -0
- package/dist/workflows/runners/prompt.js +212 -0
- package/dist/workflows/runners/prompt.js.map +1 -0
- package/dist/workflows/runners/script.d.ts +17 -0
- package/dist/workflows/runners/script.d.ts.map +1 -0
- package/dist/workflows/runners/script.js +155 -0
- package/dist/workflows/runners/script.js.map +1 -0
- package/dist/workflows/runners/types.d.ts +51 -0
- package/dist/workflows/runners/types.d.ts.map +1 -0
- package/dist/workflows/runners/types.js +13 -0
- package/dist/workflows/runners/types.js.map +1 -0
- package/dist/workflows/schemas/workflow.d.ts +166 -0
- package/dist/workflows/schemas/workflow.d.ts.map +1 -0
- package/dist/workflows/schemas/workflow.js +437 -0
- package/dist/workflows/schemas/workflow.js.map +1 -0
- package/hooks/hooks.json +27 -0
- package/package.json +67 -0
- package/scripts/add-lang/bench.sh +60 -0
- package/scripts/add-lang/check-grammar.mjs +75 -0
- package/scripts/add-lang/dump-ast.mjs +103 -0
- package/scripts/add-lang/verify-extraction.mjs +70 -0
- package/scripts/agent-eval/arms-F.sh +21 -0
- package/scripts/agent-eval/arms-matrix.sh +37 -0
- package/scripts/agent-eval/audit.sh +68 -0
- package/scripts/agent-eval/bench-readme.sh +28 -0
- package/scripts/agent-eval/bench-why-repo.sh +22 -0
- package/scripts/agent-eval/block-read-hook.sh +19 -0
- package/scripts/agent-eval/hook-settings.json +15 -0
- package/scripts/agent-eval/itrun.sh +120 -0
- package/scripts/agent-eval/parse-arms.mjs +116 -0
- package/scripts/agent-eval/parse-bench-readme.mjs +84 -0
- package/scripts/agent-eval/parse-run.mjs +45 -0
- package/scripts/agent-eval/parse-session.mjs +93 -0
- package/scripts/agent-eval/probe-context.mjs +21 -0
- package/scripts/agent-eval/probe-explore.mjs +40 -0
- package/scripts/agent-eval/probe-node.mjs +20 -0
- package/scripts/agent-eval/probe-sweep.mjs +119 -0
- package/scripts/agent-eval/probe-trace.mjs +20 -0
- package/scripts/agent-eval/run-agent.sh +34 -0
- package/scripts/agent-eval/run-all.sh +67 -0
- package/scripts/agent-eval/run-arms.sh +56 -0
- package/scripts/agent-eval/seq-matrix.mjs +137 -0
- package/scripts/build-bundle.sh +118 -0
- package/scripts/build-server-bundle.mjs +80 -0
- package/scripts/build-web-bundle.mjs +66 -0
- package/scripts/extract-release-notes.mjs +130 -0
- package/scripts/local-install.sh +41 -0
- package/scripts/npm-sdk.js +75 -0
- package/scripts/npm-shim.js +246 -0
- package/scripts/offline-install.ps1 +148 -0
- package/scripts/offline-install.sh +136 -0
- package/scripts/pack-npm.sh +119 -0
- package/scripts/prepare-release.mjs +270 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SpecShip
|
|
4
|
+
*
|
|
5
|
+
* A local-first code intelligence system that builds a semantic
|
|
6
|
+
* knowledge graph from any codebase.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
42
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.SpecShip = exports.MCPServer = exports.LockUnavailableError = exports.FileWatcher = exports.MemoryMonitor = exports.throttle = exports.debounce = exports.processInBatches = exports.FileLock = exports.Mutex = exports.defaultLogger = exports.silentLogger = exports.getLogger = exports.setLogger = exports.ConfigError = exports.VectorError = exports.SearchError = exports.DatabaseError = exports.ParseError = exports.FileError = exports.SpecShipError = exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.detectLanguage = exports.SPECSHIP_DIR = exports.findNearestSpecShipRoot = exports.isInitialized = exports.getSpecShipDir = exports.QueryBuilder = exports.DatabaseConnection = exports.getDatabasePath = void 0;
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const db_1 = require("./db");
|
|
48
|
+
const queries_1 = require("./db/queries");
|
|
49
|
+
const directory_1 = require("./directory");
|
|
50
|
+
const extraction_1 = require("./extraction");
|
|
51
|
+
const markdown_spec_extractor_1 = require("./extraction/specs/markdown-spec-extractor");
|
|
52
|
+
const resolution_1 = require("./resolution");
|
|
53
|
+
const spec_link_resolver_1 = require("./resolution/spec-link-resolver");
|
|
54
|
+
const spec_queries_1 = require("./db/spec-queries");
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const crypto_1 = require("crypto");
|
|
57
|
+
const graph_1 = require("./graph");
|
|
58
|
+
const context_1 = require("./context");
|
|
59
|
+
const utils_1 = require("./utils");
|
|
60
|
+
const sync_1 = require("./sync");
|
|
61
|
+
// Re-export types for consumers
|
|
62
|
+
__exportStar(require("./types"), exports);
|
|
63
|
+
// Storage building blocks for embedded/SDK consumers that drive the graph
|
|
64
|
+
// directly (open a DB, run prepared queries) rather than through the SpecShip
|
|
65
|
+
// facade. Exposed from the package entry so they no longer require deep imports
|
|
66
|
+
// into dist/ (issue #354).
|
|
67
|
+
var db_2 = require("./db");
|
|
68
|
+
Object.defineProperty(exports, "getDatabasePath", { enumerable: true, get: function () { return db_2.getDatabasePath; } });
|
|
69
|
+
Object.defineProperty(exports, "DatabaseConnection", { enumerable: true, get: function () { return db_2.DatabaseConnection; } });
|
|
70
|
+
var queries_2 = require("./db/queries");
|
|
71
|
+
Object.defineProperty(exports, "QueryBuilder", { enumerable: true, get: function () { return queries_2.QueryBuilder; } });
|
|
72
|
+
var directory_2 = require("./directory");
|
|
73
|
+
Object.defineProperty(exports, "getSpecShipDir", { enumerable: true, get: function () { return directory_2.getSpecShipDir; } });
|
|
74
|
+
Object.defineProperty(exports, "isInitialized", { enumerable: true, get: function () { return directory_2.isInitialized; } });
|
|
75
|
+
Object.defineProperty(exports, "findNearestSpecShipRoot", { enumerable: true, get: function () { return directory_2.findNearestSpecShipRoot; } });
|
|
76
|
+
Object.defineProperty(exports, "SPECSHIP_DIR", { enumerable: true, get: function () { return directory_2.SPECSHIP_DIR; } });
|
|
77
|
+
var extraction_2 = require("./extraction");
|
|
78
|
+
Object.defineProperty(exports, "detectLanguage", { enumerable: true, get: function () { return extraction_2.detectLanguage; } });
|
|
79
|
+
Object.defineProperty(exports, "isLanguageSupported", { enumerable: true, get: function () { return extraction_2.isLanguageSupported; } });
|
|
80
|
+
Object.defineProperty(exports, "isGrammarLoaded", { enumerable: true, get: function () { return extraction_2.isGrammarLoaded; } });
|
|
81
|
+
Object.defineProperty(exports, "getSupportedLanguages", { enumerable: true, get: function () { return extraction_2.getSupportedLanguages; } });
|
|
82
|
+
Object.defineProperty(exports, "initGrammars", { enumerable: true, get: function () { return extraction_2.initGrammars; } });
|
|
83
|
+
Object.defineProperty(exports, "loadGrammarsForLanguages", { enumerable: true, get: function () { return extraction_2.loadGrammarsForLanguages; } });
|
|
84
|
+
Object.defineProperty(exports, "loadAllGrammars", { enumerable: true, get: function () { return extraction_2.loadAllGrammars; } });
|
|
85
|
+
var errors_1 = require("./errors");
|
|
86
|
+
Object.defineProperty(exports, "SpecShipError", { enumerable: true, get: function () { return errors_1.SpecShipError; } });
|
|
87
|
+
Object.defineProperty(exports, "FileError", { enumerable: true, get: function () { return errors_1.FileError; } });
|
|
88
|
+
Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return errors_1.ParseError; } });
|
|
89
|
+
Object.defineProperty(exports, "DatabaseError", { enumerable: true, get: function () { return errors_1.DatabaseError; } });
|
|
90
|
+
Object.defineProperty(exports, "SearchError", { enumerable: true, get: function () { return errors_1.SearchError; } });
|
|
91
|
+
Object.defineProperty(exports, "VectorError", { enumerable: true, get: function () { return errors_1.VectorError; } });
|
|
92
|
+
Object.defineProperty(exports, "ConfigError", { enumerable: true, get: function () { return errors_1.ConfigError; } });
|
|
93
|
+
Object.defineProperty(exports, "setLogger", { enumerable: true, get: function () { return errors_1.setLogger; } });
|
|
94
|
+
Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return errors_1.getLogger; } });
|
|
95
|
+
Object.defineProperty(exports, "silentLogger", { enumerable: true, get: function () { return errors_1.silentLogger; } });
|
|
96
|
+
Object.defineProperty(exports, "defaultLogger", { enumerable: true, get: function () { return errors_1.defaultLogger; } });
|
|
97
|
+
var utils_2 = require("./utils");
|
|
98
|
+
Object.defineProperty(exports, "Mutex", { enumerable: true, get: function () { return utils_2.Mutex; } });
|
|
99
|
+
Object.defineProperty(exports, "FileLock", { enumerable: true, get: function () { return utils_2.FileLock; } });
|
|
100
|
+
Object.defineProperty(exports, "processInBatches", { enumerable: true, get: function () { return utils_2.processInBatches; } });
|
|
101
|
+
Object.defineProperty(exports, "debounce", { enumerable: true, get: function () { return utils_2.debounce; } });
|
|
102
|
+
Object.defineProperty(exports, "throttle", { enumerable: true, get: function () { return utils_2.throttle; } });
|
|
103
|
+
Object.defineProperty(exports, "MemoryMonitor", { enumerable: true, get: function () { return utils_2.MemoryMonitor; } });
|
|
104
|
+
var sync_2 = require("./sync");
|
|
105
|
+
Object.defineProperty(exports, "FileWatcher", { enumerable: true, get: function () { return sync_2.FileWatcher; } });
|
|
106
|
+
Object.defineProperty(exports, "LockUnavailableError", { enumerable: true, get: function () { return sync_2.LockUnavailableError; } });
|
|
107
|
+
var mcp_1 = require("./mcp");
|
|
108
|
+
Object.defineProperty(exports, "MCPServer", { enumerable: true, get: function () { return mcp_1.MCPServer; } });
|
|
109
|
+
/**
|
|
110
|
+
* Main SpecShip class
|
|
111
|
+
*
|
|
112
|
+
* Provides the primary interface for interacting with the code knowledge graph.
|
|
113
|
+
*/
|
|
114
|
+
class SpecShip {
|
|
115
|
+
db;
|
|
116
|
+
queries;
|
|
117
|
+
specQueries;
|
|
118
|
+
projectRoot;
|
|
119
|
+
orchestrator;
|
|
120
|
+
resolver;
|
|
121
|
+
specLinkResolver;
|
|
122
|
+
graphManager;
|
|
123
|
+
traverser;
|
|
124
|
+
contextBuilder;
|
|
125
|
+
// Mutex for preventing concurrent indexing operations (in-process)
|
|
126
|
+
indexMutex = new utils_1.Mutex();
|
|
127
|
+
// File lock for preventing concurrent writes across processes (CLI, MCP, git hooks)
|
|
128
|
+
fileLock;
|
|
129
|
+
// File watcher for auto-sync on file changes
|
|
130
|
+
watcher = null;
|
|
131
|
+
constructor(db, queries, projectRoot) {
|
|
132
|
+
this.db = db;
|
|
133
|
+
this.queries = queries;
|
|
134
|
+
this.specQueries = new spec_queries_1.SpecQueries(db.getDb());
|
|
135
|
+
this.projectRoot = projectRoot;
|
|
136
|
+
this.fileLock = new utils_1.FileLock(path.join(projectRoot, '.specship', 'specship.lock'));
|
|
137
|
+
this.orchestrator = new extraction_1.ExtractionOrchestrator(projectRoot, queries);
|
|
138
|
+
this.resolver = (0, resolution_1.createResolver)(projectRoot, queries);
|
|
139
|
+
this.specLinkResolver = new spec_link_resolver_1.SpecLinkResolver(queries, this.specQueries);
|
|
140
|
+
this.graphManager = new graph_1.GraphQueryManager(queries);
|
|
141
|
+
this.traverser = new graph_1.GraphTraverser(queries);
|
|
142
|
+
this.contextBuilder = (0, context_1.createContextBuilder)(projectRoot, queries, this.traverser);
|
|
143
|
+
}
|
|
144
|
+
/** Access to the spec queries (for MCP tools, CLI commands, embedded SDK users). */
|
|
145
|
+
getSpecQueries() {
|
|
146
|
+
return this.specQueries;
|
|
147
|
+
}
|
|
148
|
+
/** Access to the spec link resolver. */
|
|
149
|
+
getSpecLinkResolver() {
|
|
150
|
+
return this.specLinkResolver;
|
|
151
|
+
}
|
|
152
|
+
// ===========================================================================
|
|
153
|
+
// Lifecycle Methods
|
|
154
|
+
// ===========================================================================
|
|
155
|
+
/**
|
|
156
|
+
* Initialize a new SpecShip project
|
|
157
|
+
*
|
|
158
|
+
* Creates the .SpecShip directory, database, and configuration.
|
|
159
|
+
*
|
|
160
|
+
* @param projectRoot - Path to the project root directory
|
|
161
|
+
* @param options - Initialization options
|
|
162
|
+
* @returns A new SpecShip instance
|
|
163
|
+
*/
|
|
164
|
+
static async init(projectRoot, options = {}) {
|
|
165
|
+
await (0, extraction_1.initGrammars)();
|
|
166
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
167
|
+
// Check if already initialized
|
|
168
|
+
if ((0, directory_1.isInitialized)(resolvedRoot)) {
|
|
169
|
+
throw new Error(`SpecShip already initialized in ${resolvedRoot}`);
|
|
170
|
+
}
|
|
171
|
+
// Create directory structure
|
|
172
|
+
(0, directory_1.createDirectory)(resolvedRoot);
|
|
173
|
+
// Initialize database
|
|
174
|
+
const dbPath = (0, db_1.getDatabasePath)(resolvedRoot);
|
|
175
|
+
const db = db_1.DatabaseConnection.initialize(dbPath);
|
|
176
|
+
const queries = new queries_1.QueryBuilder(db.getDb());
|
|
177
|
+
const instance = new SpecShip(db, queries, resolvedRoot);
|
|
178
|
+
// Run initial indexing if requested
|
|
179
|
+
if (options.index) {
|
|
180
|
+
await instance.indexAll({ onProgress: options.onProgress });
|
|
181
|
+
}
|
|
182
|
+
return instance;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Initialize synchronously (without indexing)
|
|
186
|
+
*/
|
|
187
|
+
static initSync(projectRoot) {
|
|
188
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
189
|
+
// Check if already initialized
|
|
190
|
+
if ((0, directory_1.isInitialized)(resolvedRoot)) {
|
|
191
|
+
throw new Error(`SpecShip already initialized in ${resolvedRoot}`);
|
|
192
|
+
}
|
|
193
|
+
// Create directory structure
|
|
194
|
+
(0, directory_1.createDirectory)(resolvedRoot);
|
|
195
|
+
// Initialize database
|
|
196
|
+
const dbPath = (0, db_1.getDatabasePath)(resolvedRoot);
|
|
197
|
+
const db = db_1.DatabaseConnection.initialize(dbPath);
|
|
198
|
+
const queries = new queries_1.QueryBuilder(db.getDb());
|
|
199
|
+
return new SpecShip(db, queries, resolvedRoot);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Open an existing SpecShip project
|
|
203
|
+
*
|
|
204
|
+
* @param projectRoot - Path to the project root directory
|
|
205
|
+
* @param options - Open options
|
|
206
|
+
* @returns A SpecShip instance
|
|
207
|
+
*/
|
|
208
|
+
static async open(projectRoot, options = {}) {
|
|
209
|
+
await (0, extraction_1.initGrammars)();
|
|
210
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
211
|
+
// Check if initialized
|
|
212
|
+
if (!(0, directory_1.isInitialized)(resolvedRoot)) {
|
|
213
|
+
throw new Error(`SpecShip not initialized in ${resolvedRoot}. Run init() first.`);
|
|
214
|
+
}
|
|
215
|
+
// Validate directory structure
|
|
216
|
+
const validation = (0, directory_1.validateDirectory)(resolvedRoot);
|
|
217
|
+
if (!validation.valid) {
|
|
218
|
+
throw new Error(`Invalid SpecShip directory: ${validation.errors.join(', ')}`);
|
|
219
|
+
}
|
|
220
|
+
// Open database
|
|
221
|
+
const dbPath = (0, db_1.getDatabasePath)(resolvedRoot);
|
|
222
|
+
const db = db_1.DatabaseConnection.open(dbPath);
|
|
223
|
+
const queries = new queries_1.QueryBuilder(db.getDb());
|
|
224
|
+
const instance = new SpecShip(db, queries, resolvedRoot);
|
|
225
|
+
// Sync if requested
|
|
226
|
+
if (options.sync) {
|
|
227
|
+
await instance.sync();
|
|
228
|
+
}
|
|
229
|
+
return instance;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Open synchronously (without sync)
|
|
233
|
+
*/
|
|
234
|
+
static openSync(projectRoot) {
|
|
235
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
236
|
+
// Check if initialized
|
|
237
|
+
if (!(0, directory_1.isInitialized)(resolvedRoot)) {
|
|
238
|
+
throw new Error(`SpecShip not initialized in ${resolvedRoot}. Run init() first.`);
|
|
239
|
+
}
|
|
240
|
+
// Validate directory structure
|
|
241
|
+
const validation = (0, directory_1.validateDirectory)(resolvedRoot);
|
|
242
|
+
if (!validation.valid) {
|
|
243
|
+
throw new Error(`Invalid SpecShip directory: ${validation.errors.join(', ')}`);
|
|
244
|
+
}
|
|
245
|
+
// Open database
|
|
246
|
+
const dbPath = (0, db_1.getDatabasePath)(resolvedRoot);
|
|
247
|
+
const db = db_1.DatabaseConnection.open(dbPath);
|
|
248
|
+
const queries = new queries_1.QueryBuilder(db.getDb());
|
|
249
|
+
return new SpecShip(db, queries, resolvedRoot);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Check if a directory has been initialized as a SpecShip project
|
|
253
|
+
*/
|
|
254
|
+
static isInitialized(projectRoot) {
|
|
255
|
+
return (0, directory_1.isInitialized)(path.resolve(projectRoot));
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Close the SpecShip instance and release resources
|
|
259
|
+
*/
|
|
260
|
+
close() {
|
|
261
|
+
this.unwatch();
|
|
262
|
+
// Release file lock if held
|
|
263
|
+
this.fileLock.release();
|
|
264
|
+
this.db.close();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get the project root directory
|
|
268
|
+
*/
|
|
269
|
+
getProjectRoot() {
|
|
270
|
+
return this.projectRoot;
|
|
271
|
+
}
|
|
272
|
+
// ===========================================================================
|
|
273
|
+
// Indexing
|
|
274
|
+
// ===========================================================================
|
|
275
|
+
/**
|
|
276
|
+
* Index all files in the project
|
|
277
|
+
*
|
|
278
|
+
* Uses a mutex to prevent concurrent indexing operations.
|
|
279
|
+
*/
|
|
280
|
+
async indexAll(options = {}) {
|
|
281
|
+
return this.indexMutex.withLock(async () => {
|
|
282
|
+
try {
|
|
283
|
+
this.fileLock.acquire();
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return { success: false, filesIndexed: 0, filesSkipped: 0, filesErrored: 0, nodesCreated: 0, edgesCreated: 0, errors: [{ message: 'Could not acquire file lock - another process may be indexing', severity: 'error' }], durationMs: 0 };
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const before = this.queries.getNodeAndEdgeCount();
|
|
290
|
+
const result = await this.orchestrator.indexAll(options.onProgress, options.signal, options.verbose);
|
|
291
|
+
// Re-detect frameworks now that the index is populated. The resolver
|
|
292
|
+
// is constructed with createResolver() before any files exist, so
|
|
293
|
+
// framework resolvers whose detect() consults the indexed file list
|
|
294
|
+
// (e.g. UIKit/SwiftUI scanning for imports, swift-objc-bridge looking
|
|
295
|
+
// for both Swift and ObjC files) all return false on that initial pass
|
|
296
|
+
// and silently drop themselves. Re-initializing here gives them a
|
|
297
|
+
// chance to see the actual project before resolution runs.
|
|
298
|
+
if (result.success && result.filesIndexed > 0) {
|
|
299
|
+
this.resolver.initialize();
|
|
300
|
+
// Cross-file finalization (e.g. NestJS RouterModule prefixes). Runs
|
|
301
|
+
// before resolution so updated names show up in subsequent reads.
|
|
302
|
+
this.resolver.runPostExtract();
|
|
303
|
+
}
|
|
304
|
+
// Resolve references to create call/import/extends edges
|
|
305
|
+
if (result.success && result.filesIndexed > 0) {
|
|
306
|
+
// Get count without loading all refs into memory
|
|
307
|
+
const unresolvedCount = this.queries.getUnresolvedReferencesCount();
|
|
308
|
+
options.onProgress?.({
|
|
309
|
+
phase: 'resolving',
|
|
310
|
+
current: 0,
|
|
311
|
+
total: unresolvedCount,
|
|
312
|
+
});
|
|
313
|
+
await this.resolveReferencesBatched((current, total) => {
|
|
314
|
+
options.onProgress?.({
|
|
315
|
+
phase: 'resolving',
|
|
316
|
+
current,
|
|
317
|
+
total,
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
// Refresh planner stats + checkpoint the WAL after bulk writes.
|
|
322
|
+
// Cheap and non-blocking; never load-bearing for correctness.
|
|
323
|
+
if (result.success && result.filesIndexed > 0) {
|
|
324
|
+
this.db.runMaintenance();
|
|
325
|
+
}
|
|
326
|
+
// Spec extraction runs after code so spec-declared `implementations:`
|
|
327
|
+
// links can resolve against the freshly-indexed nodes. Code-comment
|
|
328
|
+
// links also get scanned here. Best-effort: a spec parse error never
|
|
329
|
+
// fails the overall index.
|
|
330
|
+
if (result.success) {
|
|
331
|
+
try {
|
|
332
|
+
// Release the lock before re-acquiring inside indexSpecs (mutex
|
|
333
|
+
// is re-entrant via withLock chaining; the file lock isn't).
|
|
334
|
+
this.fileLock.release();
|
|
335
|
+
await this.indexSpecsInternal();
|
|
336
|
+
// Re-acquire so the outer finally can release once.
|
|
337
|
+
this.fileLock.acquire();
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
// Spec failures are non-fatal in v1. Surface via errors array.
|
|
341
|
+
result.errors.push({
|
|
342
|
+
message: `Spec indexing failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
343
|
+
severity: 'warning',
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// The orchestrator only sees extraction-phase counts; resolution and
|
|
348
|
+
// synthesizer edges (often >50% of the graph on JVM repos) come later.
|
|
349
|
+
// Recompute against the DB so the CLI summary reports the true totals.
|
|
350
|
+
if (result.success && result.filesIndexed > 0) {
|
|
351
|
+
const after = this.queries.getNodeAndEdgeCount();
|
|
352
|
+
result.nodesCreated = after.nodes - before.nodes;
|
|
353
|
+
result.edgesCreated = after.edges - before.edges;
|
|
354
|
+
}
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
this.fileLock.release();
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Internal spec indexing — does NOT take the indexMutex (caller already holds it)
|
|
364
|
+
* and does NOT take the fileLock. Used by indexAll which manages locks itself.
|
|
365
|
+
*/
|
|
366
|
+
async indexSpecsInternal() {
|
|
367
|
+
const specRoots = this.defaultSpecRoots();
|
|
368
|
+
if (specRoots.length === 0)
|
|
369
|
+
return;
|
|
370
|
+
const specFiles = [];
|
|
371
|
+
for (const root of specRoots) {
|
|
372
|
+
this.collectSpecFiles(root, specFiles);
|
|
373
|
+
}
|
|
374
|
+
for (const absPath of specFiles) {
|
|
375
|
+
const rel = path.relative(this.projectRoot, absPath);
|
|
376
|
+
let source;
|
|
377
|
+
let stat;
|
|
378
|
+
try {
|
|
379
|
+
source = fs.readFileSync(absPath, 'utf-8');
|
|
380
|
+
stat = fs.statSync(absPath);
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
const hashHex = (0, crypto_1.createHash)('sha256').update(source).digest('hex').substring(0, 32);
|
|
386
|
+
const existing = this.specQueries.getSpecFileByPath(rel);
|
|
387
|
+
if (existing && existing.contentHash === hashHex)
|
|
388
|
+
continue;
|
|
389
|
+
this.specQueries.deleteSpecsByFile(rel);
|
|
390
|
+
const result = new markdown_spec_extractor_1.MarkdownSpecExtractor(rel, source).extract();
|
|
391
|
+
this.specQueries.insertSpecsBatch(result.specs);
|
|
392
|
+
this.specQueries.upsertSpecFile({
|
|
393
|
+
path: rel,
|
|
394
|
+
contentHash: hashHex,
|
|
395
|
+
format: 'markdown',
|
|
396
|
+
size: stat.size,
|
|
397
|
+
modifiedAt: stat.mtimeMs,
|
|
398
|
+
indexedAt: Date.now(),
|
|
399
|
+
specCount: result.specs.length,
|
|
400
|
+
errors: result.errors,
|
|
401
|
+
});
|
|
402
|
+
const specsById = new Map(result.specs.map((s) => [s.id, s]));
|
|
403
|
+
this.specLinkResolver.applyDeclarationCandidates(result.linkCandidates, specsById);
|
|
404
|
+
for (const spec of result.specs) {
|
|
405
|
+
this.specLinkResolver.markSpecDrifted(spec.id, spec.contentHash);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const allFiles = this.queries.getAllFiles().map((f) => f.path);
|
|
409
|
+
this.specLinkResolver.applyCodeCommentLinks(allFiles);
|
|
410
|
+
this.specLinkResolver.resolveAll();
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Index spec files (Markdown only in v1).
|
|
414
|
+
*
|
|
415
|
+
* Default spec roots: `<projectRoot>/specs/` if it exists. Callers can
|
|
416
|
+
* pass explicit roots/files for non-default layouts.
|
|
417
|
+
*
|
|
418
|
+
* Pipeline per file:
|
|
419
|
+
* 1. Hash file content; skip if `spec_files.content_hash` matches.
|
|
420
|
+
* 2. Delete prior specs for this file (cascade-deletes children, links).
|
|
421
|
+
* 3. Run MarkdownSpecExtractor → insert specs + virtual node projections.
|
|
422
|
+
* 4. Apply `spec-declaration` link candidates from `implementations:`.
|
|
423
|
+
* 5. Mark previously-linked specs as drifted (drift_axis='spec') when
|
|
424
|
+
* their content_hash has changed.
|
|
425
|
+
*
|
|
426
|
+
* Returns extraction stats including spec link resolver effects.
|
|
427
|
+
*/
|
|
428
|
+
async indexSpecs(roots) {
|
|
429
|
+
const start = Date.now();
|
|
430
|
+
return this.indexMutex.withLock(async () => {
|
|
431
|
+
try {
|
|
432
|
+
this.fileLock.acquire();
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return {
|
|
436
|
+
filesProcessed: 0,
|
|
437
|
+
filesSkipped: 0,
|
|
438
|
+
specsExtracted: 0,
|
|
439
|
+
linkCandidates: 0,
|
|
440
|
+
resolverStats: {
|
|
441
|
+
scanned: 0,
|
|
442
|
+
reresolved: 0,
|
|
443
|
+
orphaned: 0,
|
|
444
|
+
driftedCode: 0,
|
|
445
|
+
candidatesApplied: 0,
|
|
446
|
+
commentLinksApplied: 0,
|
|
447
|
+
},
|
|
448
|
+
errors: 0,
|
|
449
|
+
durationMs: Date.now() - start,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const specRoots = roots && roots.length > 0 ? roots : this.defaultSpecRoots();
|
|
454
|
+
const specFiles = [];
|
|
455
|
+
for (const root of specRoots) {
|
|
456
|
+
this.collectSpecFiles(root, specFiles);
|
|
457
|
+
}
|
|
458
|
+
let processed = 0;
|
|
459
|
+
let skipped = 0;
|
|
460
|
+
let totalSpecs = 0;
|
|
461
|
+
let totalCandidates = 0;
|
|
462
|
+
let totalErrors = 0;
|
|
463
|
+
const stats = {
|
|
464
|
+
scanned: 0,
|
|
465
|
+
reresolved: 0,
|
|
466
|
+
orphaned: 0,
|
|
467
|
+
driftedCode: 0,
|
|
468
|
+
candidatesApplied: 0,
|
|
469
|
+
commentLinksApplied: 0,
|
|
470
|
+
};
|
|
471
|
+
for (const absPath of specFiles) {
|
|
472
|
+
const rel = path.relative(this.projectRoot, absPath);
|
|
473
|
+
let source;
|
|
474
|
+
let stat;
|
|
475
|
+
try {
|
|
476
|
+
source = fs.readFileSync(absPath, 'utf-8');
|
|
477
|
+
stat = fs.statSync(absPath);
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const hashHex = (0, crypto_1.createHash)('sha256').update(source).digest('hex').substring(0, 32);
|
|
483
|
+
const existing = this.specQueries.getSpecFileByPath(rel);
|
|
484
|
+
if (existing && existing.contentHash === hashHex) {
|
|
485
|
+
skipped++;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
// Replace prior specs for this file. CASCADE removes children +
|
|
489
|
+
// spec_links FK'd on spec_id.
|
|
490
|
+
this.specQueries.deleteSpecsByFile(rel);
|
|
491
|
+
const result = new markdown_spec_extractor_1.MarkdownSpecExtractor(rel, source).extract();
|
|
492
|
+
this.specQueries.insertSpecsBatch(result.specs);
|
|
493
|
+
this.specQueries.upsertSpecFile({
|
|
494
|
+
path: rel,
|
|
495
|
+
contentHash: hashHex,
|
|
496
|
+
format: 'markdown',
|
|
497
|
+
size: stat.size,
|
|
498
|
+
modifiedAt: stat.mtimeMs,
|
|
499
|
+
indexedAt: Date.now(),
|
|
500
|
+
specCount: result.specs.length,
|
|
501
|
+
errors: result.errors,
|
|
502
|
+
});
|
|
503
|
+
// Apply spec-declared `implementations:` link candidates.
|
|
504
|
+
const specsById = new Map(result.specs.map((s) => [s.id, s]));
|
|
505
|
+
this.specLinkResolver.applyDeclarationCandidates(result.linkCandidates, specsById, stats);
|
|
506
|
+
// Spec-side drift: if any prior link's specHashAtLink differs from
|
|
507
|
+
// the new contentHash of its spec, flip the link to drifted(spec).
|
|
508
|
+
for (const spec of result.specs) {
|
|
509
|
+
this.specLinkResolver.markSpecDrifted(spec.id, spec.contentHash);
|
|
510
|
+
}
|
|
511
|
+
processed++;
|
|
512
|
+
totalSpecs += result.specs.length;
|
|
513
|
+
totalCandidates += result.linkCandidates.length;
|
|
514
|
+
totalErrors += result.errors.filter((e) => e.severity === 'error').length;
|
|
515
|
+
}
|
|
516
|
+
// After spec changes settle, also scan code docstrings for
|
|
517
|
+
// `@implements REQ-X` markers. Bounded to the files that already
|
|
518
|
+
// have nodes in the graph (full-graph scan is acceptable here —
|
|
519
|
+
// node count is bounded and the scan is O(N) regex over docstrings).
|
|
520
|
+
const allFiles = this.queries.getAllFiles().map((f) => f.path);
|
|
521
|
+
this.specLinkResolver.applyCodeCommentLinks(allFiles, stats);
|
|
522
|
+
// Re-resolve all links (post-extraction).
|
|
523
|
+
const resolveStats = this.specLinkResolver.resolveAll();
|
|
524
|
+
stats.scanned += resolveStats.scanned;
|
|
525
|
+
stats.reresolved += resolveStats.reresolved;
|
|
526
|
+
stats.orphaned += resolveStats.orphaned;
|
|
527
|
+
stats.driftedCode += resolveStats.driftedCode;
|
|
528
|
+
return {
|
|
529
|
+
filesProcessed: processed,
|
|
530
|
+
filesSkipped: skipped,
|
|
531
|
+
specsExtracted: totalSpecs,
|
|
532
|
+
linkCandidates: totalCandidates,
|
|
533
|
+
resolverStats: stats,
|
|
534
|
+
errors: totalErrors,
|
|
535
|
+
durationMs: Date.now() - start,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
finally {
|
|
539
|
+
this.fileLock.release();
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Default spec roots: `<projectRoot>/specs/` if it exists. Future
|
|
545
|
+
* iterations can read from `.specship/config.json`.
|
|
546
|
+
*/
|
|
547
|
+
defaultSpecRoots() {
|
|
548
|
+
const candidate = path.join(this.projectRoot, 'specs');
|
|
549
|
+
try {
|
|
550
|
+
const stat = fs.statSync(candidate);
|
|
551
|
+
if (stat.isDirectory())
|
|
552
|
+
return [candidate];
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
// No specs/ directory — return empty so indexSpecs is a no-op.
|
|
556
|
+
}
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Recursively collect `.md` files from a directory.
|
|
561
|
+
*/
|
|
562
|
+
collectSpecFiles(root, out) {
|
|
563
|
+
let entries;
|
|
564
|
+
try {
|
|
565
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
for (const entry of entries) {
|
|
571
|
+
const full = path.join(root, entry.name);
|
|
572
|
+
if (entry.isDirectory()) {
|
|
573
|
+
// Skip hidden/system directories
|
|
574
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules')
|
|
575
|
+
continue;
|
|
576
|
+
this.collectSpecFiles(full, out);
|
|
577
|
+
}
|
|
578
|
+
else if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) {
|
|
579
|
+
out.push(full);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Index specific files
|
|
585
|
+
*
|
|
586
|
+
* Uses a mutex to prevent concurrent indexing operations.
|
|
587
|
+
*/
|
|
588
|
+
async indexFiles(filePaths) {
|
|
589
|
+
return this.indexMutex.withLock(async () => {
|
|
590
|
+
try {
|
|
591
|
+
this.fileLock.acquire();
|
|
592
|
+
}
|
|
593
|
+
catch {
|
|
594
|
+
return { success: false, filesIndexed: 0, filesSkipped: 0, filesErrored: 0, nodesCreated: 0, edgesCreated: 0, errors: [{ message: 'Could not acquire file lock - another process may be indexing', severity: 'error' }], durationMs: 0 };
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
return this.orchestrator.indexFiles(filePaths);
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
this.fileLock.release();
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Sync with current file state (incremental update)
|
|
606
|
+
*
|
|
607
|
+
* Uses a mutex to prevent concurrent indexing operations.
|
|
608
|
+
*/
|
|
609
|
+
async sync(options = {}) {
|
|
610
|
+
return this.indexMutex.withLock(async () => {
|
|
611
|
+
try {
|
|
612
|
+
this.fileLock.acquire();
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
return { filesChecked: 0, filesAdded: 0, filesModified: 0, filesRemoved: 0, nodesUpdated: 0, durationMs: 0 };
|
|
616
|
+
}
|
|
617
|
+
try {
|
|
618
|
+
const result = await this.orchestrator.sync(options.onProgress);
|
|
619
|
+
// Cross-file finalization (e.g. NestJS RouterModule prefixes). Run on
|
|
620
|
+
// every sync that touched files so edits to `app.module.ts` propagate
|
|
621
|
+
// to controllers in unchanged files. The pass is idempotent and cheap
|
|
622
|
+
// (regex over *.module.ts only).
|
|
623
|
+
if (result.filesAdded > 0 || result.filesModified > 0) {
|
|
624
|
+
this.resolver.runPostExtract();
|
|
625
|
+
}
|
|
626
|
+
// Resolve references if files were updated
|
|
627
|
+
if (result.filesAdded > 0 || result.filesModified > 0) {
|
|
628
|
+
if (result.changedFilePaths) {
|
|
629
|
+
// Scope resolution to changed files (git fast path — bounded set)
|
|
630
|
+
const unresolvedRefs = this.queries.getUnresolvedReferencesByFiles(result.changedFilePaths);
|
|
631
|
+
options.onProgress?.({
|
|
632
|
+
phase: 'resolving',
|
|
633
|
+
current: 0,
|
|
634
|
+
total: unresolvedRefs.length,
|
|
635
|
+
});
|
|
636
|
+
this.resolver.resolveAndPersist(unresolvedRefs, (current, total) => {
|
|
637
|
+
options.onProgress?.({
|
|
638
|
+
phase: 'resolving',
|
|
639
|
+
current,
|
|
640
|
+
total,
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
// No git info — use batched resolution to avoid OOM
|
|
646
|
+
const unresolvedCount = this.queries.getUnresolvedReferencesCount();
|
|
647
|
+
options.onProgress?.({
|
|
648
|
+
phase: 'resolving',
|
|
649
|
+
current: 0,
|
|
650
|
+
total: unresolvedCount,
|
|
651
|
+
});
|
|
652
|
+
await this.resolveReferencesBatched((current, total) => {
|
|
653
|
+
options.onProgress?.({
|
|
654
|
+
phase: 'resolving',
|
|
655
|
+
current,
|
|
656
|
+
total,
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// Refresh planner stats + checkpoint the WAL after bulk writes.
|
|
662
|
+
if (result.filesAdded > 0 || result.filesModified > 0 || result.filesRemoved > 0) {
|
|
663
|
+
this.db.runMaintenance();
|
|
664
|
+
}
|
|
665
|
+
return result;
|
|
666
|
+
}
|
|
667
|
+
finally {
|
|
668
|
+
this.fileLock.release();
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Check if an indexing operation is currently in progress
|
|
674
|
+
*/
|
|
675
|
+
isIndexing() {
|
|
676
|
+
return this.indexMutex.isLocked();
|
|
677
|
+
}
|
|
678
|
+
// ===========================================================================
|
|
679
|
+
// File Watching
|
|
680
|
+
// ===========================================================================
|
|
681
|
+
/**
|
|
682
|
+
* Start watching for file changes and auto-syncing.
|
|
683
|
+
*
|
|
684
|
+
* Uses native OS file events (FSEvents on macOS, inotify on Linux 19+,
|
|
685
|
+
* ReadDirectoryChangesW on Windows) with debouncing to avoid thrashing.
|
|
686
|
+
*
|
|
687
|
+
* @param options - Watch options (debounce delay, callbacks)
|
|
688
|
+
* @returns true if watching started successfully
|
|
689
|
+
*/
|
|
690
|
+
watch(options = {}) {
|
|
691
|
+
if (this.watcher?.isActive())
|
|
692
|
+
return true;
|
|
693
|
+
this.watcher = new sync_1.FileWatcher(this.projectRoot, async () => {
|
|
694
|
+
const result = await this.sync();
|
|
695
|
+
// sync() returns this exact zero-shape iff it failed to acquire the
|
|
696
|
+
// file lock (a real empty sync always has filesChecked > 0 because
|
|
697
|
+
// scanDirectory ran). Surface that to the watcher as a typed error
|
|
698
|
+
// so it keeps pendingFiles + reschedules instead of clearing them
|
|
699
|
+
// (#449).
|
|
700
|
+
if (result.filesChecked === 0 && result.durationMs === 0) {
|
|
701
|
+
throw new sync_1.LockUnavailableError();
|
|
702
|
+
}
|
|
703
|
+
const filesChanged = result.filesAdded + result.filesModified + result.filesRemoved;
|
|
704
|
+
return { filesChanged, durationMs: result.durationMs };
|
|
705
|
+
}, options);
|
|
706
|
+
return this.watcher.start();
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Stop watching for file changes.
|
|
710
|
+
*/
|
|
711
|
+
unwatch() {
|
|
712
|
+
if (this.watcher) {
|
|
713
|
+
this.watcher.stop();
|
|
714
|
+
this.watcher = null;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Check if the file watcher is active.
|
|
719
|
+
*/
|
|
720
|
+
isWatching() {
|
|
721
|
+
return this.watcher?.isActive() ?? false;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Files seen by the file watcher since the last successful sync —
|
|
725
|
+
* the per-file "stale" signal MCP tools attach to responses so an agent
|
|
726
|
+
* can fall back to {@link Read} for just the affected file without
|
|
727
|
+
* waiting for a debounced sync to complete (issue #403).
|
|
728
|
+
*
|
|
729
|
+
* Returns an empty list when the watcher isn't active, or no events have
|
|
730
|
+
* arrived. Each entry includes `firstSeenMs` and `lastSeenMs` (wall-clock
|
|
731
|
+
* `Date.now()` values) so callers can render "edited Nms ago", plus an
|
|
732
|
+
* `indexing` flag indicating whether the in-flight sync (if any) will
|
|
733
|
+
* absorb that file.
|
|
734
|
+
*/
|
|
735
|
+
getPendingFiles() {
|
|
736
|
+
return this.watcher?.getPendingFiles() ?? [];
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Resolves once the file watcher has installed its watch set. Useful for
|
|
740
|
+
* tests that need a deterministic boundary before asserting on
|
|
741
|
+
* `getPendingFiles()`. Resolves immediately when no watcher is active.
|
|
742
|
+
*/
|
|
743
|
+
waitUntilWatcherReady(timeoutMs) {
|
|
744
|
+
return this.watcher ? this.watcher.waitUntilReady(timeoutMs) : Promise.resolve();
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Get files that have changed since last index
|
|
748
|
+
*/
|
|
749
|
+
getChangedFiles() {
|
|
750
|
+
return this.orchestrator.getChangedFiles();
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Most recent index timestamp (ms since epoch) across all tracked files, or
|
|
754
|
+
* null when nothing is indexed yet. Lets library consumers check index
|
|
755
|
+
* freshness without shelling out to `specship status --json`. (#329)
|
|
756
|
+
*/
|
|
757
|
+
getLastIndexedAt() {
|
|
758
|
+
return this.queries.getLastIndexedAt();
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Extract nodes and edges from source code (without storing)
|
|
762
|
+
*/
|
|
763
|
+
extractFromSource(filePath, source) {
|
|
764
|
+
return (0, extraction_1.extractFromSource)(filePath, source);
|
|
765
|
+
}
|
|
766
|
+
// ===========================================================================
|
|
767
|
+
// Reference Resolution
|
|
768
|
+
// ===========================================================================
|
|
769
|
+
/**
|
|
770
|
+
* Resolve unresolved references and create edges
|
|
771
|
+
*
|
|
772
|
+
* This method takes unresolved references from extraction and attempts
|
|
773
|
+
* to resolve them using multiple strategies:
|
|
774
|
+
* - Framework-specific patterns (React, Express, Laravel)
|
|
775
|
+
* - Import-based resolution
|
|
776
|
+
* - Name-based symbol matching
|
|
777
|
+
*/
|
|
778
|
+
resolveReferences(onProgress) {
|
|
779
|
+
// Get all unresolved references from the database
|
|
780
|
+
const unresolvedRefs = this.queries.getUnresolvedReferences();
|
|
781
|
+
return this.resolver.resolveAndPersist(unresolvedRefs, onProgress);
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Resolve references in batches to keep memory bounded on large codebases.
|
|
785
|
+
* Processes chunks of unresolved refs, persisting results after each batch.
|
|
786
|
+
*/
|
|
787
|
+
async resolveReferencesBatched(onProgress) {
|
|
788
|
+
return this.resolver.resolveAndPersistBatched(onProgress);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Get detected frameworks in the project
|
|
792
|
+
*/
|
|
793
|
+
getDetectedFrameworks() {
|
|
794
|
+
return this.resolver.getDetectedFrameworks();
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Re-initialize the resolver (useful after adding new files)
|
|
798
|
+
*/
|
|
799
|
+
reinitializeResolver() {
|
|
800
|
+
this.resolver.initialize();
|
|
801
|
+
}
|
|
802
|
+
// ===========================================================================
|
|
803
|
+
// Graph Statistics
|
|
804
|
+
// ===========================================================================
|
|
805
|
+
/**
|
|
806
|
+
* Get statistics about the knowledge graph
|
|
807
|
+
*/
|
|
808
|
+
getStats() {
|
|
809
|
+
const stats = this.queries.getStats();
|
|
810
|
+
stats.dbSizeBytes = this.db.getSize();
|
|
811
|
+
return stats;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Active SQLite backend for this project's connection (`node-sqlite` — Node's
|
|
815
|
+
* built-in real-SQLite module). Surfaced via `specship status` and the
|
|
816
|
+
* `specship_status` MCP tool alongside the effective journal mode.
|
|
817
|
+
*/
|
|
818
|
+
getBackend() {
|
|
819
|
+
return this.db.getBackend();
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* The journal mode actually in effect ('wal', 'delete', …). 'wal' means
|
|
823
|
+
* readers never block on a concurrent writer; anything else means they can,
|
|
824
|
+
* which is the precondition for the "database is locked" failures in issue
|
|
825
|
+
* #238. Surfaced via `specship status` and the `specship_status` MCP tool.
|
|
826
|
+
*/
|
|
827
|
+
getJournalMode() {
|
|
828
|
+
return this.db.getJournalMode();
|
|
829
|
+
}
|
|
830
|
+
// ===========================================================================
|
|
831
|
+
// Node Operations
|
|
832
|
+
// ===========================================================================
|
|
833
|
+
/**
|
|
834
|
+
* Get a node by ID
|
|
835
|
+
*/
|
|
836
|
+
getNode(id) {
|
|
837
|
+
return this.queries.getNodeById(id);
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Get all nodes in a file
|
|
841
|
+
*/
|
|
842
|
+
getNodesInFile(filePath) {
|
|
843
|
+
return this.queries.getNodesByFile(filePath);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get all nodes of a specific kind
|
|
847
|
+
*/
|
|
848
|
+
getNodesByKind(kind) {
|
|
849
|
+
return this.queries.getNodesByKind(kind);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Get ALL nodes with an exact name (direct index lookup, not FTS-ranked/capped).
|
|
853
|
+
* Used to enumerate every overload of a heavily-overloaded name so the specific
|
|
854
|
+
* definition the caller wants is never dropped below a search cut.
|
|
855
|
+
*/
|
|
856
|
+
getNodesByName(name) {
|
|
857
|
+
return this.queries.getNodesByName(name);
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Search nodes by text
|
|
861
|
+
*/
|
|
862
|
+
searchNodes(query, options) {
|
|
863
|
+
return this.queries.searchNodes(query, options);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Find the project's "primary route file" — the file with the densest
|
|
867
|
+
* concentration of framework-emitted `route` nodes (≥3 routes, ≥30%
|
|
868
|
+
* of all non-test routes). Used to inline the routing config in
|
|
869
|
+
* `specship_explore` responses on small realworld template repos
|
|
870
|
+
* (rails-realworld, laravel-realworld, drupal-admintoolbar, …) where
|
|
871
|
+
* Glob+Read of `routes.rb`/`urls.py`/etc. otherwise beats specship.
|
|
872
|
+
*/
|
|
873
|
+
getTopRouteFile() {
|
|
874
|
+
return this.queries.getTopRouteFile();
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Build a URL → handler routing manifest from the index. Each entry
|
|
878
|
+
* pairs a route node (URL + method) with its handler function/method
|
|
879
|
+
* via the `references` edge that framework resolvers emit. Returns
|
|
880
|
+
* null when fewer than 3 valid (non-test) routes exist.
|
|
881
|
+
*/
|
|
882
|
+
getRoutingManifest(limit) {
|
|
883
|
+
return this.queries.getRoutingManifest(limit);
|
|
884
|
+
}
|
|
885
|
+
// ===========================================================================
|
|
886
|
+
// Edge Operations
|
|
887
|
+
// ===========================================================================
|
|
888
|
+
/**
|
|
889
|
+
* Get outgoing edges from a node
|
|
890
|
+
*/
|
|
891
|
+
getOutgoingEdges(nodeId) {
|
|
892
|
+
return this.queries.getOutgoingEdges(nodeId);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Get incoming edges to a node
|
|
896
|
+
*/
|
|
897
|
+
getIncomingEdges(nodeId) {
|
|
898
|
+
return this.queries.getIncomingEdges(nodeId);
|
|
899
|
+
}
|
|
900
|
+
// ===========================================================================
|
|
901
|
+
// File Operations
|
|
902
|
+
// ===========================================================================
|
|
903
|
+
/**
|
|
904
|
+
* Get a file record by path
|
|
905
|
+
*/
|
|
906
|
+
getFile(filePath) {
|
|
907
|
+
return this.queries.getFileByPath(filePath);
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Get all tracked files
|
|
911
|
+
*/
|
|
912
|
+
getFiles() {
|
|
913
|
+
return this.queries.getAllFiles();
|
|
914
|
+
}
|
|
915
|
+
// ===========================================================================
|
|
916
|
+
// Graph Query Methods
|
|
917
|
+
// ===========================================================================
|
|
918
|
+
/**
|
|
919
|
+
* Get the context for a node (ancestors, children, references)
|
|
920
|
+
*
|
|
921
|
+
* Returns comprehensive context about a node including its containment
|
|
922
|
+
* hierarchy, children, incoming/outgoing references, type information,
|
|
923
|
+
* and relevant imports.
|
|
924
|
+
*
|
|
925
|
+
* @param nodeId - ID of the focal node
|
|
926
|
+
* @returns Context object with all related information
|
|
927
|
+
*/
|
|
928
|
+
getContext(nodeId) {
|
|
929
|
+
return this.graphManager.getContext(nodeId);
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Traverse the graph from a starting node
|
|
933
|
+
*
|
|
934
|
+
* Uses breadth-first search by default. Supports filtering by edge types,
|
|
935
|
+
* node types, and traversal direction.
|
|
936
|
+
*
|
|
937
|
+
* @param startId - Starting node ID
|
|
938
|
+
* @param options - Traversal options
|
|
939
|
+
* @returns Subgraph containing traversed nodes and edges
|
|
940
|
+
*/
|
|
941
|
+
traverse(startId, options) {
|
|
942
|
+
return this.traverser.traverseBFS(startId, options);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Get the call graph for a function
|
|
946
|
+
*
|
|
947
|
+
* Returns both callers (functions that call this function) and
|
|
948
|
+
* callees (functions called by this function) up to the specified depth.
|
|
949
|
+
*
|
|
950
|
+
* @param nodeId - ID of the function/method node
|
|
951
|
+
* @param depth - Maximum depth in each direction (default: 2)
|
|
952
|
+
* @returns Subgraph containing the call graph
|
|
953
|
+
*/
|
|
954
|
+
getCallGraph(nodeId, depth = 2) {
|
|
955
|
+
return this.traverser.getCallGraph(nodeId, depth);
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Get the type hierarchy for a class/interface
|
|
959
|
+
*
|
|
960
|
+
* Returns both ancestors (types this extends/implements) and
|
|
961
|
+
* descendants (types that extend/implement this).
|
|
962
|
+
*
|
|
963
|
+
* @param nodeId - ID of the class/interface node
|
|
964
|
+
* @returns Subgraph containing the type hierarchy
|
|
965
|
+
*/
|
|
966
|
+
getTypeHierarchy(nodeId) {
|
|
967
|
+
return this.traverser.getTypeHierarchy(nodeId);
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Find all usages of a symbol
|
|
971
|
+
*
|
|
972
|
+
* Returns all nodes that reference the specified symbol through
|
|
973
|
+
* any edge type (calls, references, type_of, etc.).
|
|
974
|
+
*
|
|
975
|
+
* @param nodeId - ID of the symbol node
|
|
976
|
+
* @returns Array of nodes and edges that reference this symbol
|
|
977
|
+
*/
|
|
978
|
+
findUsages(nodeId) {
|
|
979
|
+
return this.traverser.findUsages(nodeId);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Get callers of a function/method
|
|
983
|
+
*
|
|
984
|
+
* @param nodeId - ID of the function/method node
|
|
985
|
+
* @param maxDepth - Maximum depth to traverse (default: 1)
|
|
986
|
+
* @returns Array of nodes that call this function
|
|
987
|
+
*/
|
|
988
|
+
getCallers(nodeId, maxDepth = 1) {
|
|
989
|
+
return this.traverser.getCallers(nodeId, maxDepth);
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Get callees of a function/method
|
|
993
|
+
*
|
|
994
|
+
* @param nodeId - ID of the function/method node
|
|
995
|
+
* @param maxDepth - Maximum depth to traverse (default: 1)
|
|
996
|
+
* @returns Array of nodes called by this function
|
|
997
|
+
*/
|
|
998
|
+
getCallees(nodeId, maxDepth = 1) {
|
|
999
|
+
return this.traverser.getCallees(nodeId, maxDepth);
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Calculate the impact radius of a node
|
|
1003
|
+
*
|
|
1004
|
+
* Returns all nodes that could be affected by changes to this node.
|
|
1005
|
+
*
|
|
1006
|
+
* @param nodeId - ID of the node
|
|
1007
|
+
* @param maxDepth - Maximum depth to traverse (default: 3)
|
|
1008
|
+
* @returns Subgraph containing potentially impacted nodes
|
|
1009
|
+
*/
|
|
1010
|
+
getImpactRadius(nodeId, maxDepth = 3) {
|
|
1011
|
+
return this.traverser.getImpactRadius(nodeId, maxDepth);
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Find the shortest path between two nodes
|
|
1015
|
+
*
|
|
1016
|
+
* @param fromId - Starting node ID
|
|
1017
|
+
* @param toId - Target node ID
|
|
1018
|
+
* @param edgeKinds - Edge types to consider (all if empty)
|
|
1019
|
+
* @returns Array of nodes and edges forming the path, or null if no path exists
|
|
1020
|
+
*/
|
|
1021
|
+
findPath(fromId, toId, edgeKinds) {
|
|
1022
|
+
return this.traverser.findPath(fromId, toId, edgeKinds);
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Get ancestors of a node in the containment hierarchy
|
|
1026
|
+
*
|
|
1027
|
+
* @param nodeId - ID of the node
|
|
1028
|
+
* @returns Array of ancestor nodes from immediate parent to root
|
|
1029
|
+
*/
|
|
1030
|
+
getAncestors(nodeId) {
|
|
1031
|
+
return this.traverser.getAncestors(nodeId);
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Get immediate children of a node
|
|
1035
|
+
*
|
|
1036
|
+
* @param nodeId - ID of the node
|
|
1037
|
+
* @returns Array of child nodes
|
|
1038
|
+
*/
|
|
1039
|
+
getChildren(nodeId) {
|
|
1040
|
+
return this.traverser.getChildren(nodeId);
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Get dependencies of a file
|
|
1044
|
+
*
|
|
1045
|
+
* @param filePath - Path to the file
|
|
1046
|
+
* @returns Array of file paths this file depends on
|
|
1047
|
+
*/
|
|
1048
|
+
getFileDependencies(filePath) {
|
|
1049
|
+
return this.graphManager.getFileDependencies(filePath);
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Get dependents of a file
|
|
1053
|
+
*
|
|
1054
|
+
* @param filePath - Path to the file
|
|
1055
|
+
* @returns Array of file paths that depend on this file
|
|
1056
|
+
*/
|
|
1057
|
+
getFileDependents(filePath) {
|
|
1058
|
+
return this.graphManager.getFileDependents(filePath);
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Find circular dependencies in the codebase
|
|
1062
|
+
*
|
|
1063
|
+
* @returns Array of cycles, each cycle is an array of file paths
|
|
1064
|
+
*/
|
|
1065
|
+
findCircularDependencies() {
|
|
1066
|
+
return this.graphManager.findCircularDependencies();
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Find dead code (unreferenced symbols)
|
|
1070
|
+
*
|
|
1071
|
+
* @param kinds - Node kinds to check (default: functions, methods, classes)
|
|
1072
|
+
* @returns Array of unreferenced nodes
|
|
1073
|
+
*/
|
|
1074
|
+
findDeadCode(kinds) {
|
|
1075
|
+
return this.graphManager.findDeadCode(kinds);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Get complexity metrics for a node
|
|
1079
|
+
*
|
|
1080
|
+
* @param nodeId - ID of the node
|
|
1081
|
+
* @returns Object containing various complexity metrics
|
|
1082
|
+
*/
|
|
1083
|
+
getNodeMetrics(nodeId) {
|
|
1084
|
+
return this.graphManager.getNodeMetrics(nodeId);
|
|
1085
|
+
}
|
|
1086
|
+
// ===========================================================================
|
|
1087
|
+
// Context Building
|
|
1088
|
+
// ===========================================================================
|
|
1089
|
+
/**
|
|
1090
|
+
* Get the source code for a node
|
|
1091
|
+
*
|
|
1092
|
+
* Reads the file and extracts the code between startLine and endLine.
|
|
1093
|
+
*
|
|
1094
|
+
* @param nodeId - ID of the node
|
|
1095
|
+
* @returns Code string or null if not found
|
|
1096
|
+
*/
|
|
1097
|
+
async getCode(nodeId) {
|
|
1098
|
+
return this.contextBuilder.getCode(nodeId);
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Find relevant subgraph for a query
|
|
1102
|
+
*
|
|
1103
|
+
* Combines semantic search with graph traversal to find the most
|
|
1104
|
+
* relevant nodes and their relationships for a given query.
|
|
1105
|
+
*
|
|
1106
|
+
* @param query - Natural language query describing the task
|
|
1107
|
+
* @param options - Search and traversal options
|
|
1108
|
+
* @returns Subgraph of relevant nodes and edges
|
|
1109
|
+
*/
|
|
1110
|
+
async findRelevantContext(query, options) {
|
|
1111
|
+
return this.contextBuilder.findRelevantContext(query, options);
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Build context for a task
|
|
1115
|
+
*
|
|
1116
|
+
* Creates comprehensive context by:
|
|
1117
|
+
* 1. Running FTS search to find entry points
|
|
1118
|
+
* 2. Expanding the graph around entry points
|
|
1119
|
+
* 3. Extracting code blocks for key nodes
|
|
1120
|
+
* 4. Formatting output for Claude
|
|
1121
|
+
*
|
|
1122
|
+
* @param input - Task description (string or {title, description})
|
|
1123
|
+
* @param options - Build options (maxNodes, includeCode, format, etc.)
|
|
1124
|
+
* @returns TaskContext object or formatted string (markdown/JSON)
|
|
1125
|
+
*/
|
|
1126
|
+
async buildContext(input, options) {
|
|
1127
|
+
return this.contextBuilder.buildContext(input, options);
|
|
1128
|
+
}
|
|
1129
|
+
// ===========================================================================
|
|
1130
|
+
// Database Management
|
|
1131
|
+
// ===========================================================================
|
|
1132
|
+
/**
|
|
1133
|
+
* Optimize the database (vacuum and analyze)
|
|
1134
|
+
*/
|
|
1135
|
+
optimize() {
|
|
1136
|
+
this.db.optimize();
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Clear all data from the graph
|
|
1140
|
+
*/
|
|
1141
|
+
clear() {
|
|
1142
|
+
this.queries.clear();
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Alias for close() for backwards compatibility.
|
|
1146
|
+
* @deprecated Use close() instead
|
|
1147
|
+
*/
|
|
1148
|
+
destroy() {
|
|
1149
|
+
this.close();
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Completely remove SpecShip from the project.
|
|
1153
|
+
* This closes the database and deletes the .SpecShip directory.
|
|
1154
|
+
*
|
|
1155
|
+
* WARNING: This permanently deletes all SpecShip data for the project.
|
|
1156
|
+
*/
|
|
1157
|
+
uninitialize() {
|
|
1158
|
+
this.close();
|
|
1159
|
+
(0, directory_1.removeDirectory)(this.projectRoot);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
exports.SpecShip = SpecShip;
|
|
1163
|
+
// Default export
|
|
1164
|
+
exports.default = SpecShip;
|
|
1165
|
+
//# sourceMappingURL=index.js.map
|