cgraphx 1.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/LICENSE +21 -0
- package/README.md +243 -0
- package/dist/.claude-template/commands/my-commit.md +9 -0
- package/dist/.claude-template/commands/my-query.md +4 -0
- package/dist/.claude-template/hooks/context-monitor/context-monitor.cjs +216 -0
- package/dist/.claude-template/plugins/claude-hud/dist/claude-config-dir.d.ts +4 -0
- package/dist/.claude-template/plugins/claude-hud/dist/claude-config-dir.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/claude-config-dir.js +24 -0
- package/dist/.claude-template/plugins/claude-hud/dist/claude-config-dir.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config-reader.d.ts +8 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config-reader.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config-reader.js +204 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config-reader.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config.d.ts +46 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config.js +220 -0
- package/dist/.claude-template/plugins/claude-hud/dist/config.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/constants.d.ts +10 -0
- package/dist/.claude-template/plugins/claude-hud/dist/constants.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/constants.js +10 -0
- package/dist/.claude-template/plugins/claude-hud/dist/constants.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/debug.d.ts +6 -0
- package/dist/.claude-template/plugins/claude-hud/dist/debug.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/debug.js +15 -0
- package/dist/.claude-template/plugins/claude-hud/dist/debug.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/extra-cmd.d.ts +23 -0
- package/dist/.claude-template/plugins/claude-hud/dist/extra-cmd.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/extra-cmd.js +103 -0
- package/dist/.claude-template/plugins/claude-hud/dist/extra-cmd.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/git.d.ts +16 -0
- package/dist/.claude-template/plugins/claude-hud/dist/git.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/git.js +86 -0
- package/dist/.claude-template/plugins/claude-hud/dist/git.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/index.d.ts +24 -0
- package/dist/.claude-template/plugins/claude-hud/dist/index.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/index.js +97 -0
- package/dist/.claude-template/plugins/claude-hud/dist/index.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/agents-line.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/agents-line.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/agents-line.js +44 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/agents-line.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/colors.d.ts +12 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/colors.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/colors.js +58 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/colors.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/index.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/index.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/index.js +379 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/index.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/environment.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/environment.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/environment.js +30 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/environment.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/identity.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/identity.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/identity.js +52 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/identity.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/index.d.ts +5 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/index.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/index.js +5 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/index.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/project.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/project.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/project.js +74 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/project.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/usage.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/usage.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/usage.js +92 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/lines/usage.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/session-line.d.ts +7 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/session-line.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/session-line.js +247 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/session-line.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/todos-line.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/todos-line.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/todos-line.js +25 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/todos-line.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/tools-line.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/tools-line.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/tools-line.js +43 -0
- package/dist/.claude-template/plugins/claude-hud/dist/render/tools-line.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/speed-tracker.d.ts +7 -0
- package/dist/.claude-template/plugins/claude-hud/dist/speed-tracker.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/speed-tracker.js +62 -0
- package/dist/.claude-template/plugins/claude-hud/dist/speed-tracker.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/stdin.d.ts +9 -0
- package/dist/.claude-template/plugins/claude-hud/dist/stdin.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/stdin.js +136 -0
- package/dist/.claude-template/plugins/claude-hud/dist/stdin.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/transcript.d.ts +3 -0
- package/dist/.claude-template/plugins/claude-hud/dist/transcript.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/transcript.js +189 -0
- package/dist/.claude-template/plugins/claude-hud/dist/transcript.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/types.d.ts +79 -0
- package/dist/.claude-template/plugins/claude-hud/dist/types.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/types.js +5 -0
- package/dist/.claude-template/plugins/claude-hud/dist/types.js.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/usage-api.d.ts +59 -0
- package/dist/.claude-template/plugins/claude-hud/dist/usage-api.d.ts.map +1 -0
- package/dist/.claude-template/plugins/claude-hud/dist/usage-api.js +733 -0
- package/dist/.claude-template/plugins/claude-hud/dist/usage-api.js.map +1 -0
- package/dist/.claude-template/skills/cgraphx/SKILL.md +143 -0
- package/dist/.claude-template/skills/cgraphx/agent-prompt.md +56 -0
- package/dist/.claude-template/skills/clarify-requirements/SKILL.md +425 -0
- package/dist/.claude-template/skills/code-impact-api/SKILL.md +143 -0
- package/dist/.claude-template/skills/code-impact-api/agent-prompt.md +51 -0
- package/dist/.claude-template/skills/code-impact-docgen/SKILL.md +366 -0
- package/dist/.claude-template/skills/code-impact-docgen/template-business-html.md +242 -0
- package/dist/.claude-template/skills/code-impact-docgen/template-business-md.md +107 -0
- package/dist/.claude-template/skills/code-impact-docgen/template-technical-html.md +205 -0
- package/dist/.claude-template/skills/code-impact-docgen/template-technical-md.md +155 -0
- package/dist/.claude-template/skills/code-impact-init/SKILL.md +800 -0
- package/dist/.claude-template/skills/code-impact-markdown/SKILL.md +345 -0
- package/dist/.claude-template/skills/code-impact-markdown/template-guide.md +68 -0
- package/dist/.claude-template/skills/code-impact-markdown/template-memory.md +82 -0
- package/dist/.claude-template/skills/code-impact-markdown/template-runbook.md +58 -0
- package/dist/.claude-template/skills/db-query/SKILL.md +166 -0
- package/dist/.claude-template/skills/db-query/agent-prompt.md +55 -0
- package/dist/.claude-template/skills/developer-timeline/SKILL.md +302 -0
- package/dist/.claude-template/skills/developer-timeline/demo-single-page-report.html +657 -0
- package/dist/.claude-template/skills/implementation/SKILL.md +136 -0
- package/dist/.claude-template/skills/subagent-implement/SKILL.md +225 -0
- package/dist/.claude-template/skills/subagent-implement/implementer-prompt.md +127 -0
- package/dist/.claude-template/skills/subagent-implement/quality-reviewer-prompt.md +130 -0
- package/dist/.claude-template/skills/subagent-implement/spec-reviewer-prompt.md +112 -0
- package/dist/.claude-template/skills/write-plan/SKILL.md +322 -0
- package/dist/.claude-template/skills/write-plan/plan-document-reviewer-prompt.md +134 -0
- package/dist/.claude-template/skills/write-prd/SKILL.md +242 -0
- package/dist/.claude-template/skills/write-spec/SKILL.md +278 -0
- package/dist/bin/codegraph.d.ts +26 -0
- package/dist/bin/codegraph.d.ts.map +1 -0
- package/dist/bin/codegraph.js +2014 -0
- package/dist/bin/codegraph.js.map +1 -0
- package/dist/bin/fatal-handler.d.ts +20 -0
- package/dist/bin/fatal-handler.d.ts.map +1 -0
- package/dist/bin/fatal-handler.js +118 -0
- package/dist/bin/fatal-handler.js.map +1 -0
- package/dist/bin/node-version-check.d.ts +51 -0
- package/dist/bin/node-version-check.d.ts.map +1 -0
- package/dist/bin/node-version-check.js +114 -0
- package/dist/bin/node-version-check.js.map +1 -0
- package/dist/bin/uninstall.d.ts +14 -0
- package/dist/bin/uninstall.d.ts.map +1 -0
- package/dist/bin/uninstall.js +36 -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 +1296 -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 +122 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +296 -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 +140 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/queries.d.ts +401 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +1591 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.sql +152 -0
- package/dist/db/sqlite-adapter.d.ts +53 -0
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.js +117 -0
- package/dist/db/sqlite-adapter.js.map +1 -0
- package/dist/dbquery/cli.d.ts +17 -0
- package/dist/dbquery/cli.d.ts.map +1 -0
- package/dist/dbquery/cli.js +229 -0
- package/dist/dbquery/cli.js.map +1 -0
- package/dist/dbquery/config.d.ts +38 -0
- package/dist/dbquery/config.d.ts.map +1 -0
- package/dist/dbquery/config.js +244 -0
- package/dist/dbquery/config.js.map +1 -0
- package/dist/dbquery/constants.d.ts +40 -0
- package/dist/dbquery/constants.d.ts.map +1 -0
- package/dist/dbquery/constants.js +65 -0
- package/dist/dbquery/constants.js.map +1 -0
- package/dist/dbquery/drivers/mysql.d.ts +15 -0
- package/dist/dbquery/drivers/mysql.d.ts.map +1 -0
- package/dist/dbquery/drivers/mysql.js +102 -0
- package/dist/dbquery/drivers/mysql.js.map +1 -0
- package/dist/dbquery/drivers/postgres.d.ts +16 -0
- package/dist/dbquery/drivers/postgres.d.ts.map +1 -0
- package/dist/dbquery/drivers/postgres.js +105 -0
- package/dist/dbquery/drivers/postgres.js.map +1 -0
- package/dist/dbquery/errors.d.ts +40 -0
- package/dist/dbquery/errors.d.ts.map +1 -0
- package/dist/dbquery/errors.js +85 -0
- package/dist/dbquery/errors.js.map +1 -0
- package/dist/dbquery/executor.d.ts +30 -0
- package/dist/dbquery/executor.d.ts.map +1 -0
- package/dist/dbquery/executor.js +243 -0
- package/dist/dbquery/executor.js.map +1 -0
- package/dist/dbquery/format.d.ts +18 -0
- package/dist/dbquery/format.d.ts.map +1 -0
- package/dist/dbquery/format.js +174 -0
- package/dist/dbquery/format.js.map +1 -0
- package/dist/dbquery/index.d.ts +10 -0
- package/dist/dbquery/index.d.ts.map +1 -0
- package/dist/dbquery/index.js +23 -0
- package/dist/dbquery/index.js.map +1 -0
- package/dist/dbquery/init.d.ts +33 -0
- package/dist/dbquery/init.d.ts.map +1 -0
- package/dist/dbquery/init.js +125 -0
- package/dist/dbquery/init.js.map +1 -0
- package/dist/dbquery/logging.d.ts +22 -0
- package/dist/dbquery/logging.d.ts.map +1 -0
- package/dist/dbquery/logging.js +140 -0
- package/dist/dbquery/logging.js.map +1 -0
- package/dist/dbquery/mcp-tools.d.ts +29 -0
- package/dist/dbquery/mcp-tools.d.ts.map +1 -0
- package/dist/dbquery/mcp-tools.js +206 -0
- package/dist/dbquery/mcp-tools.js.map +1 -0
- package/dist/dbquery/queries.d.ts +31 -0
- package/dist/dbquery/queries.d.ts.map +1 -0
- package/dist/dbquery/queries.js +160 -0
- package/dist/dbquery/queries.js.map +1 -0
- package/dist/dbquery/safety.d.ts +35 -0
- package/dist/dbquery/safety.d.ts.map +1 -0
- package/dist/dbquery/safety.js +306 -0
- package/dist/dbquery/safety.js.map +1 -0
- package/dist/dbquery/types.d.ts +152 -0
- package/dist/dbquery/types.d.ts.map +1 -0
- package/dist/dbquery/types.js +10 -0
- package/dist/dbquery/types.js.map +1 -0
- package/dist/directory.d.ts +147 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +523 -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/astro-extractor.d.ts +79 -0
- package/dist/extraction/astro-extractor.d.ts.map +1 -0
- package/dist/extraction/astro-extractor.js +320 -0
- package/dist/extraction/astro-extractor.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/extraction-version.d.ts +25 -0
- package/dist/extraction/extraction-version.d.ts.map +1 -0
- package/dist/extraction/extraction-version.js +28 -0
- package/dist/extraction/extraction-version.js.map +1 -0
- package/dist/extraction/function-ref.d.ts +118 -0
- package/dist/extraction/function-ref.d.ts.map +1 -0
- package/dist/extraction/function-ref.js +727 -0
- package/dist/extraction/function-ref.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 +83 -0
- package/dist/extraction/generated-detection.js.map +1 -0
- package/dist/extraction/grammars.d.ts +114 -0
- package/dist/extraction/grammars.d.ts.map +1 -0
- package/dist/extraction/grammars.js +477 -0
- package/dist/extraction/grammars.js.map +1 -0
- package/dist/extraction/index.d.ts +175 -0
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +1887 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/languages/c-cpp.d.ts +12 -0
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
- package/dist/extraction/languages/c-cpp.js +275 -0
- package/dist/extraction/languages/c-cpp.js.map +1 -0
- package/dist/extraction/languages/csharp.d.ts +25 -0
- package/dist/extraction/languages/csharp.d.ts.map +1 -0
- package/dist/extraction/languages/csharp.js +175 -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 +374 -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 +111 -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 +53 -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 +315 -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 +106 -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 +379 -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 +175 -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 +77 -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 +196 -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/r.d.ts +3 -0
- package/dist/extraction/languages/r.d.ts.map +1 -0
- package/dist/extraction/languages/r.js +314 -0
- package/dist/extraction/languages/r.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 +149 -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 +142 -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 +209 -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 +152 -0
- package/dist/extraction/languages/swift.js.map +1 -0
- package/dist/extraction/languages/typescript.d.ts +16 -0
- package/dist/extraction/languages/typescript.d.ts.map +1 -0
- package/dist/extraction/languages/typescript.js +167 -0
- package/dist/extraction/languages/typescript.js.map +1 -0
- package/dist/extraction/liquid-extractor.d.ts +59 -0
- package/dist/extraction/liquid-extractor.d.ts.map +1 -0
- package/dist/extraction/liquid-extractor.js +357 -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 +97 -0
- package/dist/extraction/parse-worker.js.map +1 -0
- package/dist/extraction/razor-extractor.d.ts +42 -0
- package/dist/extraction/razor-extractor.d.ts.map +1 -0
- package/dist/extraction/razor-extractor.js +285 -0
- package/dist/extraction/razor-extractor.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 +275 -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 +152 -0
- package/dist/extraction/tree-sitter-helpers.js.map +1 -0
- package/dist/extraction/tree-sitter-types.d.ts +239 -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 +647 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -0
- package/dist/extraction/tree-sitter.js +5592 -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 +254 -0
- package/dist/extraction/vue-extractor.js.map +1 -0
- package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -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-r.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 +339 -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 +540 -0
- package/dist/graph/traversal.js.map +1 -0
- package/dist/index.d.ts +563 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1041 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/claude-assets.d.ts +45 -0
- package/dist/installer/claude-assets.d.ts.map +1 -0
- package/dist/installer/claude-assets.js +144 -0
- package/dist/installer/claude-assets.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 +101 -0
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +692 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/instructions-template.d.ts +41 -0
- package/dist/installer/instructions-template.d.ts.map +1 -0
- package/dist/installer/instructions-template.js +53 -0
- package/dist/installer/instructions-template.js.map +1 -0
- package/dist/installer/targets/antigravity.d.ts +57 -0
- package/dist/installer/targets/antigravity.d.ts.map +1 -0
- package/dist/installer/targets/antigravity.js +308 -0
- package/dist/installer/targets/antigravity.js.map +1 -0
- package/dist/installer/targets/claude.d.ts +66 -0
- package/dist/installer/targets/claude.d.ts.map +1 -0
- package/dist/installer/targets/claude.js +564 -0
- package/dist/installer/targets/claude.js.map +1 -0
- package/dist/installer/targets/codex.d.ts +18 -0
- package/dist/installer/targets/codex.d.ts.map +1 -0
- package/dist/installer/targets/codex.js +185 -0
- package/dist/installer/targets/codex.js.map +1 -0
- package/dist/installer/targets/cursor.d.ts +35 -0
- package/dist/installer/targets/cursor.d.ts.map +1 -0
- package/dist/installer/targets/cursor.js +254 -0
- package/dist/installer/targets/cursor.js.map +1 -0
- package/dist/installer/targets/gemini.d.ts +26 -0
- package/dist/installer/targets/gemini.d.ts.map +1 -0
- package/dist/installer/targets/gemini.js +165 -0
- package/dist/installer/targets/gemini.js.map +1 -0
- package/dist/installer/targets/hermes.d.ts +18 -0
- package/dist/installer/targets/hermes.d.ts.map +1 -0
- package/dist/installer/targets/hermes.js +359 -0
- package/dist/installer/targets/hermes.js.map +1 -0
- package/dist/installer/targets/kiro.d.ts +27 -0
- package/dist/installer/targets/kiro.d.ts.map +1 -0
- package/dist/installer/targets/kiro.js +178 -0
- package/dist/installer/targets/kiro.js.map +1 -0
- package/dist/installer/targets/opencode.d.ts +38 -0
- package/dist/installer/targets/opencode.d.ts.map +1 -0
- package/dist/installer/targets/opencode.js +288 -0
- package/dist/installer/targets/opencode.js.map +1 -0
- package/dist/installer/targets/registry.d.ts +35 -0
- package/dist/installer/targets/registry.d.ts.map +1 -0
- package/dist/installer/targets/registry.js +91 -0
- package/dist/installer/targets/registry.js.map +1 -0
- package/dist/installer/targets/shared.d.ts +101 -0
- package/dist/installer/targets/shared.d.ts.map +1 -0
- package/dist/installer/targets/shared.js +264 -0
- package/dist/installer/targets/shared.js.map +1 -0
- package/dist/installer/targets/toml.d.ts +52 -0
- package/dist/installer/targets/toml.d.ts.map +1 -0
- package/dist/installer/targets/toml.js +147 -0
- package/dist/installer/targets/toml.js.map +1 -0
- package/dist/installer/targets/types.d.ts +108 -0
- package/dist/installer/targets/types.d.ts.map +1 -0
- package/dist/installer/targets/types.js +16 -0
- package/dist/installer/targets/types.js.map +1 -0
- package/dist/markdown/cli.d.ts +16 -0
- package/dist/markdown/cli.d.ts.map +1 -0
- package/dist/markdown/cli.js +533 -0
- package/dist/markdown/cli.js.map +1 -0
- package/dist/markdown/constants.d.ts +22 -0
- package/dist/markdown/constants.d.ts.map +1 -0
- package/dist/markdown/constants.js +71 -0
- package/dist/markdown/constants.js.map +1 -0
- package/dist/markdown/dedup.d.ts +20 -0
- package/dist/markdown/dedup.d.ts.map +1 -0
- package/dist/markdown/dedup.js +64 -0
- package/dist/markdown/dedup.js.map +1 -0
- package/dist/markdown/errors.d.ts +22 -0
- package/dist/markdown/errors.d.ts.map +1 -0
- package/dist/markdown/errors.js +45 -0
- package/dist/markdown/errors.js.map +1 -0
- package/dist/markdown/extractor.d.ts +43 -0
- package/dist/markdown/extractor.d.ts.map +1 -0
- package/dist/markdown/extractor.js +152 -0
- package/dist/markdown/extractor.js.map +1 -0
- package/dist/markdown/frontmatter-parser.d.ts +47 -0
- package/dist/markdown/frontmatter-parser.d.ts.map +1 -0
- package/dist/markdown/frontmatter-parser.js +199 -0
- package/dist/markdown/frontmatter-parser.js.map +1 -0
- package/dist/markdown/indexer.d.ts +34 -0
- package/dist/markdown/indexer.d.ts.map +1 -0
- package/dist/markdown/indexer.js +256 -0
- package/dist/markdown/indexer.js.map +1 -0
- package/dist/markdown/mcp-tools.d.ts +33 -0
- package/dist/markdown/mcp-tools.d.ts.map +1 -0
- package/dist/markdown/mcp-tools.js +300 -0
- package/dist/markdown/mcp-tools.js.map +1 -0
- package/dist/markdown/query.d.ts +108 -0
- package/dist/markdown/query.d.ts.map +1 -0
- package/dist/markdown/query.js +570 -0
- package/dist/markdown/query.js.map +1 -0
- package/dist/markdown/schema-bootstrap.d.ts +40 -0
- package/dist/markdown/schema-bootstrap.d.ts.map +1 -0
- package/dist/markdown/schema-bootstrap.js +85 -0
- package/dist/markdown/schema-bootstrap.js.map +1 -0
- package/dist/markdown/schema.sql +124 -0
- package/dist/markdown/store.d.ts +77 -0
- package/dist/markdown/store.d.ts.map +1 -0
- package/dist/markdown/store.js +194 -0
- package/dist/markdown/store.js.map +1 -0
- package/dist/markdown/summary-extractor.d.ts +22 -0
- package/dist/markdown/summary-extractor.d.ts.map +1 -0
- package/dist/markdown/summary-extractor.js +66 -0
- package/dist/markdown/summary-extractor.js.map +1 -0
- package/dist/markdown/types.d.ts +159 -0
- package/dist/markdown/types.d.ts.map +1 -0
- package/dist/markdown/types.js +9 -0
- package/dist/markdown/types.js.map +1 -0
- package/dist/markdown/validator.d.ts +44 -0
- package/dist/markdown/validator.d.ts.map +1 -0
- package/dist/markdown/validator.js +95 -0
- package/dist/markdown/validator.js.map +1 -0
- package/dist/mcp/daemon-manager.d.ts +42 -0
- package/dist/mcp/daemon-manager.d.ts.map +1 -0
- package/dist/mcp/daemon-manager.js +129 -0
- package/dist/mcp/daemon-manager.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-registry.d.ts +47 -0
- package/dist/mcp/daemon-registry.d.ts.map +1 -0
- package/dist/mcp/daemon-registry.js +229 -0
- package/dist/mcp/daemon-registry.js.map +1 -0
- package/dist/mcp/daemon.d.ts +220 -0
- package/dist/mcp/daemon.d.ts.map +1 -0
- package/dist/mcp/daemon.js +637 -0
- package/dist/mcp/daemon.js.map +1 -0
- package/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
- package/dist/mcp/dynamic-boundaries.js +359 -0
- package/dist/mcp/dynamic-boundaries.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 +278 -0
- package/dist/mcp/engine.js.map +1 -0
- package/dist/mcp/index.d.ts +113 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +499 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/liveness-watchdog.d.ts +18 -0
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -0
- package/dist/mcp/liveness-watchdog.js +207 -0
- package/dist/mcp/liveness-watchdog.js.map +1 -0
- package/dist/mcp/ppid-watchdog.d.ts +44 -0
- package/dist/mcp/ppid-watchdog.d.ts.map +1 -0
- package/dist/mcp/ppid-watchdog.js +27 -0
- package/dist/mcp/ppid-watchdog.js.map +1 -0
- package/dist/mcp/proxy.d.ts +87 -0
- package/dist/mcp/proxy.d.ts.map +1 -0
- package/dist/mcp/proxy.js +641 -0
- package/dist/mcp/proxy.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +34 -0
- package/dist/mcp/server-instructions.d.ts.map +1 -0
- package/dist/mcp/server-instructions.js +106 -0
- package/dist/mcp/server-instructions.js.map +1 -0
- package/dist/mcp/session.d.ts +79 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +330 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/stdin-teardown.d.ts +27 -0
- package/dist/mcp/stdin-teardown.d.ts.map +1 -0
- package/dist/mcp/stdin-teardown.js +49 -0
- package/dist/mcp/stdin-teardown.js.map +1 -0
- package/dist/mcp/tools.d.ts +547 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +4122 -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 +359 -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/project-config.d.ts +36 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +235 -0
- package/dist/project-config.js.map +1 -0
- package/dist/reasoning/config.d.ts +45 -0
- package/dist/reasoning/config.d.ts.map +1 -0
- package/dist/reasoning/config.js +171 -0
- package/dist/reasoning/config.js.map +1 -0
- package/dist/reasoning/credentials.d.ts +5 -0
- package/dist/reasoning/credentials.d.ts.map +1 -0
- package/dist/reasoning/credentials.js +83 -0
- package/dist/reasoning/credentials.js.map +1 -0
- package/dist/reasoning/login.d.ts +21 -0
- package/dist/reasoning/login.d.ts.map +1 -0
- package/dist/reasoning/login.js +85 -0
- package/dist/reasoning/login.js.map +1 -0
- package/dist/reasoning/reasoner.d.ts +43 -0
- package/dist/reasoning/reasoner.d.ts.map +1 -0
- package/dist/reasoning/reasoner.js +308 -0
- package/dist/reasoning/reasoner.js.map +1 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts +33 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
- package/dist/resolution/c-fnptr-synthesizer.js +352 -0
- package/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
- package/dist/resolution/callback-synthesizer.d.ts +15 -0
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/dist/resolution/callback-synthesizer.js +2926 -0
- package/dist/resolution/callback-synthesizer.js.map +1 -0
- package/dist/resolution/frameworks/astro.d.ts +9 -0
- package/dist/resolution/frameworks/astro.d.ts.map +1 -0
- package/dist/resolution/frameworks/astro.js +169 -0
- package/dist/resolution/frameworks/astro.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 +148 -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/goframe.d.ts +41 -0
- package/dist/resolution/frameworks/goframe.d.ts.map +1 -0
- package/dist/resolution/frameworks/goframe.js +112 -0
- package/dist/resolution/frameworks/goframe.js.map +1 -0
- package/dist/resolution/frameworks/index.d.ts +50 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -0
- package/dist/resolution/frameworks/index.js +169 -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 +509 -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 +400 -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 +410 -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 +334 -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 +253 -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 +303 -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/goframe-synthesizer.d.ts +28 -0
- package/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
- package/dist/resolution/goframe-synthesizer.js +158 -0
- package/dist/resolution/goframe-synthesizer.js.map +1 -0
- package/dist/resolution/import-resolver.d.ts +78 -0
- package/dist/resolution/import-resolver.d.ts.map +1 -0
- package/dist/resolution/import-resolver.js +1849 -0
- package/dist/resolution/import-resolver.js.map +1 -0
- package/dist/resolution/index.d.ts +196 -0
- package/dist/resolution/index.d.ts.map +1 -0
- package/dist/resolution/index.js +1328 -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 +93 -0
- package/dist/resolution/name-matcher.d.ts.map +1 -0
- package/dist/resolution/name-matcher.js +1212 -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/strip-comments.d.ts +27 -0
- package/dist/resolution/strip-comments.d.ts.map +1 -0
- package/dist/resolution/strip-comments.js +443 -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 +233 -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 +87 -0
- package/dist/search/query-utils.d.ts.map +1 -0
- package/dist/search/query-utils.js +449 -0
- package/dist/search/query-utils.js.map +1 -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 +350 -0
- package/dist/sync/watcher.d.ts.map +1 -0
- package/dist/sync/watcher.js +811 -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/telemetry/index.d.ts +143 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +541 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/timeline/bash-semantics.d.ts +52 -0
- package/dist/timeline/bash-semantics.d.ts.map +1 -0
- package/dist/timeline/bash-semantics.js +376 -0
- package/dist/timeline/bash-semantics.js.map +1 -0
- package/dist/timeline/cli.d.ts +50 -0
- package/dist/timeline/cli.d.ts.map +1 -0
- package/dist/timeline/cli.js +367 -0
- package/dist/timeline/cli.js.map +1 -0
- package/dist/timeline/constants.d.ts +62 -0
- package/dist/timeline/constants.d.ts.map +1 -0
- package/dist/timeline/constants.js +73 -0
- package/dist/timeline/constants.js.map +1 -0
- package/dist/timeline/errors.d.ts +27 -0
- package/dist/timeline/errors.d.ts.map +1 -0
- package/dist/timeline/errors.js +51 -0
- package/dist/timeline/errors.js.map +1 -0
- package/dist/timeline/hook-runner.d.ts +36 -0
- package/dist/timeline/hook-runner.d.ts.map +1 -0
- package/dist/timeline/hook-runner.js +61 -0
- package/dist/timeline/hook-runner.js.map +1 -0
- package/dist/timeline/hooks.d.ts +45 -0
- package/dist/timeline/hooks.d.ts.map +1 -0
- package/dist/timeline/hooks.js +364 -0
- package/dist/timeline/hooks.js.map +1 -0
- package/dist/timeline/index.d.ts +12 -0
- package/dist/timeline/index.d.ts.map +1 -0
- package/dist/timeline/index.js +28 -0
- package/dist/timeline/index.js.map +1 -0
- package/dist/timeline/indexer.d.ts +37 -0
- package/dist/timeline/indexer.d.ts.map +1 -0
- package/dist/timeline/indexer.js +76 -0
- package/dist/timeline/indexer.js.map +1 -0
- package/dist/timeline/installer.d.ts +33 -0
- package/dist/timeline/installer.d.ts.map +1 -0
- package/dist/timeline/installer.js +255 -0
- package/dist/timeline/installer.js.map +1 -0
- package/dist/timeline/payload.d.ts +31 -0
- package/dist/timeline/payload.d.ts.map +1 -0
- package/dist/timeline/payload.js +58 -0
- package/dist/timeline/payload.js.map +1 -0
- package/dist/timeline/post-tool-summary.d.ts +29 -0
- package/dist/timeline/post-tool-summary.d.ts.map +1 -0
- package/dist/timeline/post-tool-summary.js +190 -0
- package/dist/timeline/post-tool-summary.js.map +1 -0
- package/dist/timeline/recorder.d.ts +36 -0
- package/dist/timeline/recorder.d.ts.map +1 -0
- package/dist/timeline/recorder.js +42 -0
- package/dist/timeline/recorder.js.map +1 -0
- package/dist/timeline/schema-bootstrap.d.ts +42 -0
- package/dist/timeline/schema-bootstrap.d.ts.map +1 -0
- package/dist/timeline/schema-bootstrap.js +81 -0
- package/dist/timeline/schema-bootstrap.js.map +1 -0
- package/dist/timeline/schema.sql +37 -0
- package/dist/timeline/store.d.ts +69 -0
- package/dist/timeline/store.d.ts.map +1 -0
- package/dist/timeline/store.js +429 -0
- package/dist/timeline/store.js.map +1 -0
- package/dist/timeline/types.d.ts +78 -0
- package/dist/timeline/types.d.ts.map +1 -0
- package/dist/timeline/types.js +9 -0
- package/dist/timeline/types.js.map +1 -0
- package/dist/types.d.ts +392 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +81 -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/upgrade/index.d.ts +132 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +498 -0
- package/dist/upgrade/index.js.map +1 -0
- package/dist/utils.d.ts +224 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +583 -0
- package/dist/utils.js.map +1 -0
- package/package.json +60 -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/ab-adoption.sh +91 -0
- package/scripts/agent-eval/ab-hook.sh +86 -0
- package/scripts/agent-eval/ab-impl.sh +78 -0
- package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -0
- package/scripts/agent-eval/ab-sufficiency.sh +78 -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/offload-eval-3arm.sh +72 -0
- package/scripts/agent-eval/offload-eval-cost.mjs +133 -0
- package/scripts/agent-eval/offload-eval-effort.mjs +108 -0
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -0
- package/scripts/agent-eval/offload-eval-frontload.sh +47 -0
- package/scripts/agent-eval/offload-eval-ground-truth.json +18 -0
- package/scripts/agent-eval/offload-eval-hook.mjs +84 -0
- package/scripts/agent-eval/offload-eval-judge.mjs +103 -0
- package/scripts/agent-eval/offload-eval-matrix.sh +20 -0
- package/scripts/agent-eval/offload-eval-metrics.mjs +94 -0
- package/scripts/agent-eval/offload-eval-refs1.sh +50 -0
- package/scripts/agent-eval/offload-eval-setup.sh +24 -0
- package/scripts/agent-eval/offload-eval-styles.sh +72 -0
- package/scripts/agent-eval/offload-eval-summarize.mjs +68 -0
- package/scripts/agent-eval/offload-eval.md +76 -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/redirect-read-hook.sh +38 -0
- package/scripts/agent-eval/run-agent.sh +34 -0
- package/scripts/agent-eval/run-all.sh +69 -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/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/pack-npm.sh +118 -0
- package/scripts/prepare-release.mjs +270 -0
|
@@ -0,0 +1,1887 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Extraction Orchestrator
|
|
4
|
+
*
|
|
5
|
+
* Coordinates file scanning, parsing, and database storage.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.isSourceFile = exports.detectLanguage = exports.extractFromSource = exports.ExtractionOrchestrator = exports.ScopeIgnore = void 0;
|
|
45
|
+
exports.hashContent = hashContent;
|
|
46
|
+
exports.buildDefaultIgnore = buildDefaultIgnore;
|
|
47
|
+
exports.buildScopeIgnore = buildScopeIgnore;
|
|
48
|
+
exports.discoverEmbeddedRepoRoots = discoverEmbeddedRepoRoots;
|
|
49
|
+
exports.scanDirectory = scanDirectory;
|
|
50
|
+
exports.scanDirectoryAsync = scanDirectoryAsync;
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const fsp = __importStar(require("fs/promises"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const crypto = __importStar(require("crypto"));
|
|
55
|
+
const child_process_1 = require("child_process");
|
|
56
|
+
const tree_sitter_1 = require("./tree-sitter");
|
|
57
|
+
const grammars_1 = require("./grammars");
|
|
58
|
+
const project_config_1 = require("../project-config");
|
|
59
|
+
const directory_1 = require("../directory");
|
|
60
|
+
const errors_1 = require("../errors");
|
|
61
|
+
const utils_1 = require("../utils");
|
|
62
|
+
const ignore_1 = __importDefault(require("ignore"));
|
|
63
|
+
const frameworks_1 = require("../resolution/frameworks");
|
|
64
|
+
/**
|
|
65
|
+
* Number of files to read in parallel during indexing.
|
|
66
|
+
* File reads are I/O-bound; batching overlaps I/O wait with CPU parse work.
|
|
67
|
+
*/
|
|
68
|
+
const FILE_IO_BATCH_SIZE = 10;
|
|
69
|
+
/**
|
|
70
|
+
* How many files the `sync()` reconcile processes between cooperative yields to
|
|
71
|
+
* the event loop. The reconcile runs two O(files) loops of synchronous `fs`
|
|
72
|
+
* calls (existsSync for removals, statSync for adds/mods); on a very large repo
|
|
73
|
+
* (~100k files) an un-yielded run wedges the main thread for minutes, which both
|
|
74
|
+
* trips the liveness watchdog (it SIGKILLs a process whose loop stops turning)
|
|
75
|
+
* and blocks the first MCP tool call behind the catch-up gate (issue #905).
|
|
76
|
+
* Yielding every N files keeps the socket, the watchdog heartbeat, and any
|
|
77
|
+
* concurrent read query responsive while the reconcile runs.
|
|
78
|
+
*/
|
|
79
|
+
const SYNC_RECONCILE_YIELD_INTERVAL = 1000;
|
|
80
|
+
// PARSER_RESET_INTERVAL moved to parse-worker.ts (runs in worker thread)
|
|
81
|
+
/**
|
|
82
|
+
* Maximum time (ms) to wait for a single file to parse in the worker thread.
|
|
83
|
+
* If tree-sitter hangs or WASM runs out of memory, this prevents the entire
|
|
84
|
+
* indexing run from freezing. The worker is restarted after a timeout.
|
|
85
|
+
*/
|
|
86
|
+
const PARSE_TIMEOUT_MS = 10_000;
|
|
87
|
+
/**
|
|
88
|
+
* Number of files to parse before recycling the worker thread.
|
|
89
|
+
* WASM linear memory can grow but NEVER shrink (WebAssembly spec limitation).
|
|
90
|
+
* The only way to reclaim tree-sitter's WASM heap is to destroy the entire
|
|
91
|
+
* V8 isolate by terminating the worker thread and spawning a fresh one.
|
|
92
|
+
* This interval balances memory usage against the cost of reloading grammars.
|
|
93
|
+
*/
|
|
94
|
+
const WORKER_RECYCLE_INTERVAL = 250;
|
|
95
|
+
/**
|
|
96
|
+
* Calculate SHA256 hash of file contents
|
|
97
|
+
*/
|
|
98
|
+
function hashContent(content) {
|
|
99
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Skip files larger than this (bytes). Generated bundles, minified JS, and
|
|
103
|
+
* vendored blobs blow the WASM heap and the worker-recycle budget for no useful
|
|
104
|
+
* symbols. 1 MB covers essentially all hand-written source.
|
|
105
|
+
*/
|
|
106
|
+
const MAX_FILE_SIZE = 1024 * 1024;
|
|
107
|
+
/**
|
|
108
|
+
* Directory names that are dependency, build, cache, or tooling output across the
|
|
109
|
+
* languages/frameworks CodeGraph supports — curated from the canonical
|
|
110
|
+
* github/gitignore templates. Excluded by default so the graph reflects your code,
|
|
111
|
+
* not third-party noise, without requiring a `.gitignore` (issue #407). The
|
|
112
|
+
* exclusion applies uniformly (git or not, tracked or not); the only opt-in is an
|
|
113
|
+
* explicit `.gitignore` negation (e.g. `!vendor/`). First-party-prone or generic
|
|
114
|
+
* names (`packages`, `lib`, `app`, `bin`, `src`, `deps`, `env`, `tmp`, `storage`,
|
|
115
|
+
* `Library`) are deliberately NOT listed, to avoid ever hiding real source.
|
|
116
|
+
*
|
|
117
|
+
* Only dirs that actually contain *indexable source* (or are enormous) earn a slot
|
|
118
|
+
* — IDE/state dirs like `.idea`/`.vs` are omitted because CodeGraph indexes only
|
|
119
|
+
* recognized source extensions, so they produce no symbols regardless.
|
|
120
|
+
*/
|
|
121
|
+
const DEFAULT_IGNORE_DIRS = new Set([
|
|
122
|
+
// JS / TS — dependency directories
|
|
123
|
+
'node_modules', 'bower_components', 'jspm_packages', 'web_modules',
|
|
124
|
+
'.yarn', '.pnpm-store',
|
|
125
|
+
// JS / TS — framework & bundler build / cache / deploy output
|
|
126
|
+
'.next', '.nuxt', '.svelte-kit', '.turbo', '.vite', '.parcel-cache', '.angular',
|
|
127
|
+
'.docusaurus', 'storybook-static', '.vinxi', '.nitro', 'out-tsc',
|
|
128
|
+
'.vercel', '.netlify', '.wrangler',
|
|
129
|
+
// Build output (common across ecosystems)
|
|
130
|
+
'dist', 'build', 'out', '.output',
|
|
131
|
+
// Test / coverage
|
|
132
|
+
'coverage', '.nyc_output',
|
|
133
|
+
// Python
|
|
134
|
+
'__pycache__', '__pypackages__', '.venv', 'venv', '.pixi', '.pdm-build',
|
|
135
|
+
'.mypy_cache', '.pytest_cache', '.ruff_cache', '.tox', '.nox', '.hypothesis',
|
|
136
|
+
'.ipynb_checkpoints', '.eggs',
|
|
137
|
+
// Rust / JVM (Maven, Gradle, Scala)
|
|
138
|
+
'target', '.gradle',
|
|
139
|
+
// .NET
|
|
140
|
+
'obj',
|
|
141
|
+
// Vendored deps (Go, PHP/Composer, Ruby/Bundler)
|
|
142
|
+
'vendor',
|
|
143
|
+
// Swift / iOS
|
|
144
|
+
'.build', 'Pods', 'Carthage', 'DerivedData', '.swiftpm',
|
|
145
|
+
// Dart / Flutter
|
|
146
|
+
'.dart_tool', '.pub-cache',
|
|
147
|
+
// Native (Android NDK, C/C++ deps)
|
|
148
|
+
'.cxx', '.externalNativeBuild', 'vcpkg_installed',
|
|
149
|
+
// Scala tooling
|
|
150
|
+
'.bloop', '.metals',
|
|
151
|
+
// Lua / Luau (LuaRocks)
|
|
152
|
+
'lua_modules', '.luarocks',
|
|
153
|
+
// Delphi / RAD Studio IDE backups (duplicate .pas source — would double-count)
|
|
154
|
+
'__history', '__recovery',
|
|
155
|
+
// Generic cache
|
|
156
|
+
'.cache',
|
|
157
|
+
]);
|
|
158
|
+
/** Gitignore-style patterns for the `ignore` matcher: the dirs above plus a few globs. */
|
|
159
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
160
|
+
...Array.from(DEFAULT_IGNORE_DIRS, (d) => `${d}/`),
|
|
161
|
+
'*.egg-info/', // Python packaging metadata
|
|
162
|
+
'cmake-build-*/', // CLion / CMake build trees
|
|
163
|
+
'bazel-*/', // Bazel output symlink trees
|
|
164
|
+
];
|
|
165
|
+
/** True if `buf` decodes as strict UTF-8 (no invalid byte sequences). */
|
|
166
|
+
function isValidUtf8(buf) {
|
|
167
|
+
try {
|
|
168
|
+
new TextDecoder('utf-8', { fatal: true }).decode(buf);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Read a `.gitignore` and return patterns safe to hand to the `ignore` matcher —
|
|
177
|
+
* never throwing, even when the file isn't real gitignore text. Two failure
|
|
178
|
+
* modes, both seen in the wild (issue #682):
|
|
179
|
+
*
|
|
180
|
+
* - The file isn't valid UTF-8 — e.g. transparently encrypted in place by
|
|
181
|
+
* corporate DLP / endpoint-security software, leaving a UTF-16 header plus
|
|
182
|
+
* ciphertext. None of it is meaningful patterns, so the whole file is skipped.
|
|
183
|
+
* - The file is text but a single line can't be compiled to a regex by the
|
|
184
|
+
* `ignore` library — `\\[` and friends throw "Unterminated character class".
|
|
185
|
+
* Crucially the throw is LAZY (at match time, not `.add()`), so it would
|
|
186
|
+
* otherwise escape mid-scan. That one pattern is dropped; the rest are kept.
|
|
187
|
+
*
|
|
188
|
+
* Either way a warning that NAMES the file is logged (the reporter couldn't tell
|
|
189
|
+
* which `.gitignore` was at fault) and indexing continues instead of aborting.
|
|
190
|
+
* Returns '' when there's nothing usable.
|
|
191
|
+
*/
|
|
192
|
+
function readGitignorePatterns(giPath) {
|
|
193
|
+
let buf;
|
|
194
|
+
try {
|
|
195
|
+
buf = fs.readFileSync(giPath);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return ''; // unreadable (permissions / race) — treat as absent
|
|
199
|
+
}
|
|
200
|
+
// A NUL byte never appears in real gitignore text, and a fatal UTF-8 decode
|
|
201
|
+
// catches the rest. Such a file isn't ignore patterns at all.
|
|
202
|
+
if (buf.includes(0) || !isValidUtf8(buf)) {
|
|
203
|
+
(0, errors_1.logWarn)('Ignoring a .gitignore that is not valid UTF-8 text — it may have been encrypted ' +
|
|
204
|
+
'in place by endpoint-security software. Indexing continues without it.', { file: giPath });
|
|
205
|
+
return '';
|
|
206
|
+
}
|
|
207
|
+
const content = buf.toString('utf-8');
|
|
208
|
+
// Fast path: one `.ignores()` call forces the library to compile EVERY rule,
|
|
209
|
+
// so if it doesn't throw, the whole file is safe to use verbatim.
|
|
210
|
+
try {
|
|
211
|
+
(0, ignore_1.default)().add(content).ignores('.cgraphx-probe');
|
|
212
|
+
return content;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Fall through: a line is uncompilable — keep the good ones, drop the bad.
|
|
216
|
+
}
|
|
217
|
+
const kept = [];
|
|
218
|
+
let dropped = 0;
|
|
219
|
+
for (const line of content.split(/\r?\n/)) {
|
|
220
|
+
try {
|
|
221
|
+
(0, ignore_1.default)().add(line).ignores('.cgraphx-probe');
|
|
222
|
+
kept.push(line);
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
dropped++;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (dropped > 0) {
|
|
229
|
+
(0, errors_1.logWarn)(`Skipped ${dropped} unparseable pattern(s) in a .gitignore; the rest are applied.`, { file: giPath });
|
|
230
|
+
}
|
|
231
|
+
return kept.join('\n');
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* An `ignore` matcher seeded with the built-in defaults, merged with the project's
|
|
235
|
+
* root .gitignore so a negation there (e.g. `!vendor/`) overrides a default. Shared
|
|
236
|
+
* by both enumeration paths so behavior is identical with or without git — and so
|
|
237
|
+
* the defaults apply to tracked files too (committing a dependency dir doesn't make
|
|
238
|
+
* it project code; the explicit `.gitignore` negation is the only opt-in).
|
|
239
|
+
*/
|
|
240
|
+
function buildDefaultIgnore(rootDir) {
|
|
241
|
+
const ig = (0, ignore_1.default)().add(DEFAULT_IGNORE_PATTERNS);
|
|
242
|
+
const rootGitignore = path.join(rootDir, '.gitignore');
|
|
243
|
+
if (fs.existsSync(rootGitignore))
|
|
244
|
+
ig.add(readGitignorePatterns(rootGitignore));
|
|
245
|
+
return ig;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Defaults-only ignore matcher (no root `.gitignore` merged). Used wherever the
|
|
249
|
+
* parent repo's own ignore rules must NOT apply — inside embedded child repos,
|
|
250
|
+
* whose gitignore semantics their own `git ls-files` already enforced (#514).
|
|
251
|
+
*/
|
|
252
|
+
function defaultsOnlyIgnore() {
|
|
253
|
+
return (0, ignore_1.default)().add(DEFAULT_IGNORE_PATTERNS);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Matcher for the project's `cgraphx.json` `includeIgnored` patterns — the
|
|
257
|
+
* explicit opt-in to index embedded git repos living inside gitignored
|
|
258
|
+
* directories (#622, #699). Returns `null` when the project opted in nothing,
|
|
259
|
+
* which is the zero-config DEFAULT: `.gitignore` is then fully respected and a
|
|
260
|
+
* gitignored directory (even one holding nested repos) is never walked or
|
|
261
|
+
* indexed (#970, #976). Built once per scan/sync/scope operation from the scan
|
|
262
|
+
* root and threaded down — never global, so multi-project daemons stay isolated.
|
|
263
|
+
*/
|
|
264
|
+
function loadIncludeIgnoredMatcher(rootDir) {
|
|
265
|
+
const patterns = (0, project_config_1.loadIncludeIgnoredPatterns)(rootDir);
|
|
266
|
+
return patterns.length > 0 ? (0, ignore_1.default)().add(patterns) : null;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* `git ls-files --directory` collapses a wholly-untracked/ignored directory into
|
|
270
|
+
* one entry — and when the command's own cwd is such a directory (the indexed
|
|
271
|
+
* root is itself a git-ignored subdir of an enclosing repo), git emits the
|
|
272
|
+
* literal `./` meaning "this entire directory". That sentinel is not a real
|
|
273
|
+
* nested path: feeding it to the `ignore` matcher throws ("path should be a
|
|
274
|
+
* `path.relative()`d string, but got "./""), which used to abort `buildScopeIgnore`
|
|
275
|
+
* and so break the MCP daemon's watcher/auto-sync on connect; and joining it back
|
|
276
|
+
* onto `repoDir` would just re-point at the cwd. Drop it wherever we consume
|
|
277
|
+
* `--directory` output. (#936)
|
|
278
|
+
*/
|
|
279
|
+
function isWholeCwdEntry(entry) {
|
|
280
|
+
return entry === './' || entry === '.' || entry === '';
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* List the gitignored DIRECTORIES of a repo (collapsed, trailing-slash form),
|
|
284
|
+
* relative to `repoDir`. These are invisible to every other `git ls-files` /
|
|
285
|
+
* `git status` mode — and in a multi-repo workspace they are exactly where the
|
|
286
|
+
* nested project repos live (a super-repo `.gitignore`s its child repos to keep
|
|
287
|
+
* `git status` quiet; that does not make them third-party code). (#514)
|
|
288
|
+
*/
|
|
289
|
+
function listIgnoredDirs(repoDir) {
|
|
290
|
+
try {
|
|
291
|
+
const out = (0, child_process_1.execFileSync)('git', ['ls-files', '-z', '-o', '-i', '--exclude-standard', '--directory'], { cwd: repoDir, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
292
|
+
return out.split('\0').filter((e) => e.endsWith('/') && !isWholeCwdEntry(e));
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/** Max directory depth searched below an ignored dir for nested `.git` roots. */
|
|
299
|
+
const EMBEDDED_REPO_SEARCH_DEPTH = 4;
|
|
300
|
+
/** Max directories examined per search — a huge ignored data dir must never stall a scan/sync. */
|
|
301
|
+
const EMBEDDED_REPO_SEARCH_ENTRIES = 2000;
|
|
302
|
+
/**
|
|
303
|
+
* Classify a directory's `.git` entry for embedded-repo discovery.
|
|
304
|
+
*
|
|
305
|
+
* - A `.git` **directory** is an embedded clone — distinct first-party code a
|
|
306
|
+
* super-repo merely hides from git; index it (#193, #514).
|
|
307
|
+
* - A `.git` **file** is a pointer (`gitdir: …`). A git **worktree** points into
|
|
308
|
+
* the host repo's own `.git/worktrees/<name>`, so it is a second working view
|
|
309
|
+
* of a repo CodeGraph already indexes — indexing it just duplicates the whole
|
|
310
|
+
* graph N times; skip it (#848). A **submodule worktree** points into
|
|
311
|
+
* `.git/modules/<module>/worktrees/<name>` — same duplication, so skip it too
|
|
312
|
+
* (#945). A **submodule** checkout points into `.git/modules/<module>` (no
|
|
313
|
+
* `worktrees/` segment) and is distinct code, so index it as before.
|
|
314
|
+
*
|
|
315
|
+
* Returns `'none'` when there is no `.git` entry here.
|
|
316
|
+
*/
|
|
317
|
+
function classifyGitDir(absDir) {
|
|
318
|
+
let st;
|
|
319
|
+
try {
|
|
320
|
+
st = fs.statSync(path.join(absDir, '.git'));
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return 'none';
|
|
324
|
+
}
|
|
325
|
+
if (st.isDirectory())
|
|
326
|
+
return 'embedded';
|
|
327
|
+
if (!st.isFile())
|
|
328
|
+
return 'none';
|
|
329
|
+
try {
|
|
330
|
+
const gitdir = fs.readFileSync(path.join(absDir, '.git'), 'utf8').match(/^gitdir:\s*(.+)$/m)?.[1]?.trim();
|
|
331
|
+
// A worktree's gitdir lives under some repo's `.git/worktrees/<name>` —
|
|
332
|
+
// either the top-level repo's (`.git/worktrees/`) or, for a worktree of a
|
|
333
|
+
// submodule, that submodule's gitdir (`.git/modules/<module>/worktrees/`).
|
|
334
|
+
// The optional `modules/<module>` segment covers the submodule case (#945).
|
|
335
|
+
// Match both separators so a Windows-style pointer is recognized too.
|
|
336
|
+
if (gitdir && /(^|[\\/])\.git[\\/](modules[\\/][^\\/]+[\\/])?worktrees[\\/]/.test(gitdir))
|
|
337
|
+
return 'worktree';
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// Unreadable `.git` pointer — fall back to the prior "index it" behavior.
|
|
341
|
+
}
|
|
342
|
+
return 'embedded';
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Find git repositories nested under `absDir` (inclusive), shallow bounded BFS.
|
|
346
|
+
* Stops descending at each repo root found — contents belong to that repo's own
|
|
347
|
+
* enumeration. Skips default-ignored dirs (`node_modules` can contain `.git`
|
|
348
|
+
* from npm git-dependencies — that never makes it project code) and CodeGraph
|
|
349
|
+
* data dirs. Depth- and entry-capped so a huge ignored tree can't stall the scan.
|
|
350
|
+
*/
|
|
351
|
+
function findNestedGitRepos(absDir, relPrefix) {
|
|
352
|
+
const found = [];
|
|
353
|
+
const defaults = defaultsOnlyIgnore();
|
|
354
|
+
const queue = [
|
|
355
|
+
{ abs: absDir, rel: relPrefix, depth: 0 },
|
|
356
|
+
];
|
|
357
|
+
let examined = 0;
|
|
358
|
+
while (queue.length > 0) {
|
|
359
|
+
const { abs, rel, depth } = queue.shift();
|
|
360
|
+
if (++examined > EMBEDDED_REPO_SEARCH_ENTRIES) {
|
|
361
|
+
(0, errors_1.logDebug)('Embedded-repo search entry cap hit — deeper repos (if any) not discovered', { under: relPrefix });
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
const cls = classifyGitDir(abs);
|
|
365
|
+
if (cls === 'worktree') {
|
|
366
|
+
continue; // a git worktree duplicates an already-indexed repo (#848) — skip
|
|
367
|
+
}
|
|
368
|
+
if (cls === 'embedded') {
|
|
369
|
+
found.push(rel);
|
|
370
|
+
continue; // its own git handles everything below
|
|
371
|
+
}
|
|
372
|
+
if (depth >= EMBEDDED_REPO_SEARCH_DEPTH)
|
|
373
|
+
continue;
|
|
374
|
+
let entries;
|
|
375
|
+
try {
|
|
376
|
+
entries = fs.readdirSync(abs, { withFileTypes: true });
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
for (const entry of entries) {
|
|
382
|
+
if (!entry.isDirectory())
|
|
383
|
+
continue;
|
|
384
|
+
if (entry.name === '.git' || (0, directory_1.isCodeGraphDataDir)(entry.name))
|
|
385
|
+
continue;
|
|
386
|
+
const childRel = rel + entry.name + '/';
|
|
387
|
+
if (defaults.ignores(childRel))
|
|
388
|
+
continue;
|
|
389
|
+
queue.push({ abs: path.join(abs, entry.name), rel: childRel, depth: depth + 1 });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return found;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Workspace-scope ignore matcher. Ordinary paths get the root's matcher
|
|
396
|
+
* (built-in defaults + root `.gitignore`); paths inside an EMBEDDED repo get
|
|
397
|
+
* that repo's own matcher (defaults + its root `.gitignore`) — the parent's
|
|
398
|
+
* `.gitignore` hides a child repo from git, not from the index (#514). A
|
|
399
|
+
* directory path (trailing slash) that is an ANCESTOR of an embedded root is
|
|
400
|
+
* never ignored, so directory-pruning callers (the Linux per-directory
|
|
401
|
+
* watcher) still descend to reach the embedded repos.
|
|
402
|
+
*
|
|
403
|
+
* Single source of truth for indexer and watcher scope — they must not diverge.
|
|
404
|
+
*/
|
|
405
|
+
class ScopeIgnore {
|
|
406
|
+
rootMatcher;
|
|
407
|
+
embedded;
|
|
408
|
+
defaults = defaultsOnlyIgnore();
|
|
409
|
+
constructor(rootMatcher, embedded) {
|
|
410
|
+
this.rootMatcher = rootMatcher;
|
|
411
|
+
// Longest root first so paths in nested embedded repos hit the innermost matcher.
|
|
412
|
+
this.embedded = [...embedded].sort((a, b) => b.root.length - a.root.length);
|
|
413
|
+
}
|
|
414
|
+
ignores(rel) {
|
|
415
|
+
for (const { root, matcher } of this.embedded) {
|
|
416
|
+
if (rel.startsWith(root)) {
|
|
417
|
+
const inner = rel.slice(root.length);
|
|
418
|
+
if (inner === '')
|
|
419
|
+
return false;
|
|
420
|
+
// Built-in defaults apply to the FULL path uniformly (#407) — an
|
|
421
|
+
// embedded repo inside node_modules (an npm git-dependency) must stay
|
|
422
|
+
// excluded even though its own rules wouldn't ignore its files.
|
|
423
|
+
return this.defaults.ignores(rel) || matcher.ignores(inner);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Never prune a directory that leads to an embedded repo.
|
|
427
|
+
if (rel.endsWith('/') && this.embedded.some(({ root }) => root.startsWith(rel))) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
return this.rootMatcher.ignores(rel);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
exports.ScopeIgnore = ScopeIgnore;
|
|
434
|
+
/**
|
|
435
|
+
* Build the workspace-scope matcher. When the caller already knows the
|
|
436
|
+
* embedded roots (the scanner discovers them during collection), pass them to
|
|
437
|
+
* skip rediscovery; otherwise they're discovered here (the watcher path).
|
|
438
|
+
*/
|
|
439
|
+
function buildScopeIgnore(rootDir, embeddedRoots) {
|
|
440
|
+
const roots = embeddedRoots ? [...embeddedRoots] : discoverEmbeddedRepoRoots(rootDir);
|
|
441
|
+
return new ScopeIgnore(buildDefaultIgnore(rootDir), roots.map((root) => ({ root, matcher: buildDefaultIgnore(path.join(rootDir, root)) })));
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Standalone discovery of every embedded repo root under `rootDir` (relative,
|
|
445
|
+
* trailing-slashed) — the untracked kind (#193) always, and the gitignored kind
|
|
446
|
+
* (#514) only for directories the project opted in via `cgraphx.json`
|
|
447
|
+
* `includeIgnored` (#622, #699); otherwise `.gitignore` is respected and they
|
|
448
|
+
* are not discovered (#970, #976). Recursive (an embedded repo can embed further
|
|
449
|
+
* repos). Returns [] for non-git roots: the filesystem walk handles nested repos
|
|
450
|
+
* there already.
|
|
451
|
+
*/
|
|
452
|
+
function discoverEmbeddedRepoRoots(rootDir) {
|
|
453
|
+
try {
|
|
454
|
+
(0, child_process_1.execFileSync)('git', ['rev-parse', '--git-dir'], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
return [];
|
|
458
|
+
}
|
|
459
|
+
const out = [];
|
|
460
|
+
const defaults = defaultsOnlyIgnore();
|
|
461
|
+
const includeIgnored = loadIncludeIgnoredMatcher(rootDir);
|
|
462
|
+
const visit = (repoAbs, prefix) => {
|
|
463
|
+
const candidates = [];
|
|
464
|
+
try {
|
|
465
|
+
const o = (0, child_process_1.execFileSync)('git', ['ls-files', '-z', '-o', '--exclude-standard', '--directory'], { cwd: repoAbs, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
466
|
+
for (const e of o.split('\0')) {
|
|
467
|
+
if (e.endsWith('/') && !isWholeCwdEntry(e) && !defaults.ignores(e)) {
|
|
468
|
+
candidates.push(...findNestedGitRepos(path.join(repoAbs, e), e));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch { /* untracked listing failed — ignored-side discovery still runs */ }
|
|
473
|
+
candidates.push(...findIgnoredEmbeddedRepos(repoAbs, includeIgnored, prefix));
|
|
474
|
+
for (const rel of candidates) {
|
|
475
|
+
const full = (0, utils_1.normalizePath)(prefix + rel);
|
|
476
|
+
out.push(full);
|
|
477
|
+
visit(path.join(repoAbs, rel), full);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
visit(rootDir, '');
|
|
481
|
+
return out;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Discover embedded repos hidden by `repoDir`'s OWN gitignore rules: for each
|
|
485
|
+
* gitignored directory, search for nested `.git` roots. Returns repo paths
|
|
486
|
+
* relative to `repoDir`, trailing-slashed.
|
|
487
|
+
*
|
|
488
|
+
* OPT-IN ONLY. Walking into a gitignored directory contradicts what every other
|
|
489
|
+
* tool (and CodeGraph's own `git ls-files` foundation) does — `.gitignore`
|
|
490
|
+
* excludes. So this returns `[]` unless the project opted the directory in via
|
|
491
|
+
* `cgraphx.json` `includeIgnored`; without that, a gitignored dir — including
|
|
492
|
+
* a huge reference/data dir full of nested clones — is left untouched (#970,
|
|
493
|
+
* #976). When opted in, it restores the super-repo-of-clones behavior (#622,
|
|
494
|
+
* #699). `prefix` is the scan-root-relative path of `repoDir`, so a pattern like
|
|
495
|
+
* `services/` opts that whole subtree in at any recursion depth. Built-in
|
|
496
|
+
* default excludes (`node_modules`, …) are always skipped.
|
|
497
|
+
*/
|
|
498
|
+
function findIgnoredEmbeddedRepos(repoDir, includeIgnored, prefix) {
|
|
499
|
+
if (!includeIgnored)
|
|
500
|
+
return [];
|
|
501
|
+
const defaults = defaultsOnlyIgnore();
|
|
502
|
+
const repos = [];
|
|
503
|
+
for (const dir of listIgnoredDirs(repoDir)) {
|
|
504
|
+
if (defaults.ignores(dir))
|
|
505
|
+
continue;
|
|
506
|
+
if (!includeIgnored.ignores((0, utils_1.normalizePath)(prefix + dir)))
|
|
507
|
+
continue;
|
|
508
|
+
repos.push(...findNestedGitRepos(path.join(repoDir, dir), dir));
|
|
509
|
+
}
|
|
510
|
+
return repos;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Collect git-visible files (tracked + untracked, .gitignore-respected) from the
|
|
514
|
+
* git repository rooted at `repoDir`, adding each to `files` with `prefix`
|
|
515
|
+
* prepended so paths stay relative to the original scan root.
|
|
516
|
+
*
|
|
517
|
+
* Recurses into embedded git repositories — nested repos that are NOT submodules
|
|
518
|
+
* (independent clones living inside the workspace, common in CMake "super-repo"
|
|
519
|
+
* layouts). The parent repo's `git ls-files` cannot see into them: tracked output
|
|
520
|
+
* skips them entirely, and untracked output reports them only as an opaque
|
|
521
|
+
* "subdir/" entry (trailing slash) rather than expanding their files. Each
|
|
522
|
+
* embedded repo is its own git boundary, so we re-run `git ls-files` inside it.
|
|
523
|
+
* (See issue #193.) GITIGNORED embedded repos are invisible even to that; they
|
|
524
|
+
* are discovered separately via `findIgnoredEmbeddedRepos` (#514) but ONLY for
|
|
525
|
+
* directories the project opted in through `cgraphx.json` `includeIgnored`
|
|
526
|
+
* (`includeIgnored` here, threaded from the scan root) — by default `.gitignore`
|
|
527
|
+
* is respected and they stay out (#970, #976). Every embedded repo root (however
|
|
528
|
+
* found) is recorded in `embeddedRoots` so callers can exempt its files from the
|
|
529
|
+
* parent's own gitignore rules.
|
|
530
|
+
*/
|
|
531
|
+
function collectGitFiles(repoDir, prefix, files, embeddedRoots, includeIgnored = null) {
|
|
532
|
+
const gitOpts = { cwd: repoDir, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true };
|
|
533
|
+
// Tracked files. --recurse-submodules pulls in files from active submodules,
|
|
534
|
+
// which the index would otherwise represent only as a commit pointer.
|
|
535
|
+
// Without this, monorepos using submodules index 0 files. (See issue #147.)
|
|
536
|
+
// Note: --recurse-submodules only supports -c/--cached and --stage modes — it
|
|
537
|
+
// can't be combined with -o, so untracked files are gathered separately below.
|
|
538
|
+
// -z gives NUL-separated, unquoted output so non-ASCII (e.g. CJK) paths
|
|
539
|
+
// survive verbatim. Without it git octal-escapes and double-quotes such paths
|
|
540
|
+
// (the core.quotepath default), and the quoted form never matches a real file
|
|
541
|
+
// on disk → those files are silently dropped from the index. (#541)
|
|
542
|
+
const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-z', '-c', '--recurse-submodules'], gitOpts);
|
|
543
|
+
for (const rel of tracked.split('\0')) {
|
|
544
|
+
if (rel)
|
|
545
|
+
files.add((0, utils_1.normalizePath)(prefix + rel));
|
|
546
|
+
}
|
|
547
|
+
// Untracked files (submodules manage their own untracked state). Embedded git
|
|
548
|
+
// repos surface here as a single "subdir/" entry that git refuses to descend
|
|
549
|
+
// into — recurse into those as their own repos so their source gets indexed.
|
|
550
|
+
const untracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-z', '-o', '--exclude-standard'], gitOpts);
|
|
551
|
+
for (const rel of untracked.split('\0')) {
|
|
552
|
+
if (!rel)
|
|
553
|
+
continue;
|
|
554
|
+
if (rel.endsWith('/')) {
|
|
555
|
+
// git only emits a trailing-slash directory entry for an embedded repo.
|
|
556
|
+
// Guard with a .git check anyway, and skip anything else exactly as git
|
|
557
|
+
// itself skips it (we never descend into a non-repo opaque dir). Never
|
|
558
|
+
// descend into default-ignored locations — an embedded repo inside
|
|
559
|
+
// node_modules is an npm git-dependency, not project code.
|
|
560
|
+
const childDir = path.join(repoDir, rel);
|
|
561
|
+
// A git worktree surfaces here as an opaque untracked dir too — skip it,
|
|
562
|
+
// it's a duplicate working view of an already-indexed repo (#848).
|
|
563
|
+
if (classifyGitDir(childDir) === 'embedded' && !defaultsOnlyIgnore().ignores(rel)) {
|
|
564
|
+
embeddedRoots?.add((0, utils_1.normalizePath)(prefix + rel));
|
|
565
|
+
collectGitFiles(childDir, prefix + rel, files, embeddedRoots, includeIgnored);
|
|
566
|
+
}
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
files.add((0, utils_1.normalizePath)(prefix + rel));
|
|
570
|
+
}
|
|
571
|
+
// Embedded repos hidden by THIS repo's ignore rules (`/packages/` in a
|
|
572
|
+
// super-repo .gitignore) never appear in any listing above. By default they
|
|
573
|
+
// stay hidden — `.gitignore` is respected (#970, #976). They are recursed into
|
|
574
|
+
// only when the project opted the directory in via `cgraphx.json`
|
|
575
|
+
// `includeIgnored` (#622, #699), which `findIgnoredEmbeddedRepos` enforces.
|
|
576
|
+
for (const rel of findIgnoredEmbeddedRepos(repoDir, includeIgnored, prefix)) {
|
|
577
|
+
embeddedRoots?.add((0, utils_1.normalizePath)(prefix + rel));
|
|
578
|
+
collectGitFiles(path.join(repoDir, rel), prefix + rel, files, embeddedRoots, includeIgnored);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Get all files visible to git (tracked + untracked but not ignored).
|
|
583
|
+
* Respects .gitignore at all levels (root, subdirectories) and descends into
|
|
584
|
+
* embedded (nested, non-submodule) git repos. Returns null on failure
|
|
585
|
+
* (non-git project) so callers can fall back to a filesystem walk.
|
|
586
|
+
*/
|
|
587
|
+
function getGitVisibleFiles(rootDir) {
|
|
588
|
+
try {
|
|
589
|
+
// Check if the project directory is gitignored by a parent repo.
|
|
590
|
+
// When rootDir lives inside a parent git repo that ignores it,
|
|
591
|
+
// `git ls-files` returns nothing — fall back to filesystem walk.
|
|
592
|
+
const gitRoot = (0, child_process_1.execFileSync)('git', ['rev-parse', '--show-toplevel'], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }).trim();
|
|
593
|
+
if (path.resolve(gitRoot) !== path.resolve(rootDir)) {
|
|
594
|
+
try {
|
|
595
|
+
// git check-ignore exits 0 if the path IS ignored, 1 if not
|
|
596
|
+
(0, child_process_1.execFileSync)('git', ['check-ignore', '-q', path.resolve(rootDir)], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
597
|
+
// Directory is gitignored by parent repo — fall back to filesystem walk
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
catch {
|
|
601
|
+
// Not ignored — safe to use git ls-files
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const files = new Set();
|
|
605
|
+
const embeddedRoots = new Set();
|
|
606
|
+
collectGitFiles(rootDir, '', files, embeddedRoots, loadIncludeIgnoredMatcher(rootDir));
|
|
607
|
+
// Apply built-in default ignores uniformly — to tracked files too, since
|
|
608
|
+
// committing a dependency/build dir doesn't make it project code. A
|
|
609
|
+
// `.gitignore` negation (e.g. `!vendor/`) is the explicit opt-in. (issue #407)
|
|
610
|
+
// Files inside an EMBEDDED repo are matched against that repo's own rules,
|
|
611
|
+
// not the parent's: the parent's .gitignore hides the child repo from git,
|
|
612
|
+
// not from the index. (#514)
|
|
613
|
+
const ig = buildScopeIgnore(rootDir, embeddedRoots);
|
|
614
|
+
return new Set([...files].filter((f) => !ig.ignores(f)));
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Use `git status` to detect changed files instead of scanning every file.
|
|
622
|
+
* Returns null on failure so callers fall back to full scan.
|
|
623
|
+
*
|
|
624
|
+
* Recurses into embedded repos — the untracked kind (#193: the parent's status
|
|
625
|
+
* collapses them to an opaque `?? subdir/` entry) always, and the gitignored
|
|
626
|
+
* kind (#514: they never appear in the parent's status at all) only for
|
|
627
|
+
* directories opted in via `cgraphx.json` `includeIgnored` (#622, #699) —
|
|
628
|
+
* running `git status` inside each, so changes in a multi-repo workspace sync
|
|
629
|
+
* without a full rescan. By default a gitignored dir is left alone, matching the
|
|
630
|
+
* full-index scan (#970, #976). Deleting an ENTIRE embedded repo dir is the one
|
|
631
|
+
* case this cannot see (the child status that would report the deletions is gone
|
|
632
|
+
* with it); a full `cgraphx index` reconciles that.
|
|
633
|
+
*/
|
|
634
|
+
function getGitChangedFiles(rootDir) {
|
|
635
|
+
try {
|
|
636
|
+
const changes = { modified: [], added: [], deleted: [] };
|
|
637
|
+
// Custom extension → language overrides from the project's cgraphx.json,
|
|
638
|
+
// so change detection sees the same custom-extension files the full index does.
|
|
639
|
+
const overrides = (0, project_config_1.loadExtensionOverrides)(rootDir);
|
|
640
|
+
collectGitStatus(rootDir, '', changes, overrides, loadIncludeIgnoredMatcher(rootDir));
|
|
641
|
+
return changes;
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function collectGitStatus(repoDir, prefix, out, overrides, includeIgnored = null) {
|
|
648
|
+
const output = (0, child_process_1.execFileSync)('git', ['status', '--porcelain', '--no-renames'], { cwd: repoDir, encoding: 'utf-8', timeout: 10000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
649
|
+
// This repo's own ignore rules — built-in defaults (#407) plus its .gitignore.
|
|
650
|
+
// Change detection must exclude the SAME files the full index does, but git
|
|
651
|
+
// status hides neither: it ignores nothing for *tracked* paths, and the
|
|
652
|
+
// built-in defaults aren't gitignore at all. Without this filter a committed
|
|
653
|
+
// vendor/ dir, or a tracked file under a .gitignored dir, surfaces here as a
|
|
654
|
+
// change — so `cgraphx status` (which reads getChangedFiles) reports a
|
|
655
|
+
// pending edit the full index never tracks and `sync` never clears. Matching
|
|
656
|
+
// repo-relative `rel` at each recursion level mirrors getGitVisibleFiles'
|
|
657
|
+
// ScopeIgnore: every embedded repo is judged by ITS OWN rules, never the
|
|
658
|
+
// parent's. (#766)
|
|
659
|
+
const ig = buildDefaultIgnore(repoDir);
|
|
660
|
+
const untrackedDirs = [];
|
|
661
|
+
for (const line of output.split('\n')) {
|
|
662
|
+
if (line.length < 4)
|
|
663
|
+
continue; // Minimum: "XY file"
|
|
664
|
+
const statusCode = line.substring(0, 2);
|
|
665
|
+
const rel = (0, utils_1.normalizePath)(line.substring(3));
|
|
666
|
+
// Untracked directory entries (trailing slash) may hide an embedded repo —
|
|
667
|
+
// collect for the recursion below instead of treating as a file.
|
|
668
|
+
if (statusCode === '??' && rel.endsWith('/')) {
|
|
669
|
+
untrackedDirs.push(rel);
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
const filePath = (0, utils_1.normalizePath)(prefix + rel);
|
|
673
|
+
if (!(0, grammars_1.isSourceFile)(filePath, overrides))
|
|
674
|
+
continue;
|
|
675
|
+
if (statusCode.includes('D')) {
|
|
676
|
+
// Deletions stay unfiltered: getChangedFiles acts on one only when the
|
|
677
|
+
// path is already tracked in the DB, where removal is always correct — and
|
|
678
|
+
// that lets a newly-excluded dir's stale rows clean themselves up. (#766)
|
|
679
|
+
out.deleted.push(filePath);
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
// Added (`??`) / modified files inside an excluded dir must not enter the
|
|
683
|
+
// index — match against the repo-relative path, same as the full scan. (#766)
|
|
684
|
+
if (ig.ignores(rel))
|
|
685
|
+
continue;
|
|
686
|
+
if (statusCode === '??') {
|
|
687
|
+
out.added.push(filePath);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
// M, MM, AM, A (staged), etc. — treat as modified
|
|
691
|
+
out.modified.push(filePath);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// Recurse embedded repos found under untracked dirs (at the dir itself or
|
|
695
|
+
// nested deeper). Gitignored dirs are walked only for the directories the
|
|
696
|
+
// project opted in via `includeIgnored`; by default `.gitignore` is respected
|
|
697
|
+
// and they are left alone (#970, #976), mirroring the full-index scan.
|
|
698
|
+
for (const rel of untrackedDirs) {
|
|
699
|
+
for (const repoRel of findNestedGitRepos(path.join(repoDir, rel), rel)) {
|
|
700
|
+
collectGitStatus(path.join(repoDir, repoRel), prefix + repoRel, out, overrides, includeIgnored);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
for (const rel of findIgnoredEmbeddedRepos(repoDir, includeIgnored, prefix)) {
|
|
704
|
+
collectGitStatus(path.join(repoDir, rel), prefix + rel, out, overrides, includeIgnored);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Recursively scan a directory for source files.
|
|
709
|
+
*
|
|
710
|
+
* In git repos, uses `git ls-files` (inherently respects .gitignore at all
|
|
711
|
+
* levels), then keeps files with a supported source extension. For non-git
|
|
712
|
+
* projects, falls back to a filesystem walk that parses .gitignore itself.
|
|
713
|
+
*/
|
|
714
|
+
function scanDirectory(rootDir, onProgress) {
|
|
715
|
+
// Custom extension → language overrides from the project's cgraphx.json.
|
|
716
|
+
const overrides = (0, project_config_1.loadExtensionOverrides)(rootDir);
|
|
717
|
+
// Fast path: use git to get all visible files (respects .gitignore everywhere)
|
|
718
|
+
const gitFiles = getGitVisibleFiles(rootDir);
|
|
719
|
+
if (gitFiles) {
|
|
720
|
+
const files = [];
|
|
721
|
+
let count = 0;
|
|
722
|
+
for (const filePath of gitFiles) {
|
|
723
|
+
if ((0, grammars_1.isSourceFile)(filePath, overrides)) {
|
|
724
|
+
files.push(filePath);
|
|
725
|
+
count++;
|
|
726
|
+
onProgress?.(count, filePath);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return files;
|
|
730
|
+
}
|
|
731
|
+
// Fallback: walk filesystem for non-git projects
|
|
732
|
+
return scanDirectoryWalk(rootDir, onProgress);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Async variant of scanDirectory that yields to the event loop periodically,
|
|
736
|
+
* allowing worker threads to receive and render progress messages.
|
|
737
|
+
*/
|
|
738
|
+
async function scanDirectoryAsync(rootDir, onProgress) {
|
|
739
|
+
// Custom extension → language overrides from the project's cgraphx.json.
|
|
740
|
+
const overrides = (0, project_config_1.loadExtensionOverrides)(rootDir);
|
|
741
|
+
const gitFiles = getGitVisibleFiles(rootDir);
|
|
742
|
+
if (gitFiles) {
|
|
743
|
+
const files = [];
|
|
744
|
+
let count = 0;
|
|
745
|
+
for (const filePath of gitFiles) {
|
|
746
|
+
if ((0, grammars_1.isSourceFile)(filePath, overrides)) {
|
|
747
|
+
files.push(filePath);
|
|
748
|
+
count++;
|
|
749
|
+
onProgress?.(count, filePath);
|
|
750
|
+
// Yield every 100 files so worker threads can render progress
|
|
751
|
+
if (count % 100 === 0) {
|
|
752
|
+
await new Promise(r => setImmediate(r));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return files;
|
|
757
|
+
}
|
|
758
|
+
return scanDirectoryWalk(rootDir, onProgress);
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Filesystem walk fallback for non-git projects.
|
|
762
|
+
*/
|
|
763
|
+
function scanDirectoryWalk(rootDir, onProgress) {
|
|
764
|
+
const files = [];
|
|
765
|
+
let count = 0;
|
|
766
|
+
const visitedDirs = new Set();
|
|
767
|
+
// Custom extension → language overrides from the project's cgraphx.json.
|
|
768
|
+
const overrides = (0, project_config_1.loadExtensionOverrides)(rootDir);
|
|
769
|
+
const loadIgnore = (dir) => {
|
|
770
|
+
const giPath = path.join(dir, '.gitignore');
|
|
771
|
+
if (!fs.existsSync(giPath))
|
|
772
|
+
return null;
|
|
773
|
+
// readGitignorePatterns is defensive: a non-UTF-8 (DLP-encrypted) or
|
|
774
|
+
// uncompilable .gitignore is skipped/filtered with a warning, never thrown
|
|
775
|
+
// (issue #682) — so the per-file `.ignores()` calls below can't crash.
|
|
776
|
+
const patterns = readGitignorePatterns(giPath);
|
|
777
|
+
return patterns ? { dir, ig: (0, ignore_1.default)().add(patterns) } : null;
|
|
778
|
+
};
|
|
779
|
+
const isIgnored = (fullPath, isDir, matchers) => {
|
|
780
|
+
for (const { dir, ig } of matchers) {
|
|
781
|
+
let rel = (0, utils_1.normalizePath)(path.relative(dir, fullPath));
|
|
782
|
+
if (!rel || rel.startsWith('..'))
|
|
783
|
+
continue; // not under this matcher's dir
|
|
784
|
+
if (isDir)
|
|
785
|
+
rel += '/'; // dir-only rules (e.g. `build/`) only match with the slash
|
|
786
|
+
if (ig.ignores(rel))
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
return false;
|
|
790
|
+
};
|
|
791
|
+
function walk(dir, matchers) {
|
|
792
|
+
let realDir;
|
|
793
|
+
try {
|
|
794
|
+
realDir = fs.realpathSync(dir);
|
|
795
|
+
}
|
|
796
|
+
catch {
|
|
797
|
+
(0, errors_1.logDebug)('Skipping unresolvable directory', { dir });
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (visitedDirs.has(realDir)) {
|
|
801
|
+
(0, errors_1.logDebug)('Skipping already-visited directory (symlink cycle)', { dir, realDir });
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
visitedDirs.add(realDir);
|
|
805
|
+
// This directory's own .gitignore (if present) applies to everything below it.
|
|
806
|
+
// The root's .gitignore is already merged into the seeded base matcher (so a
|
|
807
|
+
// negation there can override a built-in default), so skip it here.
|
|
808
|
+
const own = dir === rootDir ? null : loadIgnore(dir);
|
|
809
|
+
const active = own ? [...matchers, own] : matchers;
|
|
810
|
+
let entries;
|
|
811
|
+
try {
|
|
812
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
(0, errors_1.logDebug)('Skipping unreadable directory', { dir, error: String(error) });
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
for (const entry of entries) {
|
|
819
|
+
// Never descend into git internals or any CodeGraph data directory
|
|
820
|
+
// (the active one or a sibling another environment created — #636).
|
|
821
|
+
if (entry.name === '.git' || (0, directory_1.isCodeGraphDataDir)(entry.name))
|
|
822
|
+
continue;
|
|
823
|
+
const fullPath = path.join(dir, entry.name);
|
|
824
|
+
const relativePath = (0, utils_1.normalizePath)(path.relative(rootDir, fullPath));
|
|
825
|
+
if (entry.isSymbolicLink()) {
|
|
826
|
+
try {
|
|
827
|
+
const realTarget = fs.realpathSync(fullPath);
|
|
828
|
+
const stat = fs.statSync(realTarget);
|
|
829
|
+
if (stat.isDirectory()) {
|
|
830
|
+
if (!isIgnored(fullPath, true, active)) {
|
|
831
|
+
walk(fullPath, active);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
else if (stat.isFile()) {
|
|
835
|
+
if (!isIgnored(fullPath, false, active) && (0, grammars_1.isSourceFile)(relativePath, overrides)) {
|
|
836
|
+
files.push(relativePath);
|
|
837
|
+
count++;
|
|
838
|
+
onProgress?.(count, relativePath);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
(0, errors_1.logDebug)('Skipping broken symlink', { path: fullPath });
|
|
844
|
+
}
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
if (entry.isDirectory()) {
|
|
848
|
+
if (!isIgnored(fullPath, true, active)) {
|
|
849
|
+
walk(fullPath, active);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
else if (entry.isFile()) {
|
|
853
|
+
if (!isIgnored(fullPath, false, active) && (0, grammars_1.isSourceFile)(relativePath, overrides)) {
|
|
854
|
+
files.push(relativePath);
|
|
855
|
+
count++;
|
|
856
|
+
onProgress?.(count, relativePath);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
// Seed a base matcher with the built-in default ignores (merged with the root
|
|
862
|
+
// .gitignore so a negation can override). Nested .gitignores still layer per-dir.
|
|
863
|
+
walk(rootDir, [{ dir: rootDir, ig: buildDefaultIgnore(rootDir) }]);
|
|
864
|
+
return files;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Extraction orchestrator
|
|
868
|
+
*/
|
|
869
|
+
class ExtractionOrchestrator {
|
|
870
|
+
rootDir;
|
|
871
|
+
queries;
|
|
872
|
+
/**
|
|
873
|
+
* Names of frameworks detected for this project, populated by indexAll().
|
|
874
|
+
* Passed to extractFromSource so framework-specific extractors (route nodes,
|
|
875
|
+
* middleware, etc.) run after the tree-sitter pass. Cleared if detection
|
|
876
|
+
* hasn't run yet so single-file re-index paths can detect on the spot.
|
|
877
|
+
*/
|
|
878
|
+
detectedFrameworkNames = null;
|
|
879
|
+
constructor(rootDir, queries) {
|
|
880
|
+
this.rootDir = rootDir;
|
|
881
|
+
this.queries = queries;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Build a filesystem-backed ResolutionContext sufficient for framework
|
|
885
|
+
* detection. Graph-query methods (getNodesByName etc.) return empty because
|
|
886
|
+
* the DB hasn't been populated yet, but detect() only uses readFile,
|
|
887
|
+
* fileExists, and getAllFiles, so that's fine.
|
|
888
|
+
*/
|
|
889
|
+
buildDetectionContext(files) {
|
|
890
|
+
const rootDir = this.rootDir;
|
|
891
|
+
return {
|
|
892
|
+
getNodesInFile: () => [],
|
|
893
|
+
getNodesByName: () => [],
|
|
894
|
+
getNodesByQualifiedName: () => [],
|
|
895
|
+
getNodesByKind: () => [],
|
|
896
|
+
getNodesByLowerName: () => [],
|
|
897
|
+
getImportMappings: () => [],
|
|
898
|
+
getAllFiles: () => files,
|
|
899
|
+
getProjectRoot: () => rootDir,
|
|
900
|
+
fileExists: (relativePath) => {
|
|
901
|
+
const full = (0, utils_1.validatePathWithinRoot)(rootDir, relativePath);
|
|
902
|
+
if (!full)
|
|
903
|
+
return false;
|
|
904
|
+
try {
|
|
905
|
+
return fs.existsSync(full);
|
|
906
|
+
}
|
|
907
|
+
catch {
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
readFile: (relativePath) => {
|
|
912
|
+
const full = (0, utils_1.validatePathWithinRoot)(rootDir, relativePath);
|
|
913
|
+
if (!full)
|
|
914
|
+
return null;
|
|
915
|
+
try {
|
|
916
|
+
return fs.readFileSync(full, 'utf-8');
|
|
917
|
+
}
|
|
918
|
+
catch {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
// Monorepo support — needed by framework detect()s that probe
|
|
923
|
+
// subpackage manifests (e.g. fabric-view looking at
|
|
924
|
+
// packages/<sub>/package.json when the root manifest is just a
|
|
925
|
+
// workspace declaration). Matches the resolver-context shape.
|
|
926
|
+
listDirectories: (relativePath) => {
|
|
927
|
+
const target = relativePath === '.' || relativePath === ''
|
|
928
|
+
? rootDir
|
|
929
|
+
: path.join(rootDir, relativePath);
|
|
930
|
+
try {
|
|
931
|
+
return fs
|
|
932
|
+
.readdirSync(target, { withFileTypes: true })
|
|
933
|
+
.filter((entry) => entry.isDirectory())
|
|
934
|
+
.map((entry) => entry.name);
|
|
935
|
+
}
|
|
936
|
+
catch {
|
|
937
|
+
return [];
|
|
938
|
+
}
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Detect frameworks on demand using the current scanned files (or a fresh
|
|
944
|
+
* scan if none are provided). Cached on the orchestrator so repeat calls
|
|
945
|
+
* inside a single run don't re-scan.
|
|
946
|
+
*/
|
|
947
|
+
ensureDetectedFrameworks(files) {
|
|
948
|
+
if (this.detectedFrameworkNames !== null)
|
|
949
|
+
return this.detectedFrameworkNames;
|
|
950
|
+
const fileList = files ?? scanDirectory(this.rootDir);
|
|
951
|
+
const context = this.buildDetectionContext(fileList);
|
|
952
|
+
this.detectedFrameworkNames = (0, frameworks_1.detectFrameworks)(context).map((r) => r.name);
|
|
953
|
+
return this.detectedFrameworkNames;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Index all files in the project
|
|
957
|
+
*/
|
|
958
|
+
async indexAll(onProgress, signal, verbose) {
|
|
959
|
+
await (0, grammars_1.initGrammars)();
|
|
960
|
+
const startTime = Date.now();
|
|
961
|
+
const errors = [];
|
|
962
|
+
let filesIndexed = 0;
|
|
963
|
+
let filesSkipped = 0;
|
|
964
|
+
let filesErrored = 0;
|
|
965
|
+
let totalNodes = 0;
|
|
966
|
+
let totalEdges = 0;
|
|
967
|
+
// Custom extension → language overrides from the project's cgraphx.json.
|
|
968
|
+
// Threaded into language detection so custom-extension files load the right
|
|
969
|
+
// grammar and store under the mapped language.
|
|
970
|
+
const overrides = (0, project_config_1.loadExtensionOverrides)(this.rootDir);
|
|
971
|
+
const log = verbose
|
|
972
|
+
? (msg) => { console.log(`[worker] ${msg}`); }
|
|
973
|
+
: (_msg) => { };
|
|
974
|
+
// Phase 1: Scan for files
|
|
975
|
+
onProgress?.({
|
|
976
|
+
phase: 'scanning',
|
|
977
|
+
current: 0,
|
|
978
|
+
total: 0,
|
|
979
|
+
});
|
|
980
|
+
const files = await scanDirectoryAsync(this.rootDir, (current, file) => {
|
|
981
|
+
onProgress?.({
|
|
982
|
+
phase: 'scanning',
|
|
983
|
+
current,
|
|
984
|
+
total: 0,
|
|
985
|
+
currentFile: file,
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
// Detect frameworks once per indexAll run using the scanned file list.
|
|
989
|
+
// Names are passed to each parse call so framework-specific extractors
|
|
990
|
+
// (route nodes, middleware, etc.) run after the tree-sitter pass.
|
|
991
|
+
// Framework detection is reset each run so adding e.g. requirements.txt
|
|
992
|
+
// between runs is picked up without restarting the process.
|
|
993
|
+
this.detectedFrameworkNames = null;
|
|
994
|
+
const frameworkNames = this.ensureDetectedFrameworks(files);
|
|
995
|
+
if (signal?.aborted) {
|
|
996
|
+
return {
|
|
997
|
+
success: false,
|
|
998
|
+
filesIndexed: 0,
|
|
999
|
+
filesSkipped: 0,
|
|
1000
|
+
filesErrored: 0,
|
|
1001
|
+
nodesCreated: 0,
|
|
1002
|
+
edgesCreated: 0,
|
|
1003
|
+
errors: [{ message: 'Aborted', severity: 'error' }],
|
|
1004
|
+
durationMs: Date.now() - startTime,
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
// Phase 2: Parse files in a worker thread (keeps main thread unblocked for UI)
|
|
1008
|
+
const total = files.length;
|
|
1009
|
+
let processed = 0;
|
|
1010
|
+
// Emit parsing phase immediately so the progress bar appears during worker setup.
|
|
1011
|
+
// The yield lets the shimmer worker flush the phase transition to stdout before
|
|
1012
|
+
// the main thread starts synchronous grammar detection work.
|
|
1013
|
+
onProgress?.({
|
|
1014
|
+
phase: 'parsing',
|
|
1015
|
+
current: 0,
|
|
1016
|
+
total,
|
|
1017
|
+
});
|
|
1018
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
1019
|
+
// Detect needed languages and load grammars in the parse worker
|
|
1020
|
+
const neededLanguages = [...new Set(files.map((f) => (0, grammars_1.detectLanguage)(f, undefined, overrides)))];
|
|
1021
|
+
// .h files default to 'c' but may be C++ — ensure cpp grammar is loaded when c is needed
|
|
1022
|
+
if (neededLanguages.includes('c') && !neededLanguages.includes('cpp')) {
|
|
1023
|
+
neededLanguages.push('cpp');
|
|
1024
|
+
}
|
|
1025
|
+
// Try to use a worker thread for parsing (keeps main thread unblocked for UI).
|
|
1026
|
+
// Falls back to in-process parsing if the compiled worker is unavailable (e.g. tests).
|
|
1027
|
+
const parseWorkerPath = path.join(__dirname, 'parse-worker.js');
|
|
1028
|
+
const useWorker = fs.existsSync(parseWorkerPath);
|
|
1029
|
+
let WorkerClass = null;
|
|
1030
|
+
if (useWorker) {
|
|
1031
|
+
const { Worker } = await Promise.resolve().then(() => __importStar(require('worker_threads')));
|
|
1032
|
+
WorkerClass = Worker;
|
|
1033
|
+
}
|
|
1034
|
+
else {
|
|
1035
|
+
// In-process fallback: load grammars locally
|
|
1036
|
+
await (0, grammars_1.loadGrammarsForLanguages)(neededLanguages);
|
|
1037
|
+
}
|
|
1038
|
+
// --- Worker lifecycle management ---
|
|
1039
|
+
// The worker can crash (OOM in WASM) or hang on pathological files.
|
|
1040
|
+
// We track pending parse promises and handle both cases:
|
|
1041
|
+
// - Timeout: terminate + restart the worker, reject the timed-out request
|
|
1042
|
+
// - Crash: reject all pending promises, restart for remaining files
|
|
1043
|
+
let parseWorker = null;
|
|
1044
|
+
let nextId = 0;
|
|
1045
|
+
let workerParseCount = 0;
|
|
1046
|
+
const pendingParses = new Map();
|
|
1047
|
+
function rejectAllPending(reason) {
|
|
1048
|
+
for (const [id, pending] of pendingParses) {
|
|
1049
|
+
clearTimeout(pending.timer);
|
|
1050
|
+
pendingParses.delete(id);
|
|
1051
|
+
pending.reject(new Error(reason));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function attachWorkerHandlers(w) {
|
|
1055
|
+
w.on('message', (msg) => {
|
|
1056
|
+
if (msg.type === 'parse-result' && msg.id !== undefined) {
|
|
1057
|
+
const pending = pendingParses.get(msg.id);
|
|
1058
|
+
if (pending) {
|
|
1059
|
+
clearTimeout(pending.timer);
|
|
1060
|
+
pendingParses.delete(msg.id);
|
|
1061
|
+
pending.resolve(msg.result);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
w.on('error', (err) => {
|
|
1066
|
+
(0, errors_1.logWarn)('Parse worker error', { error: err.message });
|
|
1067
|
+
rejectAllPending(`Worker error: ${err.message}`);
|
|
1068
|
+
});
|
|
1069
|
+
w.on('exit', (code) => {
|
|
1070
|
+
if (code !== 0 && pendingParses.size > 0) {
|
|
1071
|
+
(0, errors_1.logWarn)('Parse worker exited unexpectedly', { code });
|
|
1072
|
+
rejectAllPending(`Worker exited with code ${code}`);
|
|
1073
|
+
}
|
|
1074
|
+
// Clear reference so we know to respawn, reset count so
|
|
1075
|
+
// the fresh worker gets a full cycle before recycling.
|
|
1076
|
+
if (parseWorker === w) {
|
|
1077
|
+
parseWorker = null;
|
|
1078
|
+
workerParseCount = 0;
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
async function ensureWorker() {
|
|
1083
|
+
if (parseWorker)
|
|
1084
|
+
return parseWorker;
|
|
1085
|
+
log('Spawning new parse worker...');
|
|
1086
|
+
parseWorker = new WorkerClass(parseWorkerPath);
|
|
1087
|
+
attachWorkerHandlers(parseWorker);
|
|
1088
|
+
// Load grammars in the new worker
|
|
1089
|
+
await new Promise((resolve, reject) => {
|
|
1090
|
+
parseWorker.once('message', (msg) => {
|
|
1091
|
+
if (msg.type === 'grammars-loaded')
|
|
1092
|
+
resolve();
|
|
1093
|
+
else
|
|
1094
|
+
reject(new Error(`Unexpected message: ${msg.type}`));
|
|
1095
|
+
});
|
|
1096
|
+
parseWorker.postMessage({ type: 'load-grammars', languages: neededLanguages });
|
|
1097
|
+
});
|
|
1098
|
+
return parseWorker;
|
|
1099
|
+
}
|
|
1100
|
+
if (WorkerClass) {
|
|
1101
|
+
await ensureWorker();
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Recycle the worker thread to reclaim WASM memory.
|
|
1105
|
+
* Terminates the current worker and clears the reference so
|
|
1106
|
+
* ensureWorker() will spawn a fresh one on the next call.
|
|
1107
|
+
*/
|
|
1108
|
+
function recycleWorker() {
|
|
1109
|
+
if (!parseWorker)
|
|
1110
|
+
return;
|
|
1111
|
+
log(`Recycling worker after ${workerParseCount} parses (heap: ${Math.round(process.memoryUsage().rss / 1024 / 1024)}MB RSS)`);
|
|
1112
|
+
const w = parseWorker;
|
|
1113
|
+
parseWorker = null;
|
|
1114
|
+
workerParseCount = 0;
|
|
1115
|
+
// Fire-and-forget: worker.terminate() can hang if WASM is stuck
|
|
1116
|
+
w.terminate().catch(() => { });
|
|
1117
|
+
}
|
|
1118
|
+
async function requestParse(filePath, content) {
|
|
1119
|
+
// Resolve the language on the main thread (where the project's
|
|
1120
|
+
// cgraphx.json overrides are loaded) and hand it to the worker, so the
|
|
1121
|
+
// worker never needs the override map itself.
|
|
1122
|
+
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1123
|
+
if (!WorkerClass) {
|
|
1124
|
+
// In-process fallback
|
|
1125
|
+
return (0, tree_sitter_1.extractFromSource)(filePath, content, language, frameworkNames);
|
|
1126
|
+
}
|
|
1127
|
+
// Recycle the worker before the next parse if we've hit the threshold.
|
|
1128
|
+
// This destroys the WASM linear memory (which can grow but never shrink)
|
|
1129
|
+
// and starts a fresh worker with a clean heap.
|
|
1130
|
+
if (workerParseCount >= WORKER_RECYCLE_INTERVAL) {
|
|
1131
|
+
await recycleWorker();
|
|
1132
|
+
}
|
|
1133
|
+
const worker = await ensureWorker();
|
|
1134
|
+
const id = nextId++;
|
|
1135
|
+
workerParseCount++;
|
|
1136
|
+
// Scale timeout for large files: base 10s + 10s per 100KB
|
|
1137
|
+
const timeoutMs = PARSE_TIMEOUT_MS + Math.floor(content.length / 100_000) * 10_000;
|
|
1138
|
+
return new Promise((resolve, reject) => {
|
|
1139
|
+
const timer = setTimeout(() => {
|
|
1140
|
+
pendingParses.delete(id);
|
|
1141
|
+
log(`TIMEOUT: ${filePath} exceeded ${timeoutMs}ms — killing worker`);
|
|
1142
|
+
// Reject FIRST — worker.terminate() can hang if WASM is stuck
|
|
1143
|
+
parseWorker = null;
|
|
1144
|
+
workerParseCount = 0;
|
|
1145
|
+
reject(new Error(`Parse timed out after ${timeoutMs}ms`));
|
|
1146
|
+
// Fire-and-forget: kill the stuck worker in the background
|
|
1147
|
+
worker.terminate().catch(() => { });
|
|
1148
|
+
}, timeoutMs);
|
|
1149
|
+
pendingParses.set(id, { resolve, reject, timer });
|
|
1150
|
+
worker.postMessage({ type: 'parse', id, filePath, content, frameworkNames, language });
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
for (let i = 0; i < files.length; i += FILE_IO_BATCH_SIZE) {
|
|
1154
|
+
if (signal?.aborted) {
|
|
1155
|
+
if (parseWorker)
|
|
1156
|
+
parseWorker.terminate().catch(() => { });
|
|
1157
|
+
return {
|
|
1158
|
+
success: false,
|
|
1159
|
+
filesIndexed,
|
|
1160
|
+
filesSkipped,
|
|
1161
|
+
filesErrored,
|
|
1162
|
+
nodesCreated: totalNodes,
|
|
1163
|
+
edgesCreated: totalEdges,
|
|
1164
|
+
errors: [{ message: 'Aborted', severity: 'error' }, ...errors],
|
|
1165
|
+
durationMs: Date.now() - startTime,
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
const batch = files.slice(i, i + FILE_IO_BATCH_SIZE);
|
|
1169
|
+
// Read files in parallel (with path validation before any I/O)
|
|
1170
|
+
const fileContents = await Promise.all(batch.map(async (fp) => {
|
|
1171
|
+
try {
|
|
1172
|
+
// Indexing read: follow in-root symlinks the directory walk already
|
|
1173
|
+
// descended into (the `../` guard still applies) so files reached
|
|
1174
|
+
// via an in-root symlink-to-outside still index (#935).
|
|
1175
|
+
const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, fp, { allowSymlinkEscape: true });
|
|
1176
|
+
if (!fullPath) {
|
|
1177
|
+
(0, errors_1.logWarn)('Path traversal blocked in batch reader', { filePath: fp });
|
|
1178
|
+
return { filePath: fp, content: null, stats: null, error: new Error('Path traversal blocked') };
|
|
1179
|
+
}
|
|
1180
|
+
const content = await fsp.readFile(fullPath, 'utf-8');
|
|
1181
|
+
const stats = await fsp.stat(fullPath);
|
|
1182
|
+
return { filePath: fp, content, stats, error: null };
|
|
1183
|
+
}
|
|
1184
|
+
catch (err) {
|
|
1185
|
+
return { filePath: fp, content: null, stats: null, error: err };
|
|
1186
|
+
}
|
|
1187
|
+
}));
|
|
1188
|
+
// Send to worker for parsing, store results on main thread
|
|
1189
|
+
for (const { filePath, content, stats, error } of fileContents) {
|
|
1190
|
+
if (signal?.aborted) {
|
|
1191
|
+
if (parseWorker)
|
|
1192
|
+
parseWorker.terminate().catch(() => { });
|
|
1193
|
+
return {
|
|
1194
|
+
success: false,
|
|
1195
|
+
filesIndexed,
|
|
1196
|
+
filesSkipped,
|
|
1197
|
+
filesErrored,
|
|
1198
|
+
nodesCreated: totalNodes,
|
|
1199
|
+
edgesCreated: totalEdges,
|
|
1200
|
+
errors: [{ message: 'Aborted', severity: 'error' }, ...errors],
|
|
1201
|
+
durationMs: Date.now() - startTime,
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
// Report progress before parsing (show current file being worked on)
|
|
1205
|
+
onProgress?.({
|
|
1206
|
+
phase: 'parsing',
|
|
1207
|
+
current: processed,
|
|
1208
|
+
total,
|
|
1209
|
+
currentFile: filePath,
|
|
1210
|
+
});
|
|
1211
|
+
if (error || content === null || stats === null) {
|
|
1212
|
+
processed++;
|
|
1213
|
+
filesErrored++;
|
|
1214
|
+
errors.push({
|
|
1215
|
+
message: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
|
|
1216
|
+
filePath,
|
|
1217
|
+
severity: 'error',
|
|
1218
|
+
code: 'read_error',
|
|
1219
|
+
});
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1222
|
+
// Honour MAX_FILE_SIZE. Without this check, vendored generated
|
|
1223
|
+
// headers, minified bundles, and other multi-MB files get indexed,
|
|
1224
|
+
// wasting WASM heap and the worker recycle budget on inputs with no
|
|
1225
|
+
// useful symbols. The single-file extractFile path already enforces
|
|
1226
|
+
// this; the bulk path used to silently skip the check.
|
|
1227
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
1228
|
+
processed++;
|
|
1229
|
+
filesSkipped++;
|
|
1230
|
+
errors.push({
|
|
1231
|
+
message: `File exceeds max size (${stats.size} > ${MAX_FILE_SIZE})`,
|
|
1232
|
+
filePath,
|
|
1233
|
+
severity: 'warning',
|
|
1234
|
+
code: 'size_exceeded',
|
|
1235
|
+
});
|
|
1236
|
+
onProgress?.({ phase: 'parsing', current: processed, total });
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
// Parse in worker thread (main thread stays unblocked).
|
|
1240
|
+
// Wrapped in try/catch to handle worker timeouts and crashes gracefully.
|
|
1241
|
+
let result;
|
|
1242
|
+
try {
|
|
1243
|
+
result = await requestParse(filePath, content);
|
|
1244
|
+
}
|
|
1245
|
+
catch (parseErr) {
|
|
1246
|
+
processed++;
|
|
1247
|
+
filesErrored++;
|
|
1248
|
+
errors.push({
|
|
1249
|
+
message: parseErr instanceof Error ? parseErr.message : String(parseErr),
|
|
1250
|
+
filePath,
|
|
1251
|
+
severity: 'error',
|
|
1252
|
+
code: 'parse_error',
|
|
1253
|
+
});
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
processed++;
|
|
1257
|
+
// Store in database on main thread (SQLite is not thread-safe)
|
|
1258
|
+
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1259
|
+
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1260
|
+
this.storeExtractionResult(filePath, content, language, stats, result);
|
|
1261
|
+
}
|
|
1262
|
+
if (result.errors.length > 0) {
|
|
1263
|
+
for (const err of result.errors) {
|
|
1264
|
+
if (!err.filePath)
|
|
1265
|
+
err.filePath = filePath;
|
|
1266
|
+
}
|
|
1267
|
+
errors.push(...result.errors);
|
|
1268
|
+
}
|
|
1269
|
+
if (result.nodes.length > 0) {
|
|
1270
|
+
filesIndexed++;
|
|
1271
|
+
totalNodes += result.nodes.length;
|
|
1272
|
+
totalEdges += result.edges.length;
|
|
1273
|
+
}
|
|
1274
|
+
else if (result.errors.some((e) => e.severity === 'error')) {
|
|
1275
|
+
filesErrored++;
|
|
1276
|
+
}
|
|
1277
|
+
else {
|
|
1278
|
+
// Files with no symbols but no errors (yaml, twig, properties) are
|
|
1279
|
+
// tracked at the file level — count them as indexed so the CLI
|
|
1280
|
+
// doesn't misleadingly report "No files found to index".
|
|
1281
|
+
const lang = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1282
|
+
if ((0, grammars_1.isFileLevelOnlyLanguage)(lang)) {
|
|
1283
|
+
filesIndexed++;
|
|
1284
|
+
}
|
|
1285
|
+
else {
|
|
1286
|
+
filesSkipped++;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
// Report 100% so the progress bar doesn't hang at 99%
|
|
1292
|
+
onProgress?.({
|
|
1293
|
+
phase: 'parsing',
|
|
1294
|
+
current: total,
|
|
1295
|
+
total,
|
|
1296
|
+
});
|
|
1297
|
+
// Yield so the shimmer worker's buffered stdout writes can flush.
|
|
1298
|
+
// Worker thread stdout is proxied through the main thread's event loop,
|
|
1299
|
+
// so synchronous work here blocks the animation from rendering.
|
|
1300
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
1301
|
+
// Retry pass: files that failed due to WASM memory corruption may succeed
|
|
1302
|
+
// on a fresh worker with a clean heap. Recycle before each attempt so
|
|
1303
|
+
// every file gets the absolute cleanest WASM state possible.
|
|
1304
|
+
const retryableErrors = errors.filter((e) => e.code === 'parse_error' && e.filePath &&
|
|
1305
|
+
(e.message.includes('Worker exited') || e.message.includes('memory access out of bounds')));
|
|
1306
|
+
if (retryableErrors.length > 0 && WorkerClass) {
|
|
1307
|
+
log(`Retrying ${retryableErrors.length} files that failed due to WASM memory errors...`);
|
|
1308
|
+
const stillFailing = [];
|
|
1309
|
+
for (const errEntry of retryableErrors) {
|
|
1310
|
+
const filePath = errEntry.filePath;
|
|
1311
|
+
if (signal?.aborted)
|
|
1312
|
+
break;
|
|
1313
|
+
// Fresh worker for every retry — maximum WASM headroom
|
|
1314
|
+
recycleWorker();
|
|
1315
|
+
let content;
|
|
1316
|
+
try {
|
|
1317
|
+
const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, filePath);
|
|
1318
|
+
if (!fullPath)
|
|
1319
|
+
continue;
|
|
1320
|
+
content = await fsp.readFile(fullPath, 'utf-8');
|
|
1321
|
+
}
|
|
1322
|
+
catch {
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
let result;
|
|
1326
|
+
try {
|
|
1327
|
+
result = await requestParse(filePath, content);
|
|
1328
|
+
}
|
|
1329
|
+
catch {
|
|
1330
|
+
stillFailing.push(errEntry);
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1333
|
+
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1334
|
+
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1335
|
+
const stats = await fsp.stat(path.join(this.rootDir, filePath));
|
|
1336
|
+
this.storeExtractionResult(filePath, content, language, stats, result);
|
|
1337
|
+
const idx = errors.indexOf(errEntry);
|
|
1338
|
+
if (idx >= 0)
|
|
1339
|
+
errors.splice(idx, 1);
|
|
1340
|
+
filesErrored--;
|
|
1341
|
+
filesIndexed++;
|
|
1342
|
+
totalNodes += result.nodes.length;
|
|
1343
|
+
totalEdges += result.edges.length;
|
|
1344
|
+
log(`Retry OK: ${filePath} (${result.nodes.length} nodes)`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
// Last resort: for files that still crash on a clean worker, strip
|
|
1348
|
+
// comment-only lines to reduce WASM memory pressure. Many compiler
|
|
1349
|
+
// test files are 90%+ comments (CHECK directives) that don't contribute
|
|
1350
|
+
// code nodes but consume parser memory.
|
|
1351
|
+
if (stillFailing.length > 0) {
|
|
1352
|
+
log(`${stillFailing.length} files still failing — retrying with comments stripped...`);
|
|
1353
|
+
for (const errEntry of stillFailing) {
|
|
1354
|
+
const filePath = errEntry.filePath;
|
|
1355
|
+
if (signal?.aborted)
|
|
1356
|
+
break;
|
|
1357
|
+
recycleWorker();
|
|
1358
|
+
let fullContent;
|
|
1359
|
+
try {
|
|
1360
|
+
const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, filePath);
|
|
1361
|
+
if (!fullPath)
|
|
1362
|
+
continue;
|
|
1363
|
+
fullContent = await fsp.readFile(fullPath, 'utf-8');
|
|
1364
|
+
}
|
|
1365
|
+
catch {
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
// Strip lines that are entirely comments (preserving line numbers
|
|
1369
|
+
// by replacing with empty lines so node positions stay correct)
|
|
1370
|
+
const stripped = fullContent
|
|
1371
|
+
.split('\n')
|
|
1372
|
+
.map(line => /^\s*\/\//.test(line) ? '' : line)
|
|
1373
|
+
.join('\n');
|
|
1374
|
+
let result;
|
|
1375
|
+
try {
|
|
1376
|
+
result = await requestParse(filePath, stripped);
|
|
1377
|
+
}
|
|
1378
|
+
catch {
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1382
|
+
const language = (0, grammars_1.detectLanguage)(filePath, fullContent, overrides);
|
|
1383
|
+
const stats = await fsp.stat(path.join(this.rootDir, filePath));
|
|
1384
|
+
this.storeExtractionResult(filePath, fullContent, language, stats, result);
|
|
1385
|
+
const idx = errors.indexOf(errEntry);
|
|
1386
|
+
if (idx >= 0)
|
|
1387
|
+
errors.splice(idx, 1);
|
|
1388
|
+
filesErrored--;
|
|
1389
|
+
filesIndexed++;
|
|
1390
|
+
totalNodes += result.nodes.length;
|
|
1391
|
+
totalEdges += result.edges.length;
|
|
1392
|
+
log(`Retry (stripped) OK: ${filePath} (${result.nodes.length} nodes)`);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
// Shut down parse worker and clear any pending timers
|
|
1398
|
+
rejectAllPending('Indexing complete');
|
|
1399
|
+
if (parseWorker) {
|
|
1400
|
+
parseWorker.terminate().catch(() => { });
|
|
1401
|
+
}
|
|
1402
|
+
return {
|
|
1403
|
+
success: filesIndexed > 0 || errors.filter((e) => e.severity === 'error').length === 0,
|
|
1404
|
+
filesIndexed,
|
|
1405
|
+
filesSkipped,
|
|
1406
|
+
filesErrored,
|
|
1407
|
+
nodesCreated: totalNodes,
|
|
1408
|
+
edgesCreated: totalEdges,
|
|
1409
|
+
errors,
|
|
1410
|
+
durationMs: Date.now() - startTime,
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Index specific files
|
|
1415
|
+
*/
|
|
1416
|
+
async indexFiles(filePaths) {
|
|
1417
|
+
const startTime = Date.now();
|
|
1418
|
+
const errors = [];
|
|
1419
|
+
let filesIndexed = 0;
|
|
1420
|
+
let filesSkipped = 0;
|
|
1421
|
+
let filesErrored = 0;
|
|
1422
|
+
let totalNodes = 0;
|
|
1423
|
+
let totalEdges = 0;
|
|
1424
|
+
for (const filePath of filePaths) {
|
|
1425
|
+
const result = await this.indexFile(filePath);
|
|
1426
|
+
if (result.errors.length > 0) {
|
|
1427
|
+
errors.push(...result.errors);
|
|
1428
|
+
}
|
|
1429
|
+
if (result.nodes.length > 0) {
|
|
1430
|
+
filesIndexed++;
|
|
1431
|
+
totalNodes += result.nodes.length;
|
|
1432
|
+
totalEdges += result.edges.length;
|
|
1433
|
+
}
|
|
1434
|
+
else if (result.errors.some((e) => e.severity === 'error')) {
|
|
1435
|
+
filesErrored++;
|
|
1436
|
+
}
|
|
1437
|
+
else {
|
|
1438
|
+
const tracked = this.queries.getFileByPath(filePath);
|
|
1439
|
+
if (tracked && (0, grammars_1.isFileLevelOnlyLanguage)(tracked.language)) {
|
|
1440
|
+
filesIndexed++;
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
filesSkipped++;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return {
|
|
1448
|
+
success: filesIndexed > 0 || errors.filter((e) => e.severity === 'error').length === 0,
|
|
1449
|
+
filesIndexed,
|
|
1450
|
+
filesSkipped,
|
|
1451
|
+
filesErrored,
|
|
1452
|
+
nodesCreated: totalNodes,
|
|
1453
|
+
edgesCreated: totalEdges,
|
|
1454
|
+
errors,
|
|
1455
|
+
durationMs: Date.now() - startTime,
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Index a single file
|
|
1460
|
+
*/
|
|
1461
|
+
async indexFile(relativePath) {
|
|
1462
|
+
// Indexing read: follow in-root symlinks (the `../` guard still applies), #935.
|
|
1463
|
+
const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, relativePath, { allowSymlinkEscape: true });
|
|
1464
|
+
if (!fullPath) {
|
|
1465
|
+
return {
|
|
1466
|
+
nodes: [],
|
|
1467
|
+
edges: [],
|
|
1468
|
+
unresolvedReferences: [],
|
|
1469
|
+
errors: [{ message: `Path traversal blocked: ${relativePath}`, filePath: relativePath, severity: 'error', code: 'path_traversal' }],
|
|
1470
|
+
durationMs: 0,
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
// Read file content and stats
|
|
1474
|
+
let content;
|
|
1475
|
+
let stats;
|
|
1476
|
+
try {
|
|
1477
|
+
stats = await fsp.stat(fullPath);
|
|
1478
|
+
content = await fsp.readFile(fullPath, 'utf-8');
|
|
1479
|
+
}
|
|
1480
|
+
catch (error) {
|
|
1481
|
+
return {
|
|
1482
|
+
nodes: [],
|
|
1483
|
+
edges: [],
|
|
1484
|
+
unresolvedReferences: [],
|
|
1485
|
+
errors: [
|
|
1486
|
+
{
|
|
1487
|
+
message: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
|
|
1488
|
+
filePath: relativePath,
|
|
1489
|
+
severity: 'error',
|
|
1490
|
+
code: 'read_error',
|
|
1491
|
+
},
|
|
1492
|
+
],
|
|
1493
|
+
durationMs: 0,
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
return this.indexFileWithContent(relativePath, content, stats);
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Index a single file with pre-read content and stats.
|
|
1500
|
+
* Used by the parallel batch reader to avoid redundant file I/O.
|
|
1501
|
+
*/
|
|
1502
|
+
async indexFileWithContent(relativePath, content, stats) {
|
|
1503
|
+
// Prevent `../` traversal; follow in-root symlinks like the directory walk (#935).
|
|
1504
|
+
const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, relativePath, { allowSymlinkEscape: true });
|
|
1505
|
+
if (!fullPath) {
|
|
1506
|
+
(0, errors_1.logWarn)('Path traversal blocked in indexFileWithContent', { relativePath });
|
|
1507
|
+
return {
|
|
1508
|
+
nodes: [],
|
|
1509
|
+
edges: [],
|
|
1510
|
+
unresolvedReferences: [],
|
|
1511
|
+
errors: [{ message: 'Path traversal blocked', filePath: relativePath, severity: 'error', code: 'path_traversal' }],
|
|
1512
|
+
durationMs: 0,
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
// Check file size
|
|
1516
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
1517
|
+
return {
|
|
1518
|
+
nodes: [],
|
|
1519
|
+
edges: [],
|
|
1520
|
+
unresolvedReferences: [],
|
|
1521
|
+
errors: [
|
|
1522
|
+
{
|
|
1523
|
+
message: `File exceeds max size (${stats.size} > ${MAX_FILE_SIZE})`,
|
|
1524
|
+
filePath: relativePath,
|
|
1525
|
+
severity: 'warning',
|
|
1526
|
+
code: 'size_exceeded',
|
|
1527
|
+
},
|
|
1528
|
+
],
|
|
1529
|
+
durationMs: 0,
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
// Detect language (honoring the project's cgraphx.json extension overrides)
|
|
1533
|
+
const language = (0, grammars_1.detectLanguage)(relativePath, content, (0, project_config_1.loadExtensionOverrides)(this.rootDir));
|
|
1534
|
+
if (!(0, grammars_1.isLanguageSupported)(language)) {
|
|
1535
|
+
return {
|
|
1536
|
+
nodes: [],
|
|
1537
|
+
edges: [],
|
|
1538
|
+
unresolvedReferences: [],
|
|
1539
|
+
errors: [],
|
|
1540
|
+
durationMs: 0,
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
// Extract from source. Use cached framework names if indexAll has run,
|
|
1544
|
+
// otherwise detect on the spot so single-file re-index paths still emit
|
|
1545
|
+
// route nodes / middleware / etc.
|
|
1546
|
+
const frameworkNames = this.ensureDetectedFrameworks();
|
|
1547
|
+
const result = (0, tree_sitter_1.extractFromSource)(relativePath, content, language, frameworkNames);
|
|
1548
|
+
// Store in database
|
|
1549
|
+
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1550
|
+
this.storeExtractionResult(relativePath, content, language, stats, result);
|
|
1551
|
+
}
|
|
1552
|
+
return result;
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Store extraction result in database
|
|
1556
|
+
*/
|
|
1557
|
+
storeExtractionResult(filePath, content, language, stats, result) {
|
|
1558
|
+
const contentHash = hashContent(content);
|
|
1559
|
+
// Check if file already exists and hasn't changed
|
|
1560
|
+
const existingFile = this.queries.getFileByPath(filePath);
|
|
1561
|
+
if (existingFile && existingFile.contentHash === contentHash) {
|
|
1562
|
+
return; // No changes
|
|
1563
|
+
}
|
|
1564
|
+
// Snapshot incoming cross-file edges BEFORE deleting this file's nodes.
|
|
1565
|
+
// `deleteFile` cascades to delete every edge whose source OR target is a
|
|
1566
|
+
// node in this file (edges.FK ... ON DELETE CASCADE). Edges whose SOURCE is
|
|
1567
|
+
// in this file are re-emitted by the extractor below, but edges whose SOURCE
|
|
1568
|
+
// is in a *different* (unchanged) file are not — they would be silently
|
|
1569
|
+
// dropped, which is issue #899: re-indexing a callee file severs `calls`/
|
|
1570
|
+
// `references` edges from callers that import it via module-attribute
|
|
1571
|
+
// access (`pkg.mod.fn(...)`).
|
|
1572
|
+
//
|
|
1573
|
+
// We snapshot the edge plus the target node's (name, kind) so we can
|
|
1574
|
+
// re-resolve to the re-indexed target's NEW id. Node ids are
|
|
1575
|
+
// `sha256(filePath:kind:name:line)`, so any line shift in the callee file
|
|
1576
|
+
// (e.g. a docstring-only edit above the symbol) changes every target id and
|
|
1577
|
+
// a naive re-insert by old id would silently drop every edge. Matching by
|
|
1578
|
+
// (filePath, kind, name) is stable across line shifts; if the symbol was
|
|
1579
|
+
// renamed/removed, no match is found and the edge stays dropped (correct).
|
|
1580
|
+
const crossFileIncomingEdges = existingFile
|
|
1581
|
+
? this.queries.getCrossFileIncomingEdgesWithTarget(filePath)
|
|
1582
|
+
: [];
|
|
1583
|
+
// Delete existing data for this file
|
|
1584
|
+
if (existingFile) {
|
|
1585
|
+
this.queries.deleteFile(filePath);
|
|
1586
|
+
}
|
|
1587
|
+
// Filter out nodes with missing required fields before insertion.
|
|
1588
|
+
// This prevents FK violations when edges reference nodes that would
|
|
1589
|
+
// be silently skipped by insertNode() (see issue #42).
|
|
1590
|
+
const validNodes = result.nodes.filter((n) => n.id && n.kind && n.name && n.filePath && n.language);
|
|
1591
|
+
// Insert nodes
|
|
1592
|
+
if (validNodes.length > 0) {
|
|
1593
|
+
this.queries.insertNodes(validNodes);
|
|
1594
|
+
}
|
|
1595
|
+
// Filter edges to only reference nodes that were actually inserted
|
|
1596
|
+
if (result.edges.length > 0) {
|
|
1597
|
+
const insertedIds = new Set(validNodes.map((n) => n.id));
|
|
1598
|
+
const validEdges = result.edges.filter((e) => insertedIds.has(e.source) && insertedIds.has(e.target));
|
|
1599
|
+
if (validEdges.length > 0) {
|
|
1600
|
+
this.queries.insertEdges(validEdges);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
// Re-insert cross-file incoming edges snapshotted before the delete,
|
|
1604
|
+
// re-resolving each edge's target to the re-indexed node's new id by
|
|
1605
|
+
// (filePath, kind, name). Node ids include the source line, so any line
|
|
1606
|
+
// shift in the callee file (e.g. a docstring-only edit above the symbol)
|
|
1607
|
+
// changes every target id and a naive re-insert by old id would drop them
|
|
1608
|
+
// all. `insertEdges` still filters to endpoints that exist, so edges whose
|
|
1609
|
+
// caller (source) was deleted, or whose callee (target) was renamed/removed
|
|
1610
|
+
// during the re-index (no match in `newTargetIds`), are dropped. This
|
|
1611
|
+
// closes the #899 edge-drop on `sync`.
|
|
1612
|
+
if (crossFileIncomingEdges.length > 0) {
|
|
1613
|
+
const newNodesByKindName = new Map();
|
|
1614
|
+
for (const n of validNodes) {
|
|
1615
|
+
newNodesByKindName.set(`${n.kind}\0${n.name}`, n.id);
|
|
1616
|
+
}
|
|
1617
|
+
const reinserted = [];
|
|
1618
|
+
for (const e of crossFileIncomingEdges) {
|
|
1619
|
+
const newTargetId = newNodesByKindName.get(`${e.targetKind}\0${e.targetName}`);
|
|
1620
|
+
if (newTargetId) {
|
|
1621
|
+
reinserted.push({ source: e.source, target: newTargetId, kind: e.kind, metadata: e.metadata, line: e.line, column: e.column, provenance: e.provenance });
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
if (reinserted.length > 0) {
|
|
1625
|
+
this.queries.insertEdges(reinserted);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
// Insert unresolved references in batch with denormalized filePath/language
|
|
1629
|
+
if (result.unresolvedReferences.length > 0) {
|
|
1630
|
+
const insertedIds = new Set(validNodes.map((n) => n.id));
|
|
1631
|
+
const refsWithContext = result.unresolvedReferences
|
|
1632
|
+
.filter((ref) => insertedIds.has(ref.fromNodeId))
|
|
1633
|
+
.map((ref) => ({
|
|
1634
|
+
...ref,
|
|
1635
|
+
filePath: ref.filePath ?? filePath,
|
|
1636
|
+
language: ref.language ?? language,
|
|
1637
|
+
}));
|
|
1638
|
+
if (refsWithContext.length > 0) {
|
|
1639
|
+
this.queries.insertUnresolvedRefsBatch(refsWithContext);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
// Insert file record
|
|
1643
|
+
const fileRecord = {
|
|
1644
|
+
path: filePath,
|
|
1645
|
+
contentHash,
|
|
1646
|
+
language,
|
|
1647
|
+
size: stats.size,
|
|
1648
|
+
modifiedAt: stats.mtimeMs,
|
|
1649
|
+
indexedAt: Date.now(),
|
|
1650
|
+
nodeCount: result.nodes.length,
|
|
1651
|
+
errors: result.errors.length > 0 ? result.errors : undefined,
|
|
1652
|
+
};
|
|
1653
|
+
this.queries.upsertFile(fileRecord);
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Sync the index with the current file state.
|
|
1657
|
+
*
|
|
1658
|
+
* Change detection is filesystem-based, never git: a (size, mtime) stat
|
|
1659
|
+
* pre-filter skips unchanged files, then a content-hash compare confirms real
|
|
1660
|
+
* changes. This works in non-git projects and catches committed changes from
|
|
1661
|
+
* `git pull`/`checkout`/`merge`/`rebase` that `git status` cannot see.
|
|
1662
|
+
*/
|
|
1663
|
+
async sync(onProgress) {
|
|
1664
|
+
await (0, grammars_1.initGrammars)(); // Initialize WASM runtime (grammars loaded lazily below)
|
|
1665
|
+
const startTime = Date.now();
|
|
1666
|
+
let filesChecked = 0;
|
|
1667
|
+
let filesAdded = 0;
|
|
1668
|
+
let filesModified = 0;
|
|
1669
|
+
let filesRemoved = 0;
|
|
1670
|
+
let nodesUpdated = 0;
|
|
1671
|
+
const changedFilePaths = [];
|
|
1672
|
+
onProgress?.({
|
|
1673
|
+
phase: 'scanning',
|
|
1674
|
+
current: 0,
|
|
1675
|
+
total: 0,
|
|
1676
|
+
});
|
|
1677
|
+
const filesToIndex = [];
|
|
1678
|
+
// === Filesystem reconcile (git-independent) ===
|
|
1679
|
+
// The source of truth for "what changed" is the filesystem vs the indexed
|
|
1680
|
+
// state — never git. We enumerate the current source files and reconcile
|
|
1681
|
+
// each against the DB. A cheap (size, mtime) stat pre-filter skips unchanged
|
|
1682
|
+
// files without reading or hashing them, so the expensive read+hash+parse
|
|
1683
|
+
// only runs for files that actually changed. This catches edits/adds/deletes
|
|
1684
|
+
// whether or not the project uses git, and crucially also catches committed
|
|
1685
|
+
// changes from `git pull`/`checkout`/`merge`/`rebase` — which `git status`
|
|
1686
|
+
// cannot see, because the working tree is clean afterward.
|
|
1687
|
+
const currentFiles = await scanDirectoryAsync(this.rootDir);
|
|
1688
|
+
filesChecked = currentFiles.length;
|
|
1689
|
+
const currentSet = new Set(currentFiles);
|
|
1690
|
+
const trackedFiles = this.queries.getAllFiles();
|
|
1691
|
+
const trackedMap = new Map();
|
|
1692
|
+
for (const f of trackedFiles) {
|
|
1693
|
+
trackedMap.set(f.path, f);
|
|
1694
|
+
}
|
|
1695
|
+
// Removals: tracked in the DB but no longer a present source file. Check the
|
|
1696
|
+
// filesystem directly — `scanDirectory` (via `git ls-files`) still lists a
|
|
1697
|
+
// file deleted from disk but not yet staged, so set membership alone misses it.
|
|
1698
|
+
// `reconcileChecks` drives the cooperative yield shared with the adds/mods loop
|
|
1699
|
+
// below (see SYNC_RECONCILE_YIELD_INTERVAL / issue #905).
|
|
1700
|
+
let reconcileChecks = 0;
|
|
1701
|
+
for (const tracked of trackedFiles) {
|
|
1702
|
+
if (!currentSet.has(tracked.path) || !fs.existsSync(path.join(this.rootDir, tracked.path))) {
|
|
1703
|
+
this.queries.deleteFile(tracked.path);
|
|
1704
|
+
filesRemoved++;
|
|
1705
|
+
}
|
|
1706
|
+
if (++reconcileChecks % SYNC_RECONCILE_YIELD_INTERVAL === 0) {
|
|
1707
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
// Adds / modifications.
|
|
1711
|
+
for (const filePath of currentFiles) {
|
|
1712
|
+
// Same cooperative yield as the removals loop — this is the other O(files)
|
|
1713
|
+
// synchronous-stat loop that wedges the main thread on a large repo (#905).
|
|
1714
|
+
// Yield at the top of the body so the `continue` fast-paths below still hit it.
|
|
1715
|
+
if (++reconcileChecks % SYNC_RECONCILE_YIELD_INTERVAL === 0) {
|
|
1716
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1717
|
+
}
|
|
1718
|
+
const fullPath = path.join(this.rootDir, filePath);
|
|
1719
|
+
const tracked = trackedMap.get(filePath);
|
|
1720
|
+
// Cheap pre-filter: an already-indexed file whose size AND mtime both match
|
|
1721
|
+
// the DB is unchanged — skip it without reading or hashing. (A content
|
|
1722
|
+
// change that preserves both exactly is the blind spot every mtime-based
|
|
1723
|
+
// incremental tool accepts; `index --force` is the escape hatch. Git bumps
|
|
1724
|
+
// mtime on every file it writes during checkout/merge, so pulls are caught.)
|
|
1725
|
+
if (tracked) {
|
|
1726
|
+
try {
|
|
1727
|
+
const stat = fs.statSync(fullPath);
|
|
1728
|
+
if (stat.size === tracked.size && Math.floor(stat.mtimeMs) === Math.floor(tracked.modifiedAt)) {
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
catch (error) {
|
|
1733
|
+
(0, errors_1.logDebug)('Skipping unstattable file during sync', { filePath, error: String(error) });
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
// New, or size/mtime changed — read + hash to confirm a real content change.
|
|
1738
|
+
let content;
|
|
1739
|
+
try {
|
|
1740
|
+
content = fs.readFileSync(fullPath, 'utf-8');
|
|
1741
|
+
}
|
|
1742
|
+
catch (error) {
|
|
1743
|
+
(0, errors_1.logDebug)('Skipping unreadable file during sync', { filePath, error: String(error) });
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
const contentHash = hashContent(content);
|
|
1747
|
+
if (!tracked) {
|
|
1748
|
+
filesToIndex.push(filePath);
|
|
1749
|
+
changedFilePaths.push(filePath);
|
|
1750
|
+
filesAdded++;
|
|
1751
|
+
}
|
|
1752
|
+
else if (tracked.contentHash !== contentHash) {
|
|
1753
|
+
filesToIndex.push(filePath);
|
|
1754
|
+
changedFilePaths.push(filePath);
|
|
1755
|
+
filesModified++;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
// Load only grammars needed for changed files
|
|
1759
|
+
if (filesToIndex.length > 0) {
|
|
1760
|
+
const overrides = (0, project_config_1.loadExtensionOverrides)(this.rootDir);
|
|
1761
|
+
const neededLanguages = [...new Set(filesToIndex.map((f) => (0, grammars_1.detectLanguage)(f, undefined, overrides)))];
|
|
1762
|
+
// .h files default to 'c' but may be C++ — ensure cpp grammar is loaded
|
|
1763
|
+
if (neededLanguages.includes('c') && !neededLanguages.includes('cpp')) {
|
|
1764
|
+
neededLanguages.push('cpp');
|
|
1765
|
+
}
|
|
1766
|
+
await (0, grammars_1.loadGrammarsForLanguages)(neededLanguages);
|
|
1767
|
+
}
|
|
1768
|
+
// Index changed files
|
|
1769
|
+
const total = filesToIndex.length;
|
|
1770
|
+
for (let i = 0; i < filesToIndex.length; i++) {
|
|
1771
|
+
const filePath = filesToIndex[i];
|
|
1772
|
+
onProgress?.({
|
|
1773
|
+
phase: 'parsing',
|
|
1774
|
+
current: i + 1,
|
|
1775
|
+
total,
|
|
1776
|
+
currentFile: filePath,
|
|
1777
|
+
});
|
|
1778
|
+
const result = await this.indexFile(filePath);
|
|
1779
|
+
nodesUpdated += result.nodes.length;
|
|
1780
|
+
}
|
|
1781
|
+
return {
|
|
1782
|
+
filesChecked,
|
|
1783
|
+
filesAdded,
|
|
1784
|
+
filesModified,
|
|
1785
|
+
filesRemoved,
|
|
1786
|
+
nodesUpdated,
|
|
1787
|
+
durationMs: Date.now() - startTime,
|
|
1788
|
+
changedFilePaths: changedFilePaths.length > 0 ? changedFilePaths : undefined,
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Get files that have changed since last index.
|
|
1793
|
+
* Uses git status as a fast path when available, falling back to full scan.
|
|
1794
|
+
*/
|
|
1795
|
+
getChangedFiles() {
|
|
1796
|
+
const gitChanges = getGitChangedFiles(this.rootDir);
|
|
1797
|
+
if (gitChanges) {
|
|
1798
|
+
// === Git fast path ===
|
|
1799
|
+
const added = [];
|
|
1800
|
+
const modified = [];
|
|
1801
|
+
const removed = [];
|
|
1802
|
+
// Deleted files — only report if tracked in DB
|
|
1803
|
+
for (const filePath of gitChanges.deleted) {
|
|
1804
|
+
const tracked = this.queries.getFileByPath(filePath);
|
|
1805
|
+
if (tracked) {
|
|
1806
|
+
removed.push(filePath);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
// Modified + added files — read + hash, compare with DB. Untracked (`??`)
|
|
1810
|
+
// files stay untracked in git even after indexing, so they must be
|
|
1811
|
+
// hash-compared like modified files instead of always counting as added —
|
|
1812
|
+
// otherwise status reports them as pending forever. (See issue #206.)
|
|
1813
|
+
for (const filePath of [...gitChanges.modified, ...gitChanges.added]) {
|
|
1814
|
+
const fullPath = path.join(this.rootDir, filePath);
|
|
1815
|
+
let content;
|
|
1816
|
+
try {
|
|
1817
|
+
content = fs.readFileSync(fullPath, 'utf-8');
|
|
1818
|
+
}
|
|
1819
|
+
catch (error) {
|
|
1820
|
+
(0, errors_1.logDebug)('Skipping unreadable file while detecting changes', { filePath, error: String(error) });
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1823
|
+
const contentHash = hashContent(content);
|
|
1824
|
+
const tracked = this.queries.getFileByPath(filePath);
|
|
1825
|
+
if (!tracked) {
|
|
1826
|
+
added.push(filePath);
|
|
1827
|
+
}
|
|
1828
|
+
else if (tracked.contentHash !== contentHash) {
|
|
1829
|
+
modified.push(filePath);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
return { added, modified, removed };
|
|
1833
|
+
}
|
|
1834
|
+
// === Fallback: full scan (non-git project or git failure) ===
|
|
1835
|
+
const currentFiles = new Set(scanDirectory(this.rootDir));
|
|
1836
|
+
const trackedFiles = this.queries.getAllFiles();
|
|
1837
|
+
// Build Map for O(1) lookups
|
|
1838
|
+
const trackedMap = new Map();
|
|
1839
|
+
for (const f of trackedFiles) {
|
|
1840
|
+
trackedMap.set(f.path, f);
|
|
1841
|
+
}
|
|
1842
|
+
const added = [];
|
|
1843
|
+
const modified = [];
|
|
1844
|
+
const removed = [];
|
|
1845
|
+
// Find removed files
|
|
1846
|
+
for (const tracked of trackedFiles) {
|
|
1847
|
+
if (!currentFiles.has(tracked.path)) {
|
|
1848
|
+
removed.push(tracked.path);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
// Find added and modified files
|
|
1852
|
+
for (const filePath of currentFiles) {
|
|
1853
|
+
const fullPath = path.join(this.rootDir, filePath);
|
|
1854
|
+
let content;
|
|
1855
|
+
try {
|
|
1856
|
+
content = fs.readFileSync(fullPath, 'utf-8');
|
|
1857
|
+
}
|
|
1858
|
+
catch (error) {
|
|
1859
|
+
(0, errors_1.logDebug)('Skipping unreadable file while detecting changes', { filePath, error: String(error) });
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
const contentHash = hashContent(content);
|
|
1863
|
+
const tracked = trackedMap.get(filePath);
|
|
1864
|
+
if (!tracked) {
|
|
1865
|
+
added.push(filePath);
|
|
1866
|
+
}
|
|
1867
|
+
else if (tracked.contentHash !== contentHash) {
|
|
1868
|
+
modified.push(filePath);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return { added, modified, removed };
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
exports.ExtractionOrchestrator = ExtractionOrchestrator;
|
|
1875
|
+
// Re-export useful types and functions
|
|
1876
|
+
var tree_sitter_2 = require("./tree-sitter");
|
|
1877
|
+
Object.defineProperty(exports, "extractFromSource", { enumerable: true, get: function () { return tree_sitter_2.extractFromSource; } });
|
|
1878
|
+
var grammars_2 = require("./grammars");
|
|
1879
|
+
Object.defineProperty(exports, "detectLanguage", { enumerable: true, get: function () { return grammars_2.detectLanguage; } });
|
|
1880
|
+
Object.defineProperty(exports, "isSourceFile", { enumerable: true, get: function () { return grammars_2.isSourceFile; } });
|
|
1881
|
+
Object.defineProperty(exports, "isLanguageSupported", { enumerable: true, get: function () { return grammars_2.isLanguageSupported; } });
|
|
1882
|
+
Object.defineProperty(exports, "isGrammarLoaded", { enumerable: true, get: function () { return grammars_2.isGrammarLoaded; } });
|
|
1883
|
+
Object.defineProperty(exports, "getSupportedLanguages", { enumerable: true, get: function () { return grammars_2.getSupportedLanguages; } });
|
|
1884
|
+
Object.defineProperty(exports, "initGrammars", { enumerable: true, get: function () { return grammars_2.initGrammars; } });
|
|
1885
|
+
Object.defineProperty(exports, "loadGrammarsForLanguages", { enumerable: true, get: function () { return grammars_2.loadGrammarsForLanguages; } });
|
|
1886
|
+
Object.defineProperty(exports, "loadAllGrammars", { enumerable: true, get: function () { return grammars_2.loadAllGrammars; } });
|
|
1887
|
+
//# sourceMappingURL=index.js.map
|