@stupidloud/codegraph 0.8.1 → 0.9.9

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.
Files changed (341) hide show
  1. package/README.md +319 -152
  2. package/dist/bin/codegraph.d.ts +4 -0
  3. package/dist/bin/codegraph.d.ts.map +1 -1
  4. package/dist/bin/codegraph.js +354 -90
  5. package/dist/bin/codegraph.js.map +1 -1
  6. package/dist/bin/node-version-check.d.ts +17 -0
  7. package/dist/bin/node-version-check.d.ts.map +1 -1
  8. package/dist/bin/node-version-check.js +37 -0
  9. package/dist/bin/node-version-check.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +1 -11
  12. package/dist/config.js.map +1 -1
  13. package/dist/context/formatter.d.ts.map +1 -1
  14. package/dist/context/formatter.js +25 -6
  15. package/dist/context/formatter.js.map +1 -1
  16. package/dist/context/index.d.ts +22 -0
  17. package/dist/context/index.d.ts.map +1 -1
  18. package/dist/context/index.js +257 -6
  19. package/dist/context/index.js.map +1 -1
  20. package/dist/context/markers.d.ts +19 -0
  21. package/dist/context/markers.d.ts.map +1 -0
  22. package/dist/context/markers.js +22 -0
  23. package/dist/context/markers.js.map +1 -0
  24. package/dist/db/index.d.ts +30 -1
  25. package/dist/db/index.d.ts.map +1 -1
  26. package/dist/db/index.js +75 -25
  27. package/dist/db/index.js.map +1 -1
  28. package/dist/db/queries.d.ts +104 -0
  29. package/dist/db/queries.d.ts.map +1 -1
  30. package/dist/db/queries.js +328 -31
  31. package/dist/db/queries.js.map +1 -1
  32. package/dist/db/sqlite-adapter.d.ts +24 -23
  33. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  34. package/dist/db/sqlite-adapter.js +54 -174
  35. package/dist/db/sqlite-adapter.js.map +1 -1
  36. package/dist/directory.d.ts.map +1 -1
  37. package/dist/directory.js +6 -20
  38. package/dist/directory.js.map +1 -1
  39. package/dist/extraction/generated-detection.d.ts +30 -0
  40. package/dist/extraction/generated-detection.d.ts.map +1 -0
  41. package/dist/extraction/generated-detection.js +80 -0
  42. package/dist/extraction/generated-detection.js.map +1 -0
  43. package/dist/extraction/grammars.d.ts +23 -1
  44. package/dist/extraction/grammars.d.ts.map +1 -1
  45. package/dist/extraction/grammars.js +107 -3
  46. package/dist/extraction/grammars.js.map +1 -1
  47. package/dist/extraction/index.d.ts +22 -14
  48. package/dist/extraction/index.d.ts.map +1 -1
  49. package/dist/extraction/index.js +272 -183
  50. package/dist/extraction/index.js.map +1 -1
  51. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  52. package/dist/extraction/languages/c-cpp.js +45 -0
  53. package/dist/extraction/languages/c-cpp.js.map +1 -1
  54. package/dist/extraction/languages/csharp.d.ts.map +1 -1
  55. package/dist/extraction/languages/csharp.js +2 -1
  56. package/dist/extraction/languages/csharp.js.map +1 -1
  57. package/dist/extraction/languages/go.d.ts.map +1 -1
  58. package/dist/extraction/languages/go.js +18 -2
  59. package/dist/extraction/languages/go.js.map +1 -1
  60. package/dist/extraction/languages/index.d.ts.map +1 -1
  61. package/dist/extraction/languages/index.js +6 -0
  62. package/dist/extraction/languages/index.js.map +1 -1
  63. package/dist/extraction/languages/java.d.ts.map +1 -1
  64. package/dist/extraction/languages/java.js +6 -0
  65. package/dist/extraction/languages/java.js.map +1 -1
  66. package/dist/extraction/languages/kotlin.d.ts.map +1 -1
  67. package/dist/extraction/languages/kotlin.js +6 -0
  68. package/dist/extraction/languages/kotlin.js.map +1 -1
  69. package/dist/extraction/languages/lua.d.ts +3 -0
  70. package/dist/extraction/languages/lua.d.ts.map +1 -0
  71. package/dist/extraction/languages/lua.js +150 -0
  72. package/dist/extraction/languages/lua.js.map +1 -0
  73. package/dist/extraction/languages/luau.d.ts +3 -0
  74. package/dist/extraction/languages/luau.d.ts.map +1 -0
  75. package/dist/extraction/languages/luau.js +37 -0
  76. package/dist/extraction/languages/luau.js.map +1 -0
  77. package/dist/extraction/languages/objc.d.ts +3 -0
  78. package/dist/extraction/languages/objc.d.ts.map +1 -0
  79. package/dist/extraction/languages/objc.js +133 -0
  80. package/dist/extraction/languages/objc.js.map +1 -0
  81. package/dist/extraction/mybatis-extractor.d.ts +48 -0
  82. package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
  83. package/dist/extraction/mybatis-extractor.js +198 -0
  84. package/dist/extraction/mybatis-extractor.js.map +1 -0
  85. package/dist/extraction/tree-sitter-types.d.ts +14 -0
  86. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  87. package/dist/extraction/tree-sitter.d.ts +84 -0
  88. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  89. package/dist/extraction/tree-sitter.js +715 -16
  90. package/dist/extraction/tree-sitter.js.map +1 -1
  91. package/dist/extraction/vue-extractor.d.ts +15 -0
  92. package/dist/extraction/vue-extractor.d.ts.map +1 -1
  93. package/dist/extraction/vue-extractor.js +88 -0
  94. package/dist/extraction/vue-extractor.js.map +1 -1
  95. package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
  96. package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
  97. package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
  98. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
  99. package/dist/extraction/wasm-runtime-flags.js +106 -0
  100. package/dist/extraction/wasm-runtime-flags.js.map +1 -0
  101. package/dist/graph/traversal.d.ts.map +1 -1
  102. package/dist/graph/traversal.js +76 -38
  103. package/dist/graph/traversal.js.map +1 -1
  104. package/dist/index.d.ts +77 -8
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +133 -19
  107. package/dist/index.js.map +1 -1
  108. package/dist/installer/config-writer.d.ts +7 -8
  109. package/dist/installer/config-writer.d.ts.map +1 -1
  110. package/dist/installer/config-writer.js +7 -27
  111. package/dist/installer/config-writer.js.map +1 -1
  112. package/dist/installer/index.d.ts +51 -16
  113. package/dist/installer/index.d.ts.map +1 -1
  114. package/dist/installer/index.js +120 -29
  115. package/dist/installer/index.js.map +1 -1
  116. package/dist/installer/instructions-template.d.ts +11 -21
  117. package/dist/installer/instructions-template.d.ts.map +1 -1
  118. package/dist/installer/instructions-template.js +12 -56
  119. package/dist/installer/instructions-template.js.map +1 -1
  120. package/dist/installer/targets/antigravity.d.ts +57 -0
  121. package/dist/installer/targets/antigravity.d.ts.map +1 -0
  122. package/dist/installer/targets/antigravity.js +308 -0
  123. package/dist/installer/targets/antigravity.js.map +1 -0
  124. package/dist/installer/targets/claude.d.ts +26 -1
  125. package/dist/installer/targets/claude.d.ts.map +1 -1
  126. package/dist/installer/targets/claude.js +118 -40
  127. package/dist/installer/targets/claude.js.map +1 -1
  128. package/dist/installer/targets/codex.d.ts.map +1 -1
  129. package/dist/installer/targets/codex.js +15 -13
  130. package/dist/installer/targets/codex.js.map +1 -1
  131. package/dist/installer/targets/cursor.d.ts.map +1 -1
  132. package/dist/installer/targets/cursor.js +61 -36
  133. package/dist/installer/targets/cursor.js.map +1 -1
  134. package/dist/installer/targets/gemini.d.ts +26 -0
  135. package/dist/installer/targets/gemini.d.ts.map +1 -0
  136. package/dist/installer/targets/gemini.js +167 -0
  137. package/dist/installer/targets/gemini.js.map +1 -0
  138. package/dist/installer/targets/hermes.d.ts +18 -0
  139. package/dist/installer/targets/hermes.d.ts.map +1 -0
  140. package/dist/installer/targets/hermes.js +359 -0
  141. package/dist/installer/targets/hermes.js.map +1 -0
  142. package/dist/installer/targets/kiro.d.ts +27 -0
  143. package/dist/installer/targets/kiro.d.ts.map +1 -0
  144. package/dist/installer/targets/kiro.js +178 -0
  145. package/dist/installer/targets/kiro.js.map +1 -0
  146. package/dist/installer/targets/opencode.d.ts.map +1 -1
  147. package/dist/installer/targets/opencode.js +15 -13
  148. package/dist/installer/targets/opencode.js.map +1 -1
  149. package/dist/installer/targets/registry.d.ts.map +1 -1
  150. package/dist/installer/targets/registry.js +8 -0
  151. package/dist/installer/targets/registry.js.map +1 -1
  152. package/dist/installer/targets/shared.d.ts.map +1 -1
  153. package/dist/installer/targets/shared.js +3 -2
  154. package/dist/installer/targets/shared.js.map +1 -1
  155. package/dist/installer/targets/types.d.ts +1 -16
  156. package/dist/installer/targets/types.d.ts.map +1 -1
  157. package/dist/mcp/daemon-paths.d.ts +46 -0
  158. package/dist/mcp/daemon-paths.d.ts.map +1 -0
  159. package/dist/mcp/daemon-paths.js +125 -0
  160. package/dist/mcp/daemon-paths.js.map +1 -0
  161. package/dist/mcp/daemon.d.ts +161 -0
  162. package/dist/mcp/daemon.d.ts.map +1 -0
  163. package/dist/mcp/daemon.js +403 -0
  164. package/dist/mcp/daemon.js.map +1 -0
  165. package/dist/mcp/engine.d.ts +105 -0
  166. package/dist/mcp/engine.d.ts.map +1 -0
  167. package/dist/mcp/engine.js +270 -0
  168. package/dist/mcp/engine.js.map +1 -0
  169. package/dist/mcp/index.d.ts +70 -52
  170. package/dist/mcp/index.d.ts.map +1 -1
  171. package/dist/mcp/index.js +355 -331
  172. package/dist/mcp/index.js.map +1 -1
  173. package/dist/mcp/proxy.d.ts +81 -0
  174. package/dist/mcp/proxy.d.ts.map +1 -0
  175. package/dist/mcp/proxy.js +510 -0
  176. package/dist/mcp/proxy.js.map +1 -0
  177. package/dist/mcp/server-instructions.d.ts +1 -1
  178. package/dist/mcp/server-instructions.d.ts.map +1 -1
  179. package/dist/mcp/server-instructions.js +21 -21
  180. package/dist/mcp/session.d.ts +77 -0
  181. package/dist/mcp/session.d.ts.map +1 -0
  182. package/dist/mcp/session.js +294 -0
  183. package/dist/mcp/session.js.map +1 -0
  184. package/dist/mcp/tools.d.ts +171 -15
  185. package/dist/mcp/tools.d.ts.map +1 -1
  186. package/dist/mcp/tools.js +1714 -298
  187. package/dist/mcp/tools.js.map +1 -1
  188. package/dist/mcp/transport.d.ts +111 -29
  189. package/dist/mcp/transport.d.ts.map +1 -1
  190. package/dist/mcp/transport.js +181 -71
  191. package/dist/mcp/transport.js.map +1 -1
  192. package/dist/mcp/version.d.ts +19 -0
  193. package/dist/mcp/version.d.ts.map +1 -0
  194. package/dist/mcp/version.js +71 -0
  195. package/dist/mcp/version.js.map +1 -0
  196. package/dist/resolution/callback-synthesizer.d.ts +10 -0
  197. package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
  198. package/dist/resolution/callback-synthesizer.js +1300 -0
  199. package/dist/resolution/callback-synthesizer.js.map +1 -0
  200. package/dist/resolution/frameworks/csharp.d.ts.map +1 -1
  201. package/dist/resolution/frameworks/csharp.js +36 -8
  202. package/dist/resolution/frameworks/csharp.js.map +1 -1
  203. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  204. package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
  205. package/dist/resolution/frameworks/drupal.js +367 -0
  206. package/dist/resolution/frameworks/drupal.js.map +1 -0
  207. package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  208. package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
  209. package/dist/resolution/frameworks/expo-modules.js +143 -0
  210. package/dist/resolution/frameworks/expo-modules.js.map +1 -0
  211. package/dist/resolution/frameworks/express.d.ts.map +1 -1
  212. package/dist/resolution/frameworks/express.js +102 -19
  213. package/dist/resolution/frameworks/express.js.map +1 -1
  214. package/dist/resolution/frameworks/fabric.d.ts +3 -0
  215. package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
  216. package/dist/resolution/frameworks/fabric.js +354 -0
  217. package/dist/resolution/frameworks/fabric.js.map +1 -0
  218. package/dist/resolution/frameworks/go.d.ts.map +1 -1
  219. package/dist/resolution/frameworks/go.js +6 -3
  220. package/dist/resolution/frameworks/go.js.map +1 -1
  221. package/dist/resolution/frameworks/index.d.ts +6 -0
  222. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  223. package/dist/resolution/frameworks/index.js +29 -1
  224. package/dist/resolution/frameworks/index.js.map +1 -1
  225. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  226. package/dist/resolution/frameworks/java.js +339 -12
  227. package/dist/resolution/frameworks/java.js.map +1 -1
  228. package/dist/resolution/frameworks/laravel.d.ts.map +1 -1
  229. package/dist/resolution/frameworks/laravel.js +17 -8
  230. package/dist/resolution/frameworks/laravel.js.map +1 -1
  231. package/dist/resolution/frameworks/nestjs.d.ts.map +1 -1
  232. package/dist/resolution/frameworks/nestjs.js +324 -0
  233. package/dist/resolution/frameworks/nestjs.js.map +1 -1
  234. package/dist/resolution/frameworks/play.d.ts +19 -0
  235. package/dist/resolution/frameworks/play.d.ts.map +1 -0
  236. package/dist/resolution/frameworks/play.js +111 -0
  237. package/dist/resolution/frameworks/play.js.map +1 -0
  238. package/dist/resolution/frameworks/python.d.ts.map +1 -1
  239. package/dist/resolution/frameworks/python.js +134 -16
  240. package/dist/resolution/frameworks/python.js.map +1 -1
  241. package/dist/resolution/frameworks/react-native.d.ts +3 -0
  242. package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
  243. package/dist/resolution/frameworks/react-native.js +360 -0
  244. package/dist/resolution/frameworks/react-native.js.map +1 -0
  245. package/dist/resolution/frameworks/react.d.ts.map +1 -1
  246. package/dist/resolution/frameworks/react.js +96 -3
  247. package/dist/resolution/frameworks/react.js.map +1 -1
  248. package/dist/resolution/frameworks/ruby.d.ts.map +1 -1
  249. package/dist/resolution/frameworks/ruby.js +106 -2
  250. package/dist/resolution/frameworks/ruby.js.map +1 -1
  251. package/dist/resolution/frameworks/rust.d.ts.map +1 -1
  252. package/dist/resolution/frameworks/rust.js +102 -5
  253. package/dist/resolution/frameworks/rust.js.map +1 -1
  254. package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  255. package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
  256. package/dist/resolution/frameworks/swift-objc.js +252 -0
  257. package/dist/resolution/frameworks/swift-objc.js.map +1 -0
  258. package/dist/resolution/frameworks/swift.d.ts.map +1 -1
  259. package/dist/resolution/frameworks/swift.js +30 -6
  260. package/dist/resolution/frameworks/swift.js.map +1 -1
  261. package/dist/resolution/go-module.d.ts +26 -0
  262. package/dist/resolution/go-module.d.ts.map +1 -0
  263. package/dist/resolution/go-module.js +78 -0
  264. package/dist/resolution/go-module.js.map +1 -0
  265. package/dist/resolution/import-resolver.d.ts +28 -0
  266. package/dist/resolution/import-resolver.d.ts.map +1 -1
  267. package/dist/resolution/import-resolver.js +617 -5
  268. package/dist/resolution/import-resolver.js.map +1 -1
  269. package/dist/resolution/index.d.ts +11 -0
  270. package/dist/resolution/index.d.ts.map +1 -1
  271. package/dist/resolution/index.js +196 -10
  272. package/dist/resolution/index.js.map +1 -1
  273. package/dist/resolution/lru-cache.d.ts +24 -0
  274. package/dist/resolution/lru-cache.d.ts.map +1 -0
  275. package/dist/resolution/lru-cache.js +62 -0
  276. package/dist/resolution/lru-cache.js.map +1 -0
  277. package/dist/resolution/name-matcher.d.ts.map +1 -1
  278. package/dist/resolution/name-matcher.js +212 -0
  279. package/dist/resolution/name-matcher.js.map +1 -1
  280. package/dist/resolution/swift-objc-bridge.d.ts +134 -0
  281. package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
  282. package/dist/resolution/swift-objc-bridge.js +256 -0
  283. package/dist/resolution/swift-objc-bridge.js.map +1 -0
  284. package/dist/resolution/types.d.ts +44 -0
  285. package/dist/resolution/types.d.ts.map +1 -1
  286. package/dist/resolution/workspace-packages.d.ts +48 -0
  287. package/dist/resolution/workspace-packages.d.ts.map +1 -0
  288. package/dist/resolution/workspace-packages.js +208 -0
  289. package/dist/resolution/workspace-packages.js.map +1 -0
  290. package/dist/search/query-utils.d.ts +18 -0
  291. package/dist/search/query-utils.d.ts.map +1 -1
  292. package/dist/search/query-utils.js +30 -0
  293. package/dist/search/query-utils.js.map +1 -1
  294. package/dist/sync/git-hooks.d.ts.map +1 -1
  295. package/dist/sync/git-hooks.js +2 -0
  296. package/dist/sync/git-hooks.js.map +1 -1
  297. package/dist/sync/index.d.ts +3 -1
  298. package/dist/sync/index.d.ts.map +1 -1
  299. package/dist/sync/index.js +8 -1
  300. package/dist/sync/index.js.map +1 -1
  301. package/dist/sync/watcher.d.ts +214 -12
  302. package/dist/sync/watcher.d.ts.map +1 -1
  303. package/dist/sync/watcher.js +467 -55
  304. package/dist/sync/watcher.js.map +1 -1
  305. package/dist/sync/worktree.d.ts +54 -0
  306. package/dist/sync/worktree.d.ts.map +1 -0
  307. package/dist/sync/worktree.js +137 -0
  308. package/dist/sync/worktree.js.map +1 -0
  309. package/dist/types.d.ts +9 -1
  310. package/dist/types.d.ts.map +1 -1
  311. package/dist/types.js +14 -0
  312. package/dist/types.js.map +1 -1
  313. package/dist/utils.js +1 -1
  314. package/package.json +2 -2
  315. package/scripts/add-lang/bench.sh +60 -0
  316. package/scripts/add-lang/check-grammar.mjs +75 -0
  317. package/scripts/add-lang/dump-ast.mjs +103 -0
  318. package/scripts/add-lang/verify-extraction.mjs +70 -0
  319. package/scripts/agent-eval/arms-F.sh +21 -0
  320. package/scripts/agent-eval/arms-matrix.sh +37 -0
  321. package/scripts/agent-eval/bench-readme.sh +28 -0
  322. package/scripts/agent-eval/bench-why-repo.sh +22 -0
  323. package/scripts/agent-eval/block-read-hook.sh +19 -0
  324. package/scripts/agent-eval/hook-settings.json +15 -0
  325. package/scripts/agent-eval/itrun.sh +24 -11
  326. package/scripts/agent-eval/parse-arms.mjs +116 -0
  327. package/scripts/agent-eval/parse-bench-readme.mjs +84 -0
  328. package/scripts/agent-eval/probe-context.mjs +21 -0
  329. package/scripts/agent-eval/probe-explore.mjs +40 -0
  330. package/scripts/agent-eval/probe-node.mjs +20 -0
  331. package/scripts/agent-eval/probe-sweep.mjs +119 -0
  332. package/scripts/agent-eval/probe-trace.mjs +20 -0
  333. package/scripts/agent-eval/run-arms.sh +56 -0
  334. package/scripts/agent-eval/seq-matrix.mjs +137 -0
  335. package/scripts/build-bundle.sh +118 -0
  336. package/scripts/npm-sdk.js +75 -0
  337. package/scripts/npm-shim.js +246 -0
  338. package/scripts/pack-npm.sh +119 -0
  339. package/scripts/prepare-release.mjs +270 -0
  340. package/scripts/patch-tree-sitter-dart.js +0 -112
  341. package/scripts/release.sh +0 -68
@@ -41,9 +41,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
41
41
  return (mod && mod.__esModule) ? mod : { "default": mod };
42
42
  };
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
- exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.detectLanguage = exports.extractFromSource = exports.ExtractionOrchestrator = void 0;
44
+ exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.isSourceFile = exports.detectLanguage = exports.extractFromSource = exports.ExtractionOrchestrator = void 0;
45
45
  exports.hashContent = hashContent;
46
- exports.shouldIncludeFile = shouldIncludeFile;
46
+ exports.buildDefaultIgnore = buildDefaultIgnore;
47
47
  exports.scanDirectory = scanDirectory;
48
48
  exports.scanDirectoryAsync = scanDirectoryAsync;
49
49
  const fs = __importStar(require("fs"));
@@ -55,7 +55,7 @@ const tree_sitter_1 = require("./tree-sitter");
55
55
  const grammars_1 = require("./grammars");
56
56
  const errors_1 = require("../errors");
57
57
  const utils_1 = require("../utils");
58
- const picomatch_1 = __importDefault(require("picomatch"));
58
+ const ignore_1 = __importDefault(require("ignore"));
59
59
  const frameworks_1 = require("../resolution/frameworks");
60
60
  /**
61
61
  * Number of files to read in parallel during indexing.
@@ -84,29 +84,87 @@ function hashContent(content) {
84
84
  return crypto.createHash('sha256').update(content).digest('hex');
85
85
  }
86
86
  /**
87
- * Check if a path matches any glob pattern (simplified)
87
+ * Skip files larger than this (bytes). Generated bundles, minified JS, and
88
+ * vendored blobs blow the WASM heap and the worker-recycle budget for no useful
89
+ * symbols. 1 MB covers essentially all hand-written source.
88
90
  */
89
- function matchesGlob(filePath, pattern) {
90
- filePath = (0, utils_1.normalizePath)(filePath);
91
- return picomatch_1.default.isMatch(filePath, pattern, { dot: true });
92
- }
91
+ const MAX_FILE_SIZE = 1024 * 1024;
93
92
  /**
94
- * Check if a file should be included based on config
93
+ * Directory names that are dependency, build, cache, or tooling output across the
94
+ * languages/frameworks CodeGraph supports — curated from the canonical
95
+ * github/gitignore templates. Excluded by default so the graph reflects your code,
96
+ * not third-party noise, without requiring a `.gitignore` (issue #407). The
97
+ * exclusion applies uniformly (git or not, tracked or not); the only opt-in is an
98
+ * explicit `.gitignore` negation (e.g. `!vendor/`). First-party-prone or generic
99
+ * names (`packages`, `lib`, `app`, `bin`, `src`, `deps`, `env`, `tmp`, `storage`,
100
+ * `Library`) are deliberately NOT listed, to avoid ever hiding real source.
101
+ *
102
+ * Only dirs that actually contain *indexable source* (or are enormous) earn a slot
103
+ * — IDE/state dirs like `.idea`/`.vs` are omitted because CodeGraph indexes only
104
+ * recognized source extensions, so they produce no symbols regardless.
95
105
  */
96
- function shouldIncludeFile(filePath, config) {
97
- // Check exclude patterns first
98
- for (const pattern of config.exclude) {
99
- if (matchesGlob(filePath, pattern)) {
100
- return false;
101
- }
106
+ const DEFAULT_IGNORE_DIRS = new Set([
107
+ // JS / TS — dependency directories
108
+ 'node_modules', 'bower_components', 'jspm_packages', 'web_modules',
109
+ '.yarn', '.pnpm-store',
110
+ // JS / TS — framework & bundler build / cache / deploy output
111
+ '.next', '.nuxt', '.svelte-kit', '.turbo', '.vite', '.parcel-cache', '.angular',
112
+ '.docusaurus', 'storybook-static', '.vinxi', '.nitro', 'out-tsc',
113
+ '.vercel', '.netlify', '.wrangler',
114
+ // Build output (common across ecosystems)
115
+ 'dist', 'build', 'out', '.output',
116
+ // Test / coverage
117
+ 'coverage', '.nyc_output',
118
+ // Python
119
+ '__pycache__', '__pypackages__', '.venv', 'venv', '.pixi', '.pdm-build',
120
+ '.mypy_cache', '.pytest_cache', '.ruff_cache', '.tox', '.nox', '.hypothesis',
121
+ '.ipynb_checkpoints', '.eggs',
122
+ // Rust / JVM (Maven, Gradle, Scala)
123
+ 'target', '.gradle',
124
+ // .NET
125
+ 'obj',
126
+ // Vendored deps (Go, PHP/Composer, Ruby/Bundler)
127
+ 'vendor',
128
+ // Swift / iOS
129
+ '.build', 'Pods', 'Carthage', 'DerivedData', '.swiftpm',
130
+ // Dart / Flutter
131
+ '.dart_tool', '.pub-cache',
132
+ // Native (Android NDK, C/C++ deps)
133
+ '.cxx', '.externalNativeBuild', 'vcpkg_installed',
134
+ // Scala tooling
135
+ '.bloop', '.metals',
136
+ // Lua / Luau (LuaRocks)
137
+ 'lua_modules', '.luarocks',
138
+ // Delphi / RAD Studio IDE backups (duplicate .pas source — would double-count)
139
+ '__history', '__recovery',
140
+ // Generic cache
141
+ '.cache',
142
+ ]);
143
+ /** Gitignore-style patterns for the `ignore` matcher: the dirs above plus a few globs. */
144
+ const DEFAULT_IGNORE_PATTERNS = [
145
+ ...Array.from(DEFAULT_IGNORE_DIRS, (d) => `${d}/`),
146
+ '*.egg-info/', // Python packaging metadata
147
+ 'cmake-build-*/', // CLion / CMake build trees
148
+ 'bazel-*/', // Bazel output symlink trees
149
+ ];
150
+ /**
151
+ * An `ignore` matcher seeded with the built-in defaults, merged with the project's
152
+ * root .gitignore so a negation there (e.g. `!vendor/`) overrides a default. Shared
153
+ * by both enumeration paths so behavior is identical with or without git — and so
154
+ * the defaults apply to tracked files too (committing a dependency dir doesn't make
155
+ * it project code; the explicit `.gitignore` negation is the only opt-in).
156
+ */
157
+ function buildDefaultIgnore(rootDir) {
158
+ const ig = (0, ignore_1.default)().add(DEFAULT_IGNORE_PATTERNS);
159
+ try {
160
+ const rootGitignore = path.join(rootDir, '.gitignore');
161
+ if (fs.existsSync(rootGitignore))
162
+ ig.add(fs.readFileSync(rootGitignore, 'utf-8'));
102
163
  }
103
- // Check include patterns
104
- for (const pattern of config.include) {
105
- if (matchesGlob(filePath, pattern)) {
106
- return true;
107
- }
164
+ catch {
165
+ // Unreadable root .gitignore — the built-in defaults still apply.
108
166
  }
109
- return false;
167
+ return ig;
110
168
  }
111
169
  /**
112
170
  * Collect git-visible files (tracked + untracked, .gitignore-respected) from the
@@ -122,38 +180,39 @@ function shouldIncludeFile(filePath, config) {
122
180
  * (See issue #193.)
123
181
  */
124
182
  function collectGitFiles(repoDir, prefix, files) {
125
- const gitOpts = { cwd: repoDir, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] };
183
+ const gitOpts = { cwd: repoDir, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true };
126
184
  // Tracked files. --recurse-submodules pulls in files from active submodules,
127
185
  // which the index would otherwise represent only as a commit pointer.
128
186
  // Without this, monorepos using submodules index 0 files. (See issue #147.)
129
187
  // Note: --recurse-submodules only supports -c/--cached and --stage modes — it
130
188
  // can't be combined with -o, so untracked files are gathered separately below.
131
- const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-c', '--recurse-submodules'], gitOpts);
132
- for (const line of tracked.split('\n')) {
133
- const trimmed = line.trim();
134
- if (trimmed) {
135
- files.add((0, utils_1.normalizePath)(prefix + trimmed));
136
- }
189
+ // -z gives NUL-separated, unquoted output so non-ASCII (e.g. CJK) paths
190
+ // survive verbatim. Without it git octal-escapes and double-quotes such paths
191
+ // (the core.quotepath default), and the quoted form never matches a real file
192
+ // on disk → those files are silently dropped from the index. (#541)
193
+ const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-z', '-c', '--recurse-submodules'], gitOpts);
194
+ for (const rel of tracked.split('\0')) {
195
+ if (rel)
196
+ files.add((0, utils_1.normalizePath)(prefix + rel));
137
197
  }
138
198
  // Untracked files (submodules manage their own untracked state). Embedded git
139
199
  // repos surface here as a single "subdir/" entry that git refuses to descend
140
200
  // into — recurse into those as their own repos so their source gets indexed.
141
- const untracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-o', '--exclude-standard'], gitOpts);
142
- for (const line of untracked.split('\n')) {
143
- const trimmed = line.trim();
144
- if (!trimmed)
201
+ const untracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-z', '-o', '--exclude-standard'], gitOpts);
202
+ for (const rel of untracked.split('\0')) {
203
+ if (!rel)
145
204
  continue;
146
- if (trimmed.endsWith('/')) {
205
+ if (rel.endsWith('/')) {
147
206
  // git only emits a trailing-slash directory entry for an embedded repo.
148
207
  // Guard with a .git check anyway, and skip anything else exactly as git
149
208
  // itself skips it (we never descend into a non-repo opaque dir).
150
- const childDir = path.join(repoDir, trimmed);
209
+ const childDir = path.join(repoDir, rel);
151
210
  if (fs.existsSync(path.join(childDir, '.git'))) {
152
- collectGitFiles(childDir, prefix + trimmed, files);
211
+ collectGitFiles(childDir, prefix + rel, files);
153
212
  }
154
213
  continue;
155
214
  }
156
- files.add((0, utils_1.normalizePath)(prefix + trimmed));
215
+ files.add((0, utils_1.normalizePath)(prefix + rel));
157
216
  }
158
217
  }
159
218
  /**
@@ -167,11 +226,11 @@ function getGitVisibleFiles(rootDir) {
167
226
  // Check if the project directory is gitignored by a parent repo.
168
227
  // When rootDir lives inside a parent git repo that ignores it,
169
228
  // `git ls-files` returns nothing — fall back to filesystem walk.
170
- const gitRoot = (0, child_process_1.execFileSync)('git', ['rev-parse', '--show-toplevel'], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
229
+ 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();
171
230
  if (path.resolve(gitRoot) !== path.resolve(rootDir)) {
172
231
  try {
173
232
  // git check-ignore exits 0 if the path IS ignored, 1 if not
174
- (0, child_process_1.execFileSync)('git', ['check-ignore', '-q', path.resolve(rootDir)], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
233
+ (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 });
175
234
  // Directory is gitignored by parent repo — fall back to filesystem walk
176
235
  return null;
177
236
  }
@@ -181,7 +240,11 @@ function getGitVisibleFiles(rootDir) {
181
240
  }
182
241
  const files = new Set();
183
242
  collectGitFiles(rootDir, '', files);
184
- return files;
243
+ // Apply built-in default ignores uniformly — to tracked files too, since
244
+ // committing a dependency/build dir doesn't make it project code. A
245
+ // `.gitignore` negation (e.g. `!vendor/`) is the explicit opt-in. (issue #407)
246
+ const ig = buildDefaultIgnore(rootDir);
247
+ return new Set([...files].filter((f) => !ig.ignores(f)));
185
248
  }
186
249
  catch {
187
250
  return null;
@@ -191,9 +254,9 @@ function getGitVisibleFiles(rootDir) {
191
254
  * Use `git status` to detect changed files instead of scanning every file.
192
255
  * Returns null on failure so callers fall back to full scan.
193
256
  */
194
- function getGitChangedFiles(rootDir, config) {
257
+ function getGitChangedFiles(rootDir) {
195
258
  try {
196
- const output = (0, child_process_1.execFileSync)('git', ['status', '--porcelain', '--no-renames'], { cwd: rootDir, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] });
259
+ const output = (0, child_process_1.execFileSync)('git', ['status', '--porcelain', '--no-renames'], { cwd: rootDir, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
197
260
  const modified = [];
198
261
  const added = [];
199
262
  const deleted = [];
@@ -202,8 +265,8 @@ function getGitChangedFiles(rootDir, config) {
202
265
  continue; // Minimum: "XY file"
203
266
  const statusCode = line.substring(0, 2);
204
267
  const filePath = (0, utils_1.normalizePath)(line.substring(3));
205
- // Skip files that don't match include/exclude config
206
- if (!shouldIncludeFile(filePath, config))
268
+ // Skip non-source files (git status already omits .gitignored paths).
269
+ if (!(0, grammars_1.isSourceFile)(filePath))
207
270
  continue;
208
271
  if (statusCode === '??') {
209
272
  added.push(filePath);
@@ -223,24 +286,20 @@ function getGitChangedFiles(rootDir, config) {
223
286
  }
224
287
  }
225
288
  /**
226
- * Marker file name that indicates a directory (and all children) should be skipped
227
- */
228
- const CODEGRAPH_IGNORE_MARKER = '.codegraphignore';
229
- /**
230
- * Recursively scan directory for source files.
289
+ * Recursively scan a directory for source files.
231
290
  *
232
- * In git repos, uses `git ls-files` to get the file list (inherently
233
- * respects .gitignore at all levels), then filters by config include patterns.
234
- * Falls back to filesystem walk for non-git projects.
291
+ * In git repos, uses `git ls-files` (inherently respects .gitignore at all
292
+ * levels), then keeps files with a supported source extension. For non-git
293
+ * projects, falls back to a filesystem walk that parses .gitignore itself.
235
294
  */
236
- function scanDirectory(rootDir, config, onProgress) {
295
+ function scanDirectory(rootDir, onProgress) {
237
296
  // Fast path: use git to get all visible files (respects .gitignore everywhere)
238
297
  const gitFiles = getGitVisibleFiles(rootDir);
239
298
  if (gitFiles) {
240
299
  const files = [];
241
300
  let count = 0;
242
301
  for (const filePath of gitFiles) {
243
- if (shouldIncludeFile(filePath, config)) {
302
+ if ((0, grammars_1.isSourceFile)(filePath)) {
244
303
  files.push(filePath);
245
304
  count++;
246
305
  onProgress?.(count, filePath);
@@ -249,19 +308,19 @@ function scanDirectory(rootDir, config, onProgress) {
249
308
  return files;
250
309
  }
251
310
  // Fallback: walk filesystem for non-git projects
252
- return scanDirectoryWalk(rootDir, config, onProgress);
311
+ return scanDirectoryWalk(rootDir, onProgress);
253
312
  }
254
313
  /**
255
314
  * Async variant of scanDirectory that yields to the event loop periodically,
256
315
  * allowing worker threads to receive and render progress messages.
257
316
  */
258
- async function scanDirectoryAsync(rootDir, config, onProgress) {
317
+ async function scanDirectoryAsync(rootDir, onProgress) {
259
318
  const gitFiles = getGitVisibleFiles(rootDir);
260
319
  if (gitFiles) {
261
320
  const files = [];
262
321
  let count = 0;
263
322
  for (const filePath of gitFiles) {
264
- if (shouldIncludeFile(filePath, config)) {
323
+ if ((0, grammars_1.isSourceFile)(filePath)) {
265
324
  files.push(filePath);
266
325
  count++;
267
326
  onProgress?.(count, filePath);
@@ -273,16 +332,40 @@ async function scanDirectoryAsync(rootDir, config, onProgress) {
273
332
  }
274
333
  return files;
275
334
  }
276
- return scanDirectoryWalk(rootDir, config, onProgress);
335
+ return scanDirectoryWalk(rootDir, onProgress);
277
336
  }
278
337
  /**
279
338
  * Filesystem walk fallback for non-git projects.
280
339
  */
281
- function scanDirectoryWalk(rootDir, config, onProgress) {
340
+ function scanDirectoryWalk(rootDir, onProgress) {
282
341
  const files = [];
283
342
  let count = 0;
284
343
  const visitedDirs = new Set();
285
- function walk(dir) {
344
+ const loadIgnore = (dir) => {
345
+ try {
346
+ const giPath = path.join(dir, '.gitignore');
347
+ if (fs.existsSync(giPath)) {
348
+ return { dir, ig: (0, ignore_1.default)().add(fs.readFileSync(giPath, 'utf-8')) };
349
+ }
350
+ }
351
+ catch {
352
+ // Unreadable .gitignore — treat as absent.
353
+ }
354
+ return null;
355
+ };
356
+ const isIgnored = (fullPath, isDir, matchers) => {
357
+ for (const { dir, ig } of matchers) {
358
+ let rel = (0, utils_1.normalizePath)(path.relative(dir, fullPath));
359
+ if (!rel || rel.startsWith('..'))
360
+ continue; // not under this matcher's dir
361
+ if (isDir)
362
+ rel += '/'; // dir-only rules (e.g. `build/`) only match with the slash
363
+ if (ig.ignores(rel))
364
+ return true;
365
+ }
366
+ return false;
367
+ };
368
+ function walk(dir, matchers) {
286
369
  let realDir;
287
370
  try {
288
371
  realDir = fs.realpathSync(dir);
@@ -296,12 +379,11 @@ function scanDirectoryWalk(rootDir, config, onProgress) {
296
379
  return;
297
380
  }
298
381
  visitedDirs.add(realDir);
299
- // Check for .codegraphignore marker file
300
- const ignoreMarker = path.join(dir, CODEGRAPH_IGNORE_MARKER);
301
- if (fs.existsSync(ignoreMarker)) {
302
- (0, errors_1.logDebug)('Skipping directory due to .codegraphignore marker', { dir });
303
- return;
304
- }
382
+ // This directory's own .gitignore (if present) applies to everything below it.
383
+ // The root's .gitignore is already merged into the seeded base matcher (so a
384
+ // negation there can override a built-in default), so skip it here.
385
+ const own = dir === rootDir ? null : loadIgnore(dir);
386
+ const active = own ? [...matchers, own] : matchers;
305
387
  let entries;
306
388
  try {
307
389
  entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -311,6 +393,9 @@ function scanDirectoryWalk(rootDir, config, onProgress) {
311
393
  return;
312
394
  }
313
395
  for (const entry of entries) {
396
+ // Never descend into git internals or our own data directory.
397
+ if (entry.name === '.git' || entry.name === '.codegraph')
398
+ continue;
314
399
  const fullPath = path.join(dir, entry.name);
315
400
  const relativePath = (0, utils_1.normalizePath)(path.relative(rootDir, fullPath));
316
401
  if (entry.isSymbolicLink()) {
@@ -318,20 +403,12 @@ function scanDirectoryWalk(rootDir, config, onProgress) {
318
403
  const realTarget = fs.realpathSync(fullPath);
319
404
  const stat = fs.statSync(realTarget);
320
405
  if (stat.isDirectory()) {
321
- const dirPattern = relativePath + '/';
322
- let excluded = false;
323
- for (const pattern of config.exclude) {
324
- if (matchesGlob(dirPattern, pattern) || matchesGlob(relativePath, pattern)) {
325
- excluded = true;
326
- break;
327
- }
328
- }
329
- if (!excluded) {
330
- walk(fullPath);
406
+ if (!isIgnored(fullPath, true, active)) {
407
+ walk(fullPath, active);
331
408
  }
332
409
  }
333
410
  else if (stat.isFile()) {
334
- if (shouldIncludeFile(relativePath, config)) {
411
+ if (!isIgnored(fullPath, false, active) && (0, grammars_1.isSourceFile)(relativePath)) {
335
412
  files.push(relativePath);
336
413
  count++;
337
414
  onProgress?.(count, relativePath);
@@ -344,20 +421,12 @@ function scanDirectoryWalk(rootDir, config, onProgress) {
344
421
  continue;
345
422
  }
346
423
  if (entry.isDirectory()) {
347
- const dirPattern = relativePath + '/';
348
- let excluded = false;
349
- for (const pattern of config.exclude) {
350
- if (matchesGlob(dirPattern, pattern) || matchesGlob(relativePath, pattern)) {
351
- excluded = true;
352
- break;
353
- }
354
- }
355
- if (!excluded) {
356
- walk(fullPath);
424
+ if (!isIgnored(fullPath, true, active)) {
425
+ walk(fullPath, active);
357
426
  }
358
427
  }
359
428
  else if (entry.isFile()) {
360
- if (shouldIncludeFile(relativePath, config)) {
429
+ if (!isIgnored(fullPath, false, active) && (0, grammars_1.isSourceFile)(relativePath)) {
361
430
  files.push(relativePath);
362
431
  count++;
363
432
  onProgress?.(count, relativePath);
@@ -365,7 +434,9 @@ function scanDirectoryWalk(rootDir, config, onProgress) {
365
434
  }
366
435
  }
367
436
  }
368
- walk(rootDir);
437
+ // Seed a base matcher with the built-in default ignores (merged with the root
438
+ // .gitignore so a negation can override). Nested .gitignores still layer per-dir.
439
+ walk(rootDir, [{ dir: rootDir, ig: buildDefaultIgnore(rootDir) }]);
369
440
  return files;
370
441
  }
371
442
  /**
@@ -373,7 +444,6 @@ function scanDirectoryWalk(rootDir, config, onProgress) {
373
444
  */
374
445
  class ExtractionOrchestrator {
375
446
  rootDir;
376
- config;
377
447
  queries;
378
448
  /**
379
449
  * Names of frameworks detected for this project, populated by indexAll().
@@ -382,9 +452,8 @@ class ExtractionOrchestrator {
382
452
  * hasn't run yet so single-file re-index paths can detect on the spot.
383
453
  */
384
454
  detectedFrameworkNames = null;
385
- constructor(rootDir, config, queries) {
455
+ constructor(rootDir, queries) {
386
456
  this.rootDir = rootDir;
387
- this.config = config;
388
457
  this.queries = queries;
389
458
  }
390
459
  /**
@@ -426,6 +495,24 @@ class ExtractionOrchestrator {
426
495
  return null;
427
496
  }
428
497
  },
498
+ // Monorepo support — needed by framework detect()s that probe
499
+ // subpackage manifests (e.g. fabric-view looking at
500
+ // packages/<sub>/package.json when the root manifest is just a
501
+ // workspace declaration). Matches the resolver-context shape.
502
+ listDirectories: (relativePath) => {
503
+ const target = relativePath === '.' || relativePath === ''
504
+ ? rootDir
505
+ : path.join(rootDir, relativePath);
506
+ try {
507
+ return fs
508
+ .readdirSync(target, { withFileTypes: true })
509
+ .filter((entry) => entry.isDirectory())
510
+ .map((entry) => entry.name);
511
+ }
512
+ catch {
513
+ return [];
514
+ }
515
+ },
429
516
  };
430
517
  }
431
518
  /**
@@ -436,7 +523,7 @@ class ExtractionOrchestrator {
436
523
  ensureDetectedFrameworks(files) {
437
524
  if (this.detectedFrameworkNames !== null)
438
525
  return this.detectedFrameworkNames;
439
- const fileList = files ?? scanDirectory(this.rootDir, this.config);
526
+ const fileList = files ?? scanDirectory(this.rootDir);
440
527
  const context = this.buildDetectionContext(fileList);
441
528
  this.detectedFrameworkNames = (0, frameworks_1.detectFrameworks)(context).map((r) => r.name);
442
529
  return this.detectedFrameworkNames;
@@ -462,7 +549,7 @@ class ExtractionOrchestrator {
462
549
  current: 0,
463
550
  total: 0,
464
551
  });
465
- const files = await scanDirectoryAsync(this.rootDir, this.config, (current, file) => {
552
+ const files = await scanDirectoryAsync(this.rootDir, (current, file) => {
466
553
  onProgress?.({
467
554
  phase: 'scanning',
468
555
  current,
@@ -697,18 +784,16 @@ class ExtractionOrchestrator {
697
784
  });
698
785
  continue;
699
786
  }
700
- // Honour config.maxFileSize. Without this check, vendored
701
- // generated headers, minified bundles, and other multi-MB
702
- // files get indexed despite the user setting a size cap
703
- // wasting WASM heap and the worker recycle budget on inputs
704
- // the user explicitly opted out of. The single-file extractFile
705
- // path already enforces this; the bulk path used to silently
706
- // skip the check.
707
- if (stats.size > this.config.maxFileSize) {
787
+ // Honour MAX_FILE_SIZE. Without this check, vendored generated
788
+ // headers, minified bundles, and other multi-MB files get indexed,
789
+ // wasting WASM heap and the worker recycle budget on inputs with no
790
+ // useful symbols. The single-file extractFile path already enforces
791
+ // this; the bulk path used to silently skip the check.
792
+ if (stats.size > MAX_FILE_SIZE) {
708
793
  processed++;
709
794
  filesSkipped++;
710
795
  errors.push({
711
- message: `File exceeds max size (${stats.size} > ${this.config.maxFileSize})`,
796
+ message: `File exceeds max size (${stats.size} > ${MAX_FILE_SIZE})`,
712
797
  filePath,
713
798
  severity: 'warning',
714
799
  code: 'size_exceeded',
@@ -755,7 +840,16 @@ class ExtractionOrchestrator {
755
840
  filesErrored++;
756
841
  }
757
842
  else {
758
- filesSkipped++;
843
+ // Files with no symbols but no errors (yaml, twig, properties) are
844
+ // tracked at the file level — count them as indexed so the CLI
845
+ // doesn't misleadingly report "No files found to index".
846
+ const lang = (0, grammars_1.detectLanguage)(filePath, content);
847
+ if ((0, grammars_1.isFileLevelOnlyLanguage)(lang)) {
848
+ filesIndexed++;
849
+ }
850
+ else {
851
+ filesSkipped++;
852
+ }
759
853
  }
760
854
  }
761
855
  }
@@ -906,7 +1000,13 @@ class ExtractionOrchestrator {
906
1000
  filesErrored++;
907
1001
  }
908
1002
  else {
909
- filesSkipped++;
1003
+ const tracked = this.queries.getFileByPath(filePath);
1004
+ if (tracked && (0, grammars_1.isFileLevelOnlyLanguage)(tracked.language)) {
1005
+ filesIndexed++;
1006
+ }
1007
+ else {
1008
+ filesSkipped++;
1009
+ }
910
1010
  }
911
1011
  }
912
1012
  return {
@@ -977,14 +1077,14 @@ class ExtractionOrchestrator {
977
1077
  };
978
1078
  }
979
1079
  // Check file size
980
- if (stats.size > this.config.maxFileSize) {
1080
+ if (stats.size > MAX_FILE_SIZE) {
981
1081
  return {
982
1082
  nodes: [],
983
1083
  edges: [],
984
1084
  unresolvedReferences: [],
985
1085
  errors: [
986
1086
  {
987
- message: `File exceeds max size (${stats.size} > ${this.config.maxFileSize})`,
1087
+ message: `File exceeds max size (${stats.size} > ${MAX_FILE_SIZE})`,
988
1088
  filePath: relativePath,
989
1089
  severity: 'warning',
990
1090
  code: 'size_exceeded',
@@ -1073,8 +1173,12 @@ class ExtractionOrchestrator {
1073
1173
  this.queries.upsertFile(fileRecord);
1074
1174
  }
1075
1175
  /**
1076
- * Sync with current file state.
1077
- * Uses git status as a fast path when available, falling back to full scan.
1176
+ * Sync the index with the current file state.
1177
+ *
1178
+ * Change detection is filesystem-based, never git: a (size, mtime) stat
1179
+ * pre-filter skips unchanged files, then a content-hash compare confirms real
1180
+ * changes. This works in non-git projects and catches committed changes from
1181
+ * `git pull`/`checkout`/`merge`/`rebase` that `git status` cannot see.
1078
1182
  */
1079
1183
  async sync(onProgress) {
1080
1184
  await (0, grammars_1.initGrammars)(); // Initialize WASM runtime (grammars loaded lazily below)
@@ -1091,88 +1195,72 @@ class ExtractionOrchestrator {
1091
1195
  total: 0,
1092
1196
  });
1093
1197
  const filesToIndex = [];
1094
- const gitChanges = getGitChangedFiles(this.rootDir, this.config);
1095
- if (gitChanges) {
1096
- // === Git fast path ===
1097
- // Only inspect the files git reports as changed instead of scanning everything.
1098
- filesChecked = gitChanges.modified.length + gitChanges.added.length + gitChanges.deleted.length;
1099
- // Handle deleted files
1100
- for (const filePath of gitChanges.deleted) {
1101
- const tracked = this.queries.getFileByPath(filePath);
1102
- if (tracked) {
1103
- this.queries.deleteFile(filePath);
1104
- filesRemoved++;
1105
- }
1198
+ // === Filesystem reconcile (git-independent) ===
1199
+ // The source of truth for "what changed" is the filesystem vs the indexed
1200
+ // state never git. We enumerate the current source files and reconcile
1201
+ // each against the DB. A cheap (size, mtime) stat pre-filter skips unchanged
1202
+ // files without reading or hashing them, so the expensive read+hash+parse
1203
+ // only runs for files that actually changed. This catches edits/adds/deletes
1204
+ // whether or not the project uses git, and crucially also catches committed
1205
+ // changes from `git pull`/`checkout`/`merge`/`rebase` — which `git status`
1206
+ // cannot see, because the working tree is clean afterward.
1207
+ const currentFiles = scanDirectory(this.rootDir);
1208
+ filesChecked = currentFiles.length;
1209
+ const currentSet = new Set(currentFiles);
1210
+ const trackedFiles = this.queries.getAllFiles();
1211
+ const trackedMap = new Map();
1212
+ for (const f of trackedFiles) {
1213
+ trackedMap.set(f.path, f);
1214
+ }
1215
+ // Removals: tracked in the DB but no longer a present source file. Check the
1216
+ // filesystem directly — `scanDirectory` (via `git ls-files`) still lists a
1217
+ // file deleted from disk but not yet staged, so set membership alone misses it.
1218
+ for (const tracked of trackedFiles) {
1219
+ if (!currentSet.has(tracked.path) || !fs.existsSync(path.join(this.rootDir, tracked.path))) {
1220
+ this.queries.deleteFile(tracked.path);
1221
+ filesRemoved++;
1106
1222
  }
1107
- // Handle modified + added files — read + hash only these. Untracked
1108
- // (`??`) files stay untracked in git even after we index them, so they
1109
- // can't be trusted as "new": re-hash and compare against the DB exactly
1110
- // like modified files. Otherwise every sync re-indexes them and status
1111
- // reports them as pending forever. (See issue #206.)
1112
- for (const filePath of [...gitChanges.modified, ...gitChanges.added]) {
1113
- const fullPath = path.join(this.rootDir, filePath);
1114
- let content;
1223
+ }
1224
+ // Adds / modifications.
1225
+ for (const filePath of currentFiles) {
1226
+ const fullPath = path.join(this.rootDir, filePath);
1227
+ const tracked = trackedMap.get(filePath);
1228
+ // Cheap pre-filter: an already-indexed file whose size AND mtime both match
1229
+ // the DB is unchanged — skip it without reading or hashing. (A content
1230
+ // change that preserves both exactly is the blind spot every mtime-based
1231
+ // incremental tool accepts; `index --force` is the escape hatch. Git bumps
1232
+ // mtime on every file it writes during checkout/merge, so pulls are caught.)
1233
+ if (tracked) {
1115
1234
  try {
1116
- content = fs.readFileSync(fullPath, 'utf-8');
1235
+ const stat = fs.statSync(fullPath);
1236
+ if (stat.size === tracked.size && Math.floor(stat.mtimeMs) === Math.floor(tracked.modifiedAt)) {
1237
+ continue;
1238
+ }
1117
1239
  }
1118
1240
  catch (error) {
1119
- (0, errors_1.logDebug)('Skipping unreadable file during sync', { filePath, error: String(error) });
1241
+ (0, errors_1.logDebug)('Skipping unstattable file during sync', { filePath, error: String(error) });
1120
1242
  continue;
1121
1243
  }
1122
- const contentHash = hashContent(content);
1123
- const tracked = this.queries.getFileByPath(filePath);
1124
- if (!tracked) {
1125
- filesToIndex.push(filePath);
1126
- changedFilePaths.push(filePath);
1127
- filesAdded++;
1128
- }
1129
- else if (tracked.contentHash !== contentHash) {
1130
- filesToIndex.push(filePath);
1131
- changedFilePaths.push(filePath);
1132
- filesModified++;
1133
- }
1134
1244
  }
1135
- }
1136
- else {
1137
- // === Fallback: full scan (non-git project or git failure) ===
1138
- const currentFiles = new Set(scanDirectory(this.rootDir, this.config));
1139
- filesChecked = currentFiles.size;
1140
- // Build Map for O(1) lookups instead of .find() per file
1141
- const trackedFiles = this.queries.getAllFiles();
1142
- const trackedMap = new Map();
1143
- for (const f of trackedFiles) {
1144
- trackedMap.set(f.path, f);
1245
+ // New, or size/mtime changed — read + hash to confirm a real content change.
1246
+ let content;
1247
+ try {
1248
+ content = fs.readFileSync(fullPath, 'utf-8');
1145
1249
  }
1146
- // Find files to remove (in DB but not on disk)
1147
- for (const tracked of trackedFiles) {
1148
- if (!currentFiles.has(tracked.path)) {
1149
- this.queries.deleteFile(tracked.path);
1150
- filesRemoved++;
1151
- }
1250
+ catch (error) {
1251
+ (0, errors_1.logDebug)('Skipping unreadable file during sync', { filePath, error: String(error) });
1252
+ continue;
1152
1253
  }
1153
- // Find files to add or update
1154
- for (const filePath of currentFiles) {
1155
- const fullPath = path.join(this.rootDir, filePath);
1156
- let content;
1157
- try {
1158
- content = fs.readFileSync(fullPath, 'utf-8');
1159
- }
1160
- catch (error) {
1161
- (0, errors_1.logDebug)('Skipping unreadable file during sync', { filePath, error: String(error) });
1162
- continue;
1163
- }
1164
- const contentHash = hashContent(content);
1165
- const tracked = trackedMap.get(filePath);
1166
- if (!tracked) {
1167
- filesToIndex.push(filePath);
1168
- changedFilePaths.push(filePath);
1169
- filesAdded++;
1170
- }
1171
- else if (tracked.contentHash !== contentHash) {
1172
- filesToIndex.push(filePath);
1173
- changedFilePaths.push(filePath);
1174
- filesModified++;
1175
- }
1254
+ const contentHash = hashContent(content);
1255
+ if (!tracked) {
1256
+ filesToIndex.push(filePath);
1257
+ changedFilePaths.push(filePath);
1258
+ filesAdded++;
1259
+ }
1260
+ else if (tracked.contentHash !== contentHash) {
1261
+ filesToIndex.push(filePath);
1262
+ changedFilePaths.push(filePath);
1263
+ filesModified++;
1176
1264
  }
1177
1265
  }
1178
1266
  // Load only grammars needed for changed files
@@ -1212,7 +1300,7 @@ class ExtractionOrchestrator {
1212
1300
  * Uses git status as a fast path when available, falling back to full scan.
1213
1301
  */
1214
1302
  getChangedFiles() {
1215
- const gitChanges = getGitChangedFiles(this.rootDir, this.config);
1303
+ const gitChanges = getGitChangedFiles(this.rootDir);
1216
1304
  if (gitChanges) {
1217
1305
  // === Git fast path ===
1218
1306
  const added = [];
@@ -1251,7 +1339,7 @@ class ExtractionOrchestrator {
1251
1339
  return { added, modified, removed };
1252
1340
  }
1253
1341
  // === Fallback: full scan (non-git project or git failure) ===
1254
- const currentFiles = new Set(scanDirectory(this.rootDir, this.config));
1342
+ const currentFiles = new Set(scanDirectory(this.rootDir));
1255
1343
  const trackedFiles = this.queries.getAllFiles();
1256
1344
  // Build Map for O(1) lookups
1257
1345
  const trackedMap = new Map();
@@ -1296,6 +1384,7 @@ var tree_sitter_2 = require("./tree-sitter");
1296
1384
  Object.defineProperty(exports, "extractFromSource", { enumerable: true, get: function () { return tree_sitter_2.extractFromSource; } });
1297
1385
  var grammars_2 = require("./grammars");
1298
1386
  Object.defineProperty(exports, "detectLanguage", { enumerable: true, get: function () { return grammars_2.detectLanguage; } });
1387
+ Object.defineProperty(exports, "isSourceFile", { enumerable: true, get: function () { return grammars_2.isSourceFile; } });
1299
1388
  Object.defineProperty(exports, "isLanguageSupported", { enumerable: true, get: function () { return grammars_2.isLanguageSupported; } });
1300
1389
  Object.defineProperty(exports, "isGrammarLoaded", { enumerable: true, get: function () { return grammars_2.isGrammarLoaded; } });
1301
1390
  Object.defineProperty(exports, "getSupportedLanguages", { enumerable: true, get: function () { return grammars_2.getSupportedLanguages; } });