scip-query 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +16 -43
  2. package/dist/chunk-26DOJ63W.js +161 -0
  3. package/dist/chunk-2UELLEBI.js +1 -0
  4. package/dist/{chunk-4PDAL6IL.js → chunk-4JCSOF2O.js} +3 -3
  5. package/dist/{chunk-6SXADWLW.js → chunk-5OMVSV6E.js} +13 -5
  6. package/dist/{chunk-KPPHZCZJ.js → chunk-7HK5ZLOE.js} +28 -46
  7. package/dist/{chunk-7RLE5EWE.js → chunk-7KIMF5PV.js} +34 -13
  8. package/dist/chunk-AXQKUYKF.js +1442 -0
  9. package/dist/chunk-C7H5WBTJ.js +46 -0
  10. package/dist/{chunk-NHBZIL2J.js → chunk-CHDJXYBG.js} +3 -28
  11. package/dist/{chunk-KCBMVQL5.js → chunk-CPVAQJEC.js} +13 -5
  12. package/dist/{chunk-ZQIIPFD7.js → chunk-DH7G3DDV.js} +2 -2
  13. package/dist/{chunk-BOVXCR46.js → chunk-EOROMIFO.js} +14 -6
  14. package/dist/{chunk-2CKGIR6G.js → chunk-EPWLXXBL.js} +3 -3
  15. package/dist/{chunk-5RMYT5WH.js → chunk-F7XU27LU.js} +2 -2
  16. package/dist/{chunk-63G7IQTD.js → chunk-FYYOWQXK.js} +20 -40
  17. package/dist/chunk-GEXE2T6I.js +87 -0
  18. package/dist/{chunk-DGUPQSOR.js → chunk-GJDHTTR2.js} +11 -4
  19. package/dist/chunk-GSH2FPKV.js +87 -0
  20. package/dist/{chunk-NUZ4OMU3.js → chunk-GU2H5QRN.js} +2 -2
  21. package/dist/{chunk-Z6YZJ36C.js → chunk-HJZUSUPU.js} +8 -4
  22. package/dist/{chunk-LAWMH22O.js → chunk-HLKAFWWJ.js} +82 -3
  23. package/dist/{chunk-HPFZLISB.js → chunk-HLUS2HEB.js} +2 -2
  24. package/dist/{chunk-H2MDONBU.js → chunk-J3JSOSUO.js} +9 -6
  25. package/dist/{chunk-7PBOG4YE.js → chunk-KBOQX573.js} +2 -2
  26. package/dist/{chunk-HDSRORNV.js → chunk-KKCHYLVI.js} +17 -11
  27. package/dist/{chunk-HMLMH7VZ.js → chunk-LFJQVJYJ.js} +2 -2
  28. package/dist/{chunk-4EXL2CUA.js → chunk-LQJUPXQY.js} +16 -8
  29. package/dist/{chunk-ITZ3DDOG.js → chunk-MPGIHELS.js} +18 -3
  30. package/dist/chunk-NFS5W3PP.js +37 -0
  31. package/dist/chunk-NG5F43OU.js +200 -0
  32. package/dist/{chunk-DCKMSTJ4.js → chunk-O7Q7FDUJ.js} +22 -14
  33. package/dist/{chunk-UJQN5N3I.js → chunk-OIDHN6GD.js} +6 -3
  34. package/dist/{chunk-Z4GHE2HD.js → chunk-P3E6L7KW.js} +6 -2
  35. package/dist/{chunk-BNN2RKD2.js → chunk-P4WO3BBW.js} +3 -3
  36. package/dist/{chunk-QOV2R2WT.js → chunk-QIXNAB5K.js} +42 -2
  37. package/dist/{chunk-NWCE4CIC.js → chunk-SMDCNPMK.js} +11 -28
  38. package/dist/{chunk-SEFSL2GF.js → chunk-TOIEB3LG.js} +2 -2
  39. package/dist/{chunk-4XHWPRAX.js → chunk-UGQKAVCD.js} +3 -3
  40. package/dist/{chunk-OVPLOMPY.js → chunk-UQEQ6AHX.js} +7 -4
  41. package/dist/{chunk-ZK6GXM3J.js → chunk-VIYSWZCO.js} +3 -3
  42. package/dist/chunk-VJJKSGIX.js +121 -0
  43. package/dist/{chunk-N5KEREIA.js → chunk-VT4JBH6L.js} +19 -7
  44. package/dist/{chunk-7LLPRPR5.js → chunk-WGAD3GNR.js} +2 -2
  45. package/dist/chunk-YDBXNPYU.js +69 -0
  46. package/dist/chunk-YY4QGUQ5.js +84 -0
  47. package/dist/{chunk-FGXRVW7G.js → chunk-YZ6L7GFO.js} +2 -2
  48. package/dist/{chunk-W4ALF422.js → chunk-ZEUCXQBN.js} +3 -3
  49. package/dist/cli.js +2697 -1054
  50. package/dist/{db-BNVVZSfP.d.ts → db-ShvwGDKf.d.ts} +12 -19
  51. package/dist/index.d.ts +15 -15
  52. package/dist/index.js +263 -234
  53. package/dist/postinstall.js +5 -76
  54. package/dist/queries/affected.d.ts +1 -1
  55. package/dist/queries/affected.js +3 -3
  56. package/dist/queries/bottlenecks.d.ts +1 -1
  57. package/dist/queries/bottlenecks.js +2 -2
  58. package/dist/queries/by-kind.d.ts +1 -1
  59. package/dist/queries/by-kind.js +2 -2
  60. package/dist/queries/call-graph.d.ts +1 -1
  61. package/dist/queries/call-graph.js +3 -3
  62. package/dist/queries/change-surface.d.ts +2 -2
  63. package/dist/queries/change-surface.js +3 -3
  64. package/dist/queries/code.d.ts +1 -1
  65. package/dist/queries/code.js +3 -3
  66. package/dist/queries/complexity-hotspots.d.ts +1 -1
  67. package/dist/queries/complexity-hotspots.js +3 -3
  68. package/dist/queries/complexity.d.ts +1 -1
  69. package/dist/queries/complexity.js +3 -3
  70. package/dist/queries/convergence.d.ts +1 -1
  71. package/dist/queries/convergence.js +3 -3
  72. package/dist/queries/coupling.d.ts +1 -1
  73. package/dist/queries/coupling.js +3 -1
  74. package/dist/queries/cycles.d.ts +1 -1
  75. package/dist/queries/cycles.js +3 -2
  76. package/dist/queries/dataflow.d.ts +1 -1
  77. package/dist/queries/dataflow.js +3 -3
  78. package/dist/queries/dead.d.ts +1 -1
  79. package/dist/queries/dead.js +4 -3
  80. package/dist/queries/deep-chains.d.ts +1 -1
  81. package/dist/queries/deep-chains.js +3 -2
  82. package/dist/queries/deps.d.ts +1 -1
  83. package/dist/queries/deps.js +3 -1
  84. package/dist/queries/diff-impact.d.ts +2 -2
  85. package/dist/queries/diff-impact.js +2 -3
  86. package/dist/queries/doc-coverage.d.ts +1 -1
  87. package/dist/queries/doc-coverage.js +2 -2
  88. package/dist/queries/drift.d.ts +1 -1
  89. package/dist/queries/drift.js +3 -2
  90. package/dist/queries/extract-candidates.d.ts +1 -1
  91. package/dist/queries/extract-candidates.js +3 -3
  92. package/dist/queries/fan.d.ts +1 -1
  93. package/dist/queries/fan.js +3 -2
  94. package/dist/queries/files.d.ts +1 -1
  95. package/dist/queries/health.d.ts +1 -1
  96. package/dist/queries/health.js +14 -14
  97. package/dist/queries/hierarchy.d.ts +1 -1
  98. package/dist/queries/hierarchy.js +3 -2
  99. package/dist/queries/hotspots.d.ts +1 -1
  100. package/dist/queries/hotspots.js +2 -2
  101. package/dist/queries/imports.d.ts +1 -1
  102. package/dist/queries/imports.js +3 -2
  103. package/dist/queries/index.d.ts +1 -2
  104. package/dist/queries/index.js +46 -51
  105. package/dist/queries/isolated.d.ts +1 -1
  106. package/dist/queries/isolated.js +4 -3
  107. package/dist/queries/members.d.ts +2 -2
  108. package/dist/queries/members.js +3 -2
  109. package/dist/queries/methods.d.ts +1 -1
  110. package/dist/queries/methods.js +2 -2
  111. package/dist/queries/outline.d.ts +1 -1
  112. package/dist/queries/outline.js +3 -2
  113. package/dist/queries/passthrough-candidates.d.ts +1 -1
  114. package/dist/queries/passthrough-candidates.js +3 -3
  115. package/dist/queries/redundant-reexports.d.ts +1 -1
  116. package/dist/queries/redundant-reexports.js +4 -2
  117. package/dist/queries/refs.d.ts +1 -1
  118. package/dist/queries/refs.js +3 -1
  119. package/dist/queries/similar-chains.d.ts +1 -1
  120. package/dist/queries/similar-chains.js +3 -2
  121. package/dist/queries/similar-files.d.ts +1 -1
  122. package/dist/queries/similar-files.js +3 -2
  123. package/dist/queries/similar-signatures.d.ts +1 -1
  124. package/dist/queries/similar-signatures.js +2 -2
  125. package/dist/queries/similar.d.ts +1 -1
  126. package/dist/queries/similar.js +3 -3
  127. package/dist/queries/slice.d.ts +2 -2
  128. package/dist/queries/slice.js +3 -3
  129. package/dist/queries/stale-abstractions.d.ts +1 -1
  130. package/dist/queries/stale-abstractions.js +3 -3
  131. package/dist/queries/stats.d.ts +1 -1
  132. package/dist/queries/surface.d.ts +1 -1
  133. package/dist/queries/surface.js +3 -2
  134. package/dist/queries/symbols.d.ts +1 -1
  135. package/dist/queries/symbols.js +3 -2
  136. package/dist/queries/system.d.ts +1 -1
  137. package/dist/queries/system.js +3 -2
  138. package/dist/queries/trace.d.ts +1 -1
  139. package/dist/queries/trace.js +3 -1
  140. package/dist/queries/wrapper-candidates.d.ts +1 -1
  141. package/dist/queries/wrapper-candidates.js +3 -3
  142. package/dist/reindex-worker.js +24 -12
  143. package/package.json +6 -1
  144. package/IMPROVEMENTS.md +0 -143
  145. package/PLAN.md +0 -320
  146. package/dist/chunk-2CKGIR6G.js.map +0 -1
  147. package/dist/chunk-3UOUTZQT.js +0 -45
  148. package/dist/chunk-3UOUTZQT.js.map +0 -1
  149. package/dist/chunk-4EXL2CUA.js.map +0 -1
  150. package/dist/chunk-4PDAL6IL.js.map +0 -1
  151. package/dist/chunk-4TYLS5XX.js.map +0 -1
  152. package/dist/chunk-4XHWPRAX.js.map +0 -1
  153. package/dist/chunk-5RMYT5WH.js.map +0 -1
  154. package/dist/chunk-63G7IQTD.js.map +0 -1
  155. package/dist/chunk-6SXADWLW.js.map +0 -1
  156. package/dist/chunk-74RFWB5T.js.map +0 -1
  157. package/dist/chunk-7LLPRPR5.js.map +0 -1
  158. package/dist/chunk-7PBOG4YE.js.map +0 -1
  159. package/dist/chunk-7RLE5EWE.js.map +0 -1
  160. package/dist/chunk-7UCKSQRS.js +0 -55
  161. package/dist/chunk-7UCKSQRS.js.map +0 -1
  162. package/dist/chunk-BNN2RKD2.js.map +0 -1
  163. package/dist/chunk-BOVXCR46.js.map +0 -1
  164. package/dist/chunk-D567NFIF.js +0 -65
  165. package/dist/chunk-D567NFIF.js.map +0 -1
  166. package/dist/chunk-DCKMSTJ4.js.map +0 -1
  167. package/dist/chunk-DEZKCZXD.js +0 -40
  168. package/dist/chunk-DEZKCZXD.js.map +0 -1
  169. package/dist/chunk-DGUPQSOR.js.map +0 -1
  170. package/dist/chunk-DVWGWHFW.js +0 -99
  171. package/dist/chunk-DVWGWHFW.js.map +0 -1
  172. package/dist/chunk-EQYLEQCW.js +0 -46
  173. package/dist/chunk-EQYLEQCW.js.map +0 -1
  174. package/dist/chunk-FGXRVW7G.js.map +0 -1
  175. package/dist/chunk-H2MDONBU.js.map +0 -1
  176. package/dist/chunk-HB7MRLLL.js +0 -76
  177. package/dist/chunk-HB7MRLLL.js.map +0 -1
  178. package/dist/chunk-HDSRORNV.js.map +0 -1
  179. package/dist/chunk-HMLMH7VZ.js.map +0 -1
  180. package/dist/chunk-HPFZLISB.js.map +0 -1
  181. package/dist/chunk-HZBC7PPD.js +0 -88
  182. package/dist/chunk-HZBC7PPD.js.map +0 -1
  183. package/dist/chunk-ITZ3DDOG.js.map +0 -1
  184. package/dist/chunk-JJP7KQND.js +0 -1
  185. package/dist/chunk-JJP7KQND.js.map +0 -1
  186. package/dist/chunk-KCBMVQL5.js.map +0 -1
  187. package/dist/chunk-KPPHZCZJ.js.map +0 -1
  188. package/dist/chunk-LAWMH22O.js.map +0 -1
  189. package/dist/chunk-MCUX5LA7.js +0 -103
  190. package/dist/chunk-MCUX5LA7.js.map +0 -1
  191. package/dist/chunk-MGNMHKX3.js.map +0 -1
  192. package/dist/chunk-N5KEREIA.js.map +0 -1
  193. package/dist/chunk-NHBZIL2J.js.map +0 -1
  194. package/dist/chunk-NUZ4OMU3.js.map +0 -1
  195. package/dist/chunk-NWCE4CIC.js.map +0 -1
  196. package/dist/chunk-OVPLOMPY.js.map +0 -1
  197. package/dist/chunk-QOV2R2WT.js.map +0 -1
  198. package/dist/chunk-SEFSL2GF.js.map +0 -1
  199. package/dist/chunk-UJQN5N3I.js.map +0 -1
  200. package/dist/chunk-W4ALF422.js.map +0 -1
  201. package/dist/chunk-Z4GHE2HD.js.map +0 -1
  202. package/dist/chunk-Z6YZJ36C.js.map +0 -1
  203. package/dist/chunk-ZK6GXM3J.js.map +0 -1
  204. package/dist/chunk-ZOGY2V3N.js +0 -158
  205. package/dist/chunk-ZOGY2V3N.js.map +0 -1
  206. package/dist/chunk-ZQIIPFD7.js.map +0 -1
  207. package/dist/cli.js.map +0 -1
  208. package/dist/index.js.map +0 -1
  209. package/dist/postinstall.js.map +0 -1
  210. package/dist/queries/affected.js.map +0 -1
  211. package/dist/queries/bottlenecks.js.map +0 -1
  212. package/dist/queries/by-kind.js.map +0 -1
  213. package/dist/queries/call-graph.js.map +0 -1
  214. package/dist/queries/change-surface.js.map +0 -1
  215. package/dist/queries/clean-signature.js.map +0 -1
  216. package/dist/queries/code.js.map +0 -1
  217. package/dist/queries/complexity-hotspots.js.map +0 -1
  218. package/dist/queries/complexity.js.map +0 -1
  219. package/dist/queries/convergence.js.map +0 -1
  220. package/dist/queries/coupling.js.map +0 -1
  221. package/dist/queries/cycles.js.map +0 -1
  222. package/dist/queries/dataflow.js.map +0 -1
  223. package/dist/queries/dead.js.map +0 -1
  224. package/dist/queries/deep-chains.js.map +0 -1
  225. package/dist/queries/deps.js.map +0 -1
  226. package/dist/queries/diff-impact.js.map +0 -1
  227. package/dist/queries/doc-coverage.js.map +0 -1
  228. package/dist/queries/drift.js.map +0 -1
  229. package/dist/queries/extract-candidates.js.map +0 -1
  230. package/dist/queries/fan.js.map +0 -1
  231. package/dist/queries/files.js.map +0 -1
  232. package/dist/queries/health.js.map +0 -1
  233. package/dist/queries/hierarchy.js.map +0 -1
  234. package/dist/queries/hotspots.js.map +0 -1
  235. package/dist/queries/imports.js.map +0 -1
  236. package/dist/queries/index.js.map +0 -1
  237. package/dist/queries/isolated.js.map +0 -1
  238. package/dist/queries/members.js.map +0 -1
  239. package/dist/queries/methods.js.map +0 -1
  240. package/dist/queries/outline.js.map +0 -1
  241. package/dist/queries/passthrough-candidates.js.map +0 -1
  242. package/dist/queries/redundant-reexports.js.map +0 -1
  243. package/dist/queries/refs.js.map +0 -1
  244. package/dist/queries/similar-chains.js.map +0 -1
  245. package/dist/queries/similar-files.js.map +0 -1
  246. package/dist/queries/similar-signatures.js.map +0 -1
  247. package/dist/queries/similar.js.map +0 -1
  248. package/dist/queries/slice.js.map +0 -1
  249. package/dist/queries/stale-abstractions.js.map +0 -1
  250. package/dist/queries/stats.js.map +0 -1
  251. package/dist/queries/surface.js.map +0 -1
  252. package/dist/queries/symbols.js.map +0 -1
  253. package/dist/queries/system.js.map +0 -1
  254. package/dist/queries/test-coverage.d.ts +0 -22
  255. package/dist/queries/test-coverage.js +0 -11
  256. package/dist/queries/test-coverage.js.map +0 -1
  257. package/dist/queries/trace.js.map +0 -1
  258. package/dist/queries/wrapper-candidates.js.map +0 -1
  259. package/dist/reindex-worker.js.map +0 -1
  260. package/docs/AGENT_GUIDE.md +0 -359
  261. package/reports/debloat/2026-04-10-scip-query-self-audit.md +0 -161
  262. package/src/cli.ts +0 -1480
  263. package/src/config.ts +0 -117
  264. package/src/db.ts +0 -127
  265. package/src/gitignore-filter.ts +0 -143
  266. package/src/index.ts +0 -11
  267. package/src/postinstall.ts +0 -8
  268. package/src/queries/affected.ts +0 -86
  269. package/src/queries/bottlenecks.ts +0 -67
  270. package/src/queries/by-kind.ts +0 -204
  271. package/src/queries/call-graph.ts +0 -66
  272. package/src/queries/change-surface.ts +0 -110
  273. package/src/queries/clean-signature.ts +0 -22
  274. package/src/queries/code.ts +0 -101
  275. package/src/queries/complexity-hotspots.ts +0 -119
  276. package/src/queries/complexity.ts +0 -152
  277. package/src/queries/convergence.ts +0 -82
  278. package/src/queries/coupling.ts +0 -99
  279. package/src/queries/cycles.ts +0 -78
  280. package/src/queries/dataflow.ts +0 -128
  281. package/src/queries/dead.ts +0 -122
  282. package/src/queries/deep-chains.ts +0 -59
  283. package/src/queries/deps.ts +0 -46
  284. package/src/queries/diff-impact.ts +0 -204
  285. package/src/queries/doc-coverage.ts +0 -86
  286. package/src/queries/drift.ts +0 -224
  287. package/src/queries/extract-candidates.ts +0 -167
  288. package/src/queries/fan.ts +0 -148
  289. package/src/queries/files.ts +0 -16
  290. package/src/queries/health.ts +0 -324
  291. package/src/queries/hierarchy.ts +0 -49
  292. package/src/queries/hotspots.ts +0 -53
  293. package/src/queries/imports.ts +0 -95
  294. package/src/queries/index.ts +0 -45
  295. package/src/queries/isolated.ts +0 -67
  296. package/src/queries/members.ts +0 -54
  297. package/src/queries/methods.ts +0 -27
  298. package/src/queries/outline.ts +0 -52
  299. package/src/queries/passthrough-candidates.ts +0 -94
  300. package/src/queries/redundant-reexports.ts +0 -170
  301. package/src/queries/refs.ts +0 -27
  302. package/src/queries/similar-chains.ts +0 -314
  303. package/src/queries/similar-files.ts +0 -140
  304. package/src/queries/similar-signatures.ts +0 -151
  305. package/src/queries/similar.ts +0 -305
  306. package/src/queries/slice.ts +0 -154
  307. package/src/queries/stale-abstractions.ts +0 -82
  308. package/src/queries/stats.ts +0 -22
  309. package/src/queries/surface.ts +0 -34
  310. package/src/queries/symbols.ts +0 -39
  311. package/src/queries/system.ts +0 -86
  312. package/src/queries/test-coverage.ts +0 -106
  313. package/src/queries/trace.ts +0 -55
  314. package/src/queries/wrapper-candidates.ts +0 -112
  315. package/src/query-support.ts +0 -226
  316. package/src/reindex/detect.ts +0 -58
  317. package/src/reindex/index.ts +0 -153
  318. package/src/reindex/indexers.ts +0 -220
  319. package/src/reindex/install.ts +0 -125
  320. package/src/reindex-worker.ts +0 -35
  321. package/src/setup.ts +0 -202
  322. package/src/symbol-parser.ts +0 -278
  323. package/src/types.ts +0 -654
  324. package/src/watch.ts +0 -274
  325. package/tests/gitignore-filter.test.ts +0 -48
  326. package/tests/queries.test.ts +0 -300
  327. package/tests/symbol-parser.test.ts +0 -157
  328. package/tsconfig.json +0 -20
  329. package/tsup.config.ts +0 -40
  330. package/vitest.config.ts +0 -7
package/README.md CHANGED
@@ -12,7 +12,7 @@ For goal-oriented usage guides (not just command reference), see **[Agent Guide]
12
12
  - **[Write an implementation plan](docs/AGENT_GUIDE.md#workflow-2-write-a-concrete-implementation-plan)** — identify contracts, map dependencies, find reusable code
13
13
  - **[De-bloat a codebase](docs/AGENT_GUIDE.md#workflow-3-clean-up-and-de-bloat-a-codebase)** — prioritized cleanup from dead code to pattern drift
14
14
  - **[Assess code quality](docs/AGENT_GUIDE.md#workflow-4-assess-code-quality-and-risk)** — health score, complexity hotspots, coupling risks
15
- - **[Verify change impact](docs/AGENT_GUIDE.md#workflow-5-understand-impact-after-making-changes)** — diff impact, transitive blast radius, test gaps
15
+ - **[Verify change impact](docs/AGENT_GUIDE.md#workflow-5-understand-impact-after-making-changes)** — diff impact, transitive blast radius, consumer blast radius
16
16
 
17
17
  ## Quick Start
18
18
 
@@ -53,6 +53,8 @@ scip-query diff-impact # what did my changes affect?
53
53
  | Dart | scip-dart | [releases](https://github.com/nicovince/scip-dart/releases) |
54
54
  | PHP | scip-php | [releases](https://github.com/nicovince/scip-php/releases) |
55
55
 
56
+ For Python, the npm package is `scip-python-plus`. Depending on which version you installed, the executable on your `PATH` may be `scip-python`, `scip-python-plus`, or both. `scip-query` now accepts either name.
57
+
56
58
  ## How It Works
57
59
 
58
60
  1. A SCIP indexer analyzes your source code using the actual compiler/type checker and produces a `index.scip` protobuf file containing every symbol, definition, and reference — fully type-resolved.
@@ -461,10 +463,10 @@ scip-query dead src/utils --skip-barrels --include-members
461
463
  **Options:**
462
464
  - `--min-loc <n>` — Only show symbols >= N lines (default: 1)
463
465
  - `--include-tests` — Include test files in results (excluded by default)
464
- - `--skip-barrels` — Ignore references from barrel re-export files (index.ts)
466
+ - `--skip-barrels` — Ignore references from inactive barrel re-export files
465
467
  - `--include-members` — Include class members (module-level only by default)
466
468
 
467
- **Value:** Find code you can delete. The `--skip-barrels` flag is key without it, symbols re-exported through index.ts appear "referenced" even if no real consumer uses them.
469
+ **Value:** Find code you can delete. The `--skip-barrels` flag is key when a codebase has unused barrels, but it now keeps live entry-surface barrels counted so active exports do not look dead.
468
470
 
469
471
  ---
470
472
 
@@ -510,31 +512,6 @@ scip-query doc-coverage --min-loc 5
510
512
 
511
513
  ---
512
514
 
513
- #### `test-coverage [symbol]`
514
-
515
- Check if symbols are referenced by test files. Without a symbol argument, shows a summary percentage. With a symbol, shows which specific test files cover it.
516
-
517
- ```bash
518
- scip-query test-coverage
519
- # Test coverage: 45%
520
- # Total symbols: 95
521
- # Covered: 43
522
- # Not covered: 52
523
-
524
- scip-query test-coverage AuthService
525
- # [covered] AuthService (src/services/auth.service.ts)
526
- # ← src/__tests__/auth.test.ts
527
- # [NOT COVERED] AuthService:logout() (src/services/auth.service.ts)
528
- ```
529
-
530
- **Options:**
531
- - `-s, --scope <path>` — Limit to files matching path
532
- - `--min-loc <n>` — Minimum LOC for summary mode (default: 3)
533
-
534
- **Value:** Reference-based test coverage — which symbols does your test suite actually exercise? Not execution coverage, but structural coverage: "do tests at least reference this code?"
535
-
536
- ---
537
-
538
515
  ### Codebase Metrics
539
516
 
540
517
  These commands measure structural properties of the codebase — hotspots, coupling, bottlenecks, fan-in/out.
@@ -556,7 +533,7 @@ scip-query hotspots -n 15
556
533
  - `-n, --limit <n>` — Number of results (default: 30)
557
534
  - `-s, --scope <path>` — Limit to files matching path
558
535
 
559
- **Value:** Identify the symbols where a bug or breaking change would affect the most consumers. Hotspots deserve the most careful review, the best test coverage, and the most stable interfaces.
536
+ **Value:** Identify the symbols where a bug or breaking change would affect the most consumers. Hotspots deserve the most careful review and the most stable interfaces.
560
537
 
561
538
  ---
562
539
 
@@ -581,7 +558,7 @@ scip-query fan-in -n 10
581
558
  - `-n, --limit <n>` — Number of results for top mode (default: 30)
582
559
  - `-s, --scope <path>` — Limit to files matching path
583
560
 
584
- **Value:** High fan-in = widely depended upon. Changes to high fan-in symbols have large blast radius. These symbols should have stable interfaces and thorough tests.
561
+ **Value:** High fan-in = widely depended upon. Changes to high fan-in symbols have large blast radius. These symbols should have stable interfaces and careful review.
585
562
 
586
563
  ---
587
564
 
@@ -871,19 +848,19 @@ scip-query affected login --max-depth 3
871
848
 
872
849
  #### `change-surface <file>`
873
850
 
874
- Pre-change briefing for a file: every exported symbol, consumer count, test coverage, and risk level.
851
+ Pre-change briefing for a file: every exported symbol, consumer count, and blast-radius risk.
875
852
 
876
853
  ```bash
877
854
  scip-query change-surface auth.service.ts
878
855
  # File: src/services/auth.service.ts
879
- # Test coverage: 60% | External consumers: 45
856
+ # External consumers: 45
880
857
  #
881
- # 1-50 AuthService [12 consumers] (2 test files) * medium risk *
882
- # 5-20 login() [8 consumers] (1 test file)
883
- # 22-35 logout() [3 consumers] (no tests) *** HIGH RISK ***
858
+ # 1-50 AuthService [12 consumers] *** HIGH RISK ***
859
+ # 5-20 login() [8 consumers] * medium risk *
860
+ # 22-35 logout() [0 consumers]
884
861
  ```
885
862
 
886
- **Value:** One command before modifying any file. Shows what's exported, who uses it, what's tested, and what's dangerous.
863
+ **Value:** One command before modifying any file. Shows what's exported, who uses it, and which symbols carry the largest downstream blast radius.
887
864
 
888
865
  ---
889
866
 
@@ -897,13 +874,12 @@ scip-query diff-impact --base main
897
874
  # Changed files: 3
898
875
  # Changed symbols: 12
899
876
  # Affected consumer files: 28
900
- # Test coverage: 67%
901
877
  ```
902
878
 
903
879
  **Options:**
904
880
  - `--base <ref>` — Git ref to diff against (default: HEAD)
905
881
 
906
- **Value:** Run before committing. Shows everything your changes affect, which consumer files are impacted, and where test gaps exist.
882
+ **Value:** Run before committing. Shows everything your changes affect and which consumer files sit downstream of the changed symbols.
907
883
 
908
884
  ---
909
885
 
@@ -914,15 +890,12 @@ scip-query diff-impact --base main
914
890
  Detect files that deviate from their directory's typical dependency pattern.
915
891
 
916
892
  ```bash
917
- scip-query drift --min-deviation 30
893
+ scip-query drift
918
894
  # src/services/legacy-auth.ts (65% deviation from src/services)
919
895
  # Missing expected: validator.ts, logger.ts
920
896
  # Unexpected: raw-sql.ts, deprecated-crypto.ts
921
897
  ```
922
898
 
923
- **Options:**
924
- - `--min-deviation <n>` — Minimum deviation % to report (default: 30)
925
-
926
899
  **Value:** Finds files that don't follow their neighbors' conventions. The outliers are either legacy code needing migration or intentional exceptions needing documentation.
927
900
 
928
901
  ---
@@ -1149,7 +1122,7 @@ scip-query redundant-reexports
1149
1122
  - `-s, --scope <path>` — Limit to files matching path
1150
1123
  - `-n, --limit <n>` — Number of results (default: 30)
1151
1124
 
1152
- **Value:** Finds dead entries in barrel files. Note: TypeScript namespace imports (`import * as`) resolve through barrels transparently, so the command is most effective on codebases using named imports.
1125
+ **Value:** Finds dead entries in inactive barrel files. Live barrels are skipped so shared entry surfaces like package roots and CLI registries do not show up as false positives.
1153
1126
 
1154
1127
  ---
1155
1128
 
@@ -0,0 +1,161 @@
1
+ import {
2
+ findFirstSymbolMatch,
3
+ getSourceImports,
4
+ resolveIndexedFile
5
+ } from "./chunk-AXQKUYKF.js";
6
+ import {
7
+ isModuleLikeSymbol,
8
+ leafName,
9
+ shortenSymbol
10
+ } from "./chunk-QIXNAB5K.js";
11
+
12
+ // src/queries/imports.ts
13
+ function imports(db, filePattern) {
14
+ const importer = resolveIndexedFile(db, filePattern);
15
+ if (!importer) return [];
16
+ const rows = db.all(
17
+ `SELECT DISTINCT gs.symbol, def_d.relative_path AS from_file, imp_d.relative_path AS importer
18
+ FROM mentions m
19
+ JOIN chunks c ON m.chunk_id = c.id
20
+ JOIN documents imp_d ON c.document_id = imp_d.id
21
+ JOIN global_symbols gs ON m.symbol_id = gs.id
22
+ LEFT JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
23
+ LEFT JOIN documents def_d ON der.document_id = def_d.id
24
+ WHERE imp_d.relative_path = ?
25
+ AND m.role = 2
26
+ ORDER BY def_d.relative_path, gs.symbol`,
27
+ importer
28
+ );
29
+ const indexedResults = rows.filter((row) => !db.isIgnored(row.importer)).map((r) => ({
30
+ symbol: r.symbol,
31
+ shortName: shortenSymbol(r.symbol),
32
+ fromFile: r.from_file ?? "(external)"
33
+ }));
34
+ if (indexedResults.length > 0) {
35
+ return indexedResults;
36
+ }
37
+ return getSourceImports(db, importer).map((entry) => ({
38
+ symbol: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
39
+ shortName: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
40
+ fromFile: entry.sourcePath ?? "(external)"
41
+ }));
42
+ }
43
+ function importedBy(db, symbolPattern) {
44
+ const rows = db.all(
45
+ `SELECT DISTINCT gs.symbol, d.relative_path AS importer
46
+ FROM mentions m
47
+ JOIN chunks c ON m.chunk_id = c.id
48
+ JOIN documents d ON c.document_id = d.id
49
+ JOIN global_symbols gs ON m.symbol_id = gs.id
50
+ WHERE gs.symbol LIKE ?
51
+ AND m.role = 2
52
+ ORDER BY d.relative_path`,
53
+ `%${symbolPattern}%`
54
+ );
55
+ const indexedResults = rows.filter((r) => !db.isIgnored(r.importer)).map((r) => ({
56
+ symbol: r.symbol,
57
+ shortName: shortenSymbol(r.symbol),
58
+ fromFile: r.importer
59
+ }));
60
+ if (indexedResults.length > 0) {
61
+ return indexedResults;
62
+ }
63
+ const target = findFirstSymbolMatch(db, symbolPattern);
64
+ const targetFile = target?.relativePath ?? null;
65
+ const targetLeaf = target ? leafName(target.symbol) : symbolPattern.replace(/\(\)$/, "");
66
+ const targetIsModule = target ? isModuleLikeSymbol(target.symbol) : false;
67
+ const files = db.all(
68
+ `SELECT relative_path
69
+ FROM documents
70
+ WHERE 1 = 1
71
+ ${db.pathExclusionsFor("documents")}
72
+ ORDER BY relative_path`
73
+ );
74
+ const importers = /* @__PURE__ */ new Set();
75
+ for (const row of files) {
76
+ if (db.isIgnored(row.relative_path)) continue;
77
+ for (const entry of getSourceImports(db, row.relative_path)) {
78
+ if (!entry.sourcePath) continue;
79
+ if (targetFile && normalizePath(entry.sourcePath) !== normalizePath(targetFile)) {
80
+ continue;
81
+ }
82
+ if (targetIsModule) {
83
+ importers.add(row.relative_path);
84
+ continue;
85
+ }
86
+ if (entry.kind === "named" && entry.importedName === targetLeaf) {
87
+ importers.add(row.relative_path);
88
+ continue;
89
+ }
90
+ if (entry.kind === "namespace" && entry.usedMembers.includes(targetLeaf)) {
91
+ importers.add(row.relative_path);
92
+ }
93
+ }
94
+ }
95
+ return [...importers].sort().map((importer) => ({
96
+ symbol: target?.symbol ?? targetLeaf,
97
+ shortName: target ? shortenSymbol(target.symbol) : targetLeaf,
98
+ fromFile: importer
99
+ }));
100
+ }
101
+ function unusedImports(db, filePattern) {
102
+ const importer = resolveIndexedFile(db, filePattern);
103
+ if (!importer) return [];
104
+ const rows = db.all(
105
+ `SELECT gs.symbol, d.relative_path AS imported_in, d.relative_path AS importer
106
+ FROM mentions m
107
+ JOIN chunks c ON m.chunk_id = c.id
108
+ JOIN documents d ON c.document_id = d.id
109
+ JOIN global_symbols gs ON m.symbol_id = gs.id
110
+ WHERE d.relative_path = ?
111
+ AND m.role = 2
112
+ AND NOT EXISTS (
113
+ SELECT 1
114
+ FROM mentions ref_m
115
+ JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
116
+ WHERE ref_m.symbol_id = gs.id
117
+ AND ref_m.role != 1
118
+ AND ref_c.document_id = d.id
119
+ )
120
+ ORDER BY d.relative_path, gs.symbol`,
121
+ importer
122
+ );
123
+ const indexedResults = rows.filter((row) => !db.isIgnored(row.importer)).map((r) => ({
124
+ symbol: r.symbol,
125
+ shortName: shortenSymbol(r.symbol),
126
+ importedIn: r.imported_in
127
+ }));
128
+ if (indexedResults.length > 0) {
129
+ return indexedResults;
130
+ }
131
+ return getSourceImports(db, importer).filter((entry) => entry.kind !== "side-effect" && !entry.used).map((entry) => ({
132
+ symbol: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
133
+ shortName: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
134
+ importedIn: importer
135
+ }));
136
+ }
137
+ function renderImportSymbol(importedName, localName, kind) {
138
+ if (kind === "namespace" && importedName === "*" && localName) {
139
+ return `* as ${localName}`;
140
+ }
141
+ if (kind === "default" && localName) {
142
+ return `default as ${localName}`;
143
+ }
144
+ if (kind === "side-effect") {
145
+ return "(side effect import)";
146
+ }
147
+ if (localName && localName !== importedName) {
148
+ return `${importedName} as ${localName}`;
149
+ }
150
+ return importedName;
151
+ }
152
+ function normalizePath(path) {
153
+ return path.replace(/\\/g, "/");
154
+ }
155
+
156
+ export {
157
+ imports,
158
+ importedBy,
159
+ unusedImports
160
+ };
161
+ //# sourceMappingURL=chunk-26DOJ63W.js.map
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-2UELLEBI.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  testFileExclusionSql
3
- } from "./chunk-ZOGY2V3N.js";
3
+ } from "./chunk-AXQKUYKF.js";
4
4
  import {
5
5
  shortenSymbol
6
- } from "./chunk-QOV2R2WT.js";
6
+ } from "./chunk-QIXNAB5K.js";
7
7
 
8
8
  // src/queries/wrapper-candidates.ts
9
9
  function wrapperCandidates(db, opts) {
@@ -94,4 +94,4 @@ function wrapperCandidates(db, opts) {
94
94
  export {
95
95
  wrapperCandidates
96
96
  };
97
- //# sourceMappingURL=chunk-4PDAL6IL.js.map
97
+ //# sourceMappingURL=chunk-4JCSOF2O.js.map
@@ -1,18 +1,26 @@
1
+ import {
2
+ resolveIndexedPaths
3
+ } from "./chunk-AXQKUYKF.js";
1
4
  import {
2
5
  shortenSymbol
3
- } from "./chunk-QOV2R2WT.js";
6
+ } from "./chunk-QIXNAB5K.js";
4
7
 
5
8
  // src/queries/outline.ts
6
9
  function outline(db, filePattern) {
10
+ const resolvedPaths = resolveIndexedPaths(db, filePattern);
11
+ if (resolvedPaths.length === 0) {
12
+ return [];
13
+ }
14
+ const placeholders = resolvedPaths.map(() => "?").join(", ");
7
15
  const rows = db.all(
8
16
  `SELECT gs.symbol, gs.enclosing_symbol, der.start_line, der.end_line
9
17
  FROM defn_enclosing_ranges der
10
18
  JOIN global_symbols gs ON der.symbol_id = gs.id
11
19
  JOIN documents d ON der.document_id = d.id
12
- WHERE d.relative_path LIKE ?
20
+ WHERE d.relative_path IN (${placeholders})
13
21
  ${db.symbolNoise}
14
- ORDER BY der.start_line`,
15
- `%${filePattern}%`
22
+ ORDER BY d.relative_path, der.start_line`,
23
+ ...resolvedPaths
16
24
  );
17
25
  const nodeMap = /* @__PURE__ */ new Map();
18
26
  const roots = [];
@@ -40,4 +48,4 @@ function outline(db, filePattern) {
40
48
  export {
41
49
  outline
42
50
  };
43
- //# sourceMappingURL=chunk-6SXADWLW.js.map
51
+ //# sourceMappingURL=chunk-5OMVSV6E.js.map
@@ -1,10 +1,6 @@
1
- import {
2
- TEST_FILE_PATTERNS,
3
- testFileMatchSql
4
- } from "./chunk-ZOGY2V3N.js";
5
1
  import {
6
2
  shortenSymbol
7
- } from "./chunk-QOV2R2WT.js";
3
+ } from "./chunk-QIXNAB5K.js";
8
4
 
9
5
  // src/queries/diff-impact.ts
10
6
  import { execFileSync } from "child_process";
@@ -12,23 +8,17 @@ function diffImpact(db, opts = {}) {
12
8
  const { base = "HEAD" } = opts;
13
9
  let changedFileLines;
14
10
  try {
15
- const stdout = execFileSync("git", ["diff", "--name-only", base], {
16
- encoding: "utf-8",
17
- cwd: db.config.projectRoot,
18
- timeout: 1e4
19
- });
20
- changedFileLines = stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
11
+ changedFileLines = getChangedFiles(db.config.projectRoot, base);
21
12
  } catch {
22
13
  return {
23
14
  changedFiles: [],
24
15
  changedSymbols: [],
25
16
  affectedConsumers: [],
26
- uncoveredSymbols: [],
27
17
  summary: {
28
18
  totalChangedFiles: 0,
29
19
  totalChangedSymbols: 0,
30
20
  totalAffectedFiles: 0,
31
- testCoveragePercent: 0
21
+ note: "Unable to compute git diff."
32
22
  }
33
23
  };
34
24
  }
@@ -37,12 +27,11 @@ function diffImpact(db, opts = {}) {
37
27
  changedFiles: [],
38
28
  changedSymbols: [],
39
29
  affectedConsumers: [],
40
- uncoveredSymbols: [],
41
30
  summary: {
42
31
  totalChangedFiles: 0,
43
32
  totalChangedSymbols: 0,
44
33
  totalAffectedFiles: 0,
45
- testCoveragePercent: 0
34
+ note: "No changed files found."
46
35
  }
47
36
  };
48
37
  }
@@ -65,12 +54,11 @@ function diffImpact(db, opts = {}) {
65
54
  changedFiles: changedFileLines,
66
55
  changedSymbols: [],
67
56
  affectedConsumers: [],
68
- uncoveredSymbols: [],
69
57
  summary: {
70
58
  totalChangedFiles: changedFileLines.length,
71
59
  totalChangedSymbols: 0,
72
60
  totalAffectedFiles: 0,
73
- testCoveragePercent: 0
61
+ note: "Changed files are not present in the current SCIP index."
74
62
  }
75
63
  };
76
64
  }
@@ -85,11 +73,8 @@ function diffImpact(db, opts = {}) {
85
73
  ORDER BY d.relative_path`,
86
74
  ...changedDocIds
87
75
  );
88
- const testPatternSql = testFileMatchSql("ref_d", TEST_FILE_PATTERNS);
89
76
  const changedSymbols = [];
90
77
  const consumerMap = /* @__PURE__ */ new Map();
91
- const uncoveredSymbols = [];
92
- let coveredCount = 0;
93
78
  for (const sym of syms) {
94
79
  const fanInRow = db.get(
95
80
  `SELECT COUNT(DISTINCT c.document_id) AS fan_in
@@ -126,44 +111,41 @@ function diffImpact(db, opts = {}) {
126
111
  }
127
112
  consumerMap.get(consumer.relative_path).add(shortName);
128
113
  }
129
- const hasTest = db.get(
130
- `SELECT COUNT(*) AS c
131
- FROM mentions m
132
- JOIN chunks c ON m.chunk_id = c.id
133
- JOIN documents ref_d ON c.document_id = ref_d.id
134
- WHERE m.symbol_id = ?
135
- AND m.role != 1
136
- AND (${testPatternSql})`,
137
- sym.symbol_id
138
- );
139
- if (hasTest && hasTest.c > 0) {
140
- coveredCount++;
141
- } else {
142
- uncoveredSymbols.push({
143
- symbol: sym.symbol,
144
- shortName,
145
- file: sym.relative_path
146
- });
147
- }
148
114
  }
149
115
  const affectedConsumers = [...consumerMap.entries()].map(([file, symbols]) => ({ file, consumedSymbols: symbols.size })).sort((a, b) => b.consumedSymbols - a.consumedSymbols);
150
- const totalSymbols = changedSymbols.length;
151
- const testCoveragePercent = totalSymbols > 0 ? Math.round(coveredCount / totalSymbols * 100) : 0;
152
116
  return {
153
117
  changedFiles,
154
118
  changedSymbols,
155
119
  affectedConsumers,
156
- uncoveredSymbols,
157
120
  summary: {
158
121
  totalChangedFiles: changedFiles.length,
159
- totalChangedSymbols: totalSymbols,
160
- totalAffectedFiles: affectedConsumers.length,
161
- testCoveragePercent
122
+ totalChangedSymbols: changedSymbols.length,
123
+ totalAffectedFiles: affectedConsumers.length
162
124
  }
163
125
  };
164
126
  }
127
+ function getChangedFiles(projectRoot, base) {
128
+ const diff = execFileSync("git", ["diff", "--name-only", base], {
129
+ encoding: "utf-8",
130
+ cwd: projectRoot,
131
+ timeout: 1e4
132
+ });
133
+ const staged = execFileSync("git", ["diff", "--name-only", "--cached", base], {
134
+ encoding: "utf-8",
135
+ cwd: projectRoot,
136
+ timeout: 1e4
137
+ });
138
+ const untracked = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
139
+ encoding: "utf-8",
140
+ cwd: projectRoot,
141
+ timeout: 1e4
142
+ });
143
+ return [...new Set(
144
+ [diff, staged, untracked].flatMap((chunk) => chunk.split("\n")).map((line) => line.trim()).filter((line) => line.length > 0)
145
+ )];
146
+ }
165
147
 
166
148
  export {
167
149
  diffImpact
168
150
  };
169
- //# sourceMappingURL=chunk-KPPHZCZJ.js.map
151
+ //# sourceMappingURL=chunk-7HK5ZLOE.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildFileDepGraph
3
- } from "./chunk-ZOGY2V3N.js";
3
+ } from "./chunk-AXQKUYKF.js";
4
4
 
5
5
  // src/queries/drift.ts
6
6
  import path from "path";
@@ -10,9 +10,10 @@ function drift(db, opts) {
10
10
  const symbolRefs = buildSymbolRefGraph(db, scope);
11
11
  const results = [];
12
12
  for (const [file, deps] of depGraph) {
13
- if (isStructuralRole(path.basename(file))) continue;
13
+ if (shouldSkipDriftFile(file)) continue;
14
14
  const referencedFiles = symbolRefs.get(file) ?? /* @__PURE__ */ new Set();
15
15
  for (const dep of deps) {
16
+ if (shouldSkipDriftFile(dep)) continue;
16
17
  if (!referencedFiles.has(dep)) {
17
18
  if (isLikelyTypeOnlyDep(dep)) continue;
18
19
  results.push({
@@ -26,10 +27,11 @@ function drift(db, opts) {
26
27
  }
27
28
  const layerRules = inferLayerRules(depGraph);
28
29
  for (const [file, deps] of depGraph) {
29
- if (isStructuralRole(path.basename(file))) continue;
30
- const fileLayer = getTopDir(file);
30
+ if (shouldSkipDriftFile(file)) continue;
31
+ const fileLayer = getArchitecturalLayer(file);
31
32
  for (const dep of deps) {
32
- const depLayer = getTopDir(dep);
33
+ if (shouldSkipDriftFile(dep)) continue;
34
+ const depLayer = getArchitecturalLayer(dep);
33
35
  if (fileLayer === depLayer) continue;
34
36
  const violation = layerRules.get(`${fileLayer}->${depLayer}`);
35
37
  if (violation === "violation") {
@@ -53,14 +55,16 @@ function drift(db, opts) {
53
55
  if (files.length < 3) continue;
54
56
  const depFreq = /* @__PURE__ */ new Map();
55
57
  for (const file of files) {
56
- if (isStructuralRole(path.basename(file))) continue;
58
+ if (shouldSkipDriftFile(file)) continue;
57
59
  for (const dep of depGraph.get(file) ?? []) {
60
+ if (shouldSkipDriftFile(dep)) continue;
58
61
  depFreq.set(dep, (depFreq.get(dep) ?? 0) + 1);
59
62
  }
60
63
  }
61
64
  for (const file of files) {
62
- if (isStructuralRole(path.basename(file))) continue;
65
+ if (shouldSkipDriftFile(file)) continue;
63
66
  for (const dep of depGraph.get(file) ?? []) {
67
+ if (shouldSkipDriftFile(dep)) continue;
64
68
  if ((depFreq.get(dep) ?? 0) === 1) {
65
69
  if (path.dirname(dep) === dir) continue;
66
70
  results.push({
@@ -107,10 +111,12 @@ function inferLayerRules(depGraph) {
107
111
  const layerEdges = /* @__PURE__ */ new Map();
108
112
  const layerSet = /* @__PURE__ */ new Set();
109
113
  for (const [file, deps] of depGraph) {
110
- const fromLayer = getTopDir(file);
114
+ if (shouldSkipDriftFile(file)) continue;
115
+ const fromLayer = getArchitecturalLayer(file);
111
116
  layerSet.add(fromLayer);
112
117
  for (const dep of deps) {
113
- const toLayer = getTopDir(dep);
118
+ if (shouldSkipDriftFile(dep)) continue;
119
+ const toLayer = getArchitecturalLayer(dep);
114
120
  if (fromLayer === toLayer) continue;
115
121
  layerSet.add(toLayer);
116
122
  const key = `${fromLayer}->${toLayer}`;
@@ -123,13 +129,23 @@ function inferLayerRules(depGraph) {
123
129
  }
124
130
  return rules;
125
131
  }
126
- function getTopDir(filePath) {
127
- const parts = filePath.split("/");
128
- return parts[0] ?? filePath;
132
+ function getArchitecturalLayer(filePath) {
133
+ const normalized = filePath.replace(/\\/g, "/");
134
+ const parts = normalized.split("/").filter(Boolean);
135
+ if (parts.length <= 1) {
136
+ return "(root)";
137
+ }
138
+ if (parts.length >= 3 && ["src", "lib", "app", "server", "client"].includes(parts[0])) {
139
+ return `${parts[0]}/${parts[1]}`;
140
+ }
141
+ return parts[0];
129
142
  }
130
143
  function isLikelyTypeOnlyDep(dep) {
131
144
  return dep.includes("types") || dep.endsWith(".d.ts");
132
145
  }
146
+ function shouldSkipDriftFile(filePath) {
147
+ return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
148
+ }
133
149
  function isStructuralRole(basename) {
134
150
  if (basename === "index.ts" || basename === "index.js") return true;
135
151
  if (basename === "cli.ts" || basename === "main.ts" || basename === "main.rs") return true;
@@ -137,8 +153,13 @@ function isStructuralRole(basename) {
137
153
  if (basename === "health.ts" || basename === "health.js") return true;
138
154
  return false;
139
155
  }
156
+ function isTestLikePath(filePath) {
157
+ const normalized = filePath.replace(/\\/g, "/");
158
+ const basename = path.basename(normalized);
159
+ return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(basename) || /_(test|spec)\.[A-Za-z0-9]+$/.test(basename) || /^test[_-]/.test(basename) || /^test\./.test(basename);
160
+ }
140
161
 
141
162
  export {
142
163
  drift
143
164
  };
144
- //# sourceMappingURL=chunk-7RLE5EWE.js.map
165
+ //# sourceMappingURL=chunk-7KIMF5PV.js.map