seer-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/LICENSE +176 -0
  3. package/README.md +272 -0
  4. package/README_dev.md +199 -0
  5. package/dist/bundle/ci.d.ts +47 -0
  6. package/dist/bundle/ci.d.ts.map +1 -0
  7. package/dist/bundle/ci.js +113 -0
  8. package/dist/bundle/ci.js.map +1 -0
  9. package/dist/bundle/contract.d.ts +111 -0
  10. package/dist/bundle/contract.d.ts.map +1 -0
  11. package/dist/bundle/contract.js +352 -0
  12. package/dist/bundle/contract.js.map +1 -0
  13. package/dist/bundle/export.d.ts +36 -0
  14. package/dist/bundle/export.d.ts.map +1 -0
  15. package/dist/bundle/export.js +152 -0
  16. package/dist/bundle/export.js.map +1 -0
  17. package/dist/bundle/external.d.ts +66 -0
  18. package/dist/bundle/external.d.ts.map +1 -0
  19. package/dist/bundle/external.js +238 -0
  20. package/dist/bundle/external.js.map +1 -0
  21. package/dist/bundle/format.d.ts +94 -0
  22. package/dist/bundle/format.d.ts.map +1 -0
  23. package/dist/bundle/format.js +42 -0
  24. package/dist/bundle/format.js.map +1 -0
  25. package/dist/bundle/import.d.ts +49 -0
  26. package/dist/bundle/import.d.ts.map +1 -0
  27. package/dist/bundle/import.js +116 -0
  28. package/dist/bundle/import.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +1402 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/init.d.ts +48 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +284 -0
  36. package/dist/cli/init.js.map +1 -0
  37. package/dist/db/schema.d.ts +3 -0
  38. package/dist/db/schema.d.ts.map +1 -0
  39. package/dist/db/schema.js +616 -0
  40. package/dist/db/schema.js.map +1 -0
  41. package/dist/db/store.d.ts +1011 -0
  42. package/dist/db/store.d.ts.map +1 -0
  43. package/dist/db/store.js +3888 -0
  44. package/dist/db/store.js.map +1 -0
  45. package/dist/graph/pagerank.d.ts +9 -0
  46. package/dist/graph/pagerank.d.ts.map +1 -0
  47. package/dist/graph/pagerank.js +47 -0
  48. package/dist/graph/pagerank.js.map +1 -0
  49. package/dist/indexer/architecture.d.ts +72 -0
  50. package/dist/indexer/architecture.d.ts.map +1 -0
  51. package/dist/indexer/architecture.js +112 -0
  52. package/dist/indexer/architecture.js.map +1 -0
  53. package/dist/indexer/behavior.d.ts +75 -0
  54. package/dist/indexer/behavior.d.ts.map +1 -0
  55. package/dist/indexer/behavior.js +395 -0
  56. package/dist/indexer/behavior.js.map +1 -0
  57. package/dist/indexer/boundaries.d.ts +60 -0
  58. package/dist/indexer/boundaries.d.ts.map +1 -0
  59. package/dist/indexer/boundaries.js +366 -0
  60. package/dist/indexer/boundaries.js.map +1 -0
  61. package/dist/indexer/churn.d.ts +15 -0
  62. package/dist/indexer/churn.d.ts.map +1 -0
  63. package/dist/indexer/churn.js +49 -0
  64. package/dist/indexer/churn.js.map +1 -0
  65. package/dist/indexer/classify.d.ts +9 -0
  66. package/dist/indexer/classify.d.ts.map +1 -0
  67. package/dist/indexer/classify.js +90 -0
  68. package/dist/indexer/classify.js.map +1 -0
  69. package/dist/indexer/context.d.ts +176 -0
  70. package/dist/indexer/context.d.ts.map +1 -0
  71. package/dist/indexer/context.js +193 -0
  72. package/dist/indexer/context.js.map +1 -0
  73. package/dist/indexer/continuity.d.ts +67 -0
  74. package/dist/indexer/continuity.d.ts.map +1 -0
  75. package/dist/indexer/continuity.js +288 -0
  76. package/dist/indexer/continuity.js.map +1 -0
  77. package/dist/indexer/detectchanges.d.ts +32 -0
  78. package/dist/indexer/detectchanges.d.ts.map +1 -0
  79. package/dist/indexer/detectchanges.js +74 -0
  80. package/dist/indexer/detectchanges.js.map +1 -0
  81. package/dist/indexer/discovery.d.ts +37 -0
  82. package/dist/indexer/discovery.d.ts.map +1 -0
  83. package/dist/indexer/discovery.js +136 -0
  84. package/dist/indexer/discovery.js.map +1 -0
  85. package/dist/indexer/externaldeps.d.ts +18 -0
  86. package/dist/indexer/externaldeps.d.ts.map +1 -0
  87. package/dist/indexer/externaldeps.js +288 -0
  88. package/dist/indexer/externaldeps.js.map +1 -0
  89. package/dist/indexer/freshness.d.ts +48 -0
  90. package/dist/indexer/freshness.d.ts.map +1 -0
  91. package/dist/indexer/freshness.js +128 -0
  92. package/dist/indexer/freshness.js.map +1 -0
  93. package/dist/indexer/git.d.ts +144 -0
  94. package/dist/indexer/git.d.ts.map +1 -0
  95. package/dist/indexer/git.js +444 -0
  96. package/dist/indexer/git.js.map +1 -0
  97. package/dist/indexer/index.d.ts +145 -0
  98. package/dist/indexer/index.d.ts.map +1 -0
  99. package/dist/indexer/index.js +930 -0
  100. package/dist/indexer/index.js.map +1 -0
  101. package/dist/indexer/modules.d.ts +62 -0
  102. package/dist/indexer/modules.d.ts.map +1 -0
  103. package/dist/indexer/modules.js +293 -0
  104. package/dist/indexer/modules.js.map +1 -0
  105. package/dist/indexer/preflight.d.ts +154 -0
  106. package/dist/indexer/preflight.d.ts.map +1 -0
  107. package/dist/indexer/preflight.js +399 -0
  108. package/dist/indexer/preflight.js.map +1 -0
  109. package/dist/indexer/protoScanner.d.ts +34 -0
  110. package/dist/indexer/protoScanner.d.ts.map +1 -0
  111. package/dist/indexer/protoScanner.js +133 -0
  112. package/dist/indexer/protoScanner.js.map +1 -0
  113. package/dist/indexer/risk.d.ts +115 -0
  114. package/dist/indexer/risk.d.ts.map +1 -0
  115. package/dist/indexer/risk.js +194 -0
  116. package/dist/indexer/risk.js.map +1 -0
  117. package/dist/indexer/serviceHostScanner.d.ts +25 -0
  118. package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
  119. package/dist/indexer/serviceHostScanner.js +95 -0
  120. package/dist/indexer/serviceHostScanner.js.map +1 -0
  121. package/dist/indexer/serviceLinks.d.ts +105 -0
  122. package/dist/indexer/serviceLinks.d.ts.map +1 -0
  123. package/dist/indexer/serviceLinks.js +509 -0
  124. package/dist/indexer/serviceLinks.js.map +1 -0
  125. package/dist/indexer/shapehash.d.ts +98 -0
  126. package/dist/indexer/shapehash.d.ts.map +1 -0
  127. package/dist/indexer/shapehash.js +354 -0
  128. package/dist/indexer/shapehash.js.map +1 -0
  129. package/dist/indexer/skeleton.d.ts +15 -0
  130. package/dist/indexer/skeleton.d.ts.map +1 -0
  131. package/dist/indexer/skeleton.js +136 -0
  132. package/dist/indexer/skeleton.js.map +1 -0
  133. package/dist/indexer/symbolhistory.d.ts +41 -0
  134. package/dist/indexer/symbolhistory.d.ts.map +1 -0
  135. package/dist/indexer/symbolhistory.js +124 -0
  136. package/dist/indexer/symbolhistory.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +68 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +179 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/server.d.ts +80 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +1610 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/parser/index.d.ts +8 -0
  146. package/dist/parser/index.d.ts.map +1 -0
  147. package/dist/parser/index.js +33 -0
  148. package/dist/parser/index.js.map +1 -0
  149. package/dist/parser/languages/cpp.d.ts +3 -0
  150. package/dist/parser/languages/cpp.d.ts.map +1 -0
  151. package/dist/parser/languages/cpp.js +350 -0
  152. package/dist/parser/languages/cpp.js.map +1 -0
  153. package/dist/parser/languages/csharp.d.ts +3 -0
  154. package/dist/parser/languages/csharp.d.ts.map +1 -0
  155. package/dist/parser/languages/csharp.js +239 -0
  156. package/dist/parser/languages/csharp.js.map +1 -0
  157. package/dist/parser/languages/go.d.ts +3 -0
  158. package/dist/parser/languages/go.d.ts.map +1 -0
  159. package/dist/parser/languages/go.js +259 -0
  160. package/dist/parser/languages/go.js.map +1 -0
  161. package/dist/parser/languages/java.d.ts +3 -0
  162. package/dist/parser/languages/java.d.ts.map +1 -0
  163. package/dist/parser/languages/java.js +391 -0
  164. package/dist/parser/languages/java.js.map +1 -0
  165. package/dist/parser/languages/python.d.ts +3 -0
  166. package/dist/parser/languages/python.d.ts.map +1 -0
  167. package/dist/parser/languages/python.js +396 -0
  168. package/dist/parser/languages/python.js.map +1 -0
  169. package/dist/parser/languages/rust.d.ts +3 -0
  170. package/dist/parser/languages/rust.d.ts.map +1 -0
  171. package/dist/parser/languages/rust.js +159 -0
  172. package/dist/parser/languages/rust.js.map +1 -0
  173. package/dist/parser/languages/typescript.d.ts +3 -0
  174. package/dist/parser/languages/typescript.d.ts.map +1 -0
  175. package/dist/parser/languages/typescript.js +1442 -0
  176. package/dist/parser/languages/typescript.js.map +1 -0
  177. package/dist/parser/parserContext.d.ts +77 -0
  178. package/dist/parser/parserContext.d.ts.map +1 -0
  179. package/dist/parser/parserContext.js +354 -0
  180. package/dist/parser/parserContext.js.map +1 -0
  181. package/dist/parser/walker.d.ts +81 -0
  182. package/dist/parser/walker.d.ts.map +1 -0
  183. package/dist/parser/walker.js +217 -0
  184. package/dist/parser/walker.js.map +1 -0
  185. package/dist/parser/worker.d.ts +66 -0
  186. package/dist/parser/worker.d.ts.map +1 -0
  187. package/dist/parser/worker.js +129 -0
  188. package/dist/parser/worker.js.map +1 -0
  189. package/dist/parser/workerpool.d.ts +107 -0
  190. package/dist/parser/workerpool.d.ts.map +1 -0
  191. package/dist/parser/workerpool.js +383 -0
  192. package/dist/parser/workerpool.js.map +1 -0
  193. package/dist/scip/format.d.ts +87 -0
  194. package/dist/scip/format.d.ts.map +1 -0
  195. package/dist/scip/format.js +31 -0
  196. package/dist/scip/format.js.map +1 -0
  197. package/dist/scip/import.d.ts +37 -0
  198. package/dist/scip/import.d.ts.map +1 -0
  199. package/dist/scip/import.js +180 -0
  200. package/dist/scip/import.js.map +1 -0
  201. package/dist/types.d.ts +392 -0
  202. package/dist/types.d.ts.map +1 -0
  203. package/dist/types.js +4 -0
  204. package/dist/types.js.map +1 -0
  205. package/docs/architecture.md +105 -0
  206. package/docs/benchmarks/methodology.md +134 -0
  207. package/docs/benchmarks/raw-results.md +71 -0
  208. package/docs/benchmarks.md +74 -0
  209. package/docs/cli.md +148 -0
  210. package/docs/examples/behavior-tests.md +70 -0
  211. package/docs/examples/change-history.md +85 -0
  212. package/docs/examples/pre-edit-context.md +81 -0
  213. package/docs/examples/service-links.md +88 -0
  214. package/docs/examples.md +80 -0
  215. package/docs/faq.md +70 -0
  216. package/docs/internals.md +104 -0
  217. package/docs/languages.md +70 -0
  218. package/docs/limits.md +52 -0
  219. package/docs/mcp.md +199 -0
  220. package/docs/quickstart.md +119 -0
  221. package/docs/testing.md +123 -0
  222. package/docs/tools.md +115 -0
  223. package/package.json +52 -0
  224. package/research-codebase.md +578 -0
  225. package/seer-cli-docs.md +326 -0
  226. package/seer-master-guide.md +246 -0
  227. package/src/bundle/ci.ts +141 -0
  228. package/src/bundle/contract.ts +387 -0
  229. package/src/bundle/export.ts +175 -0
  230. package/src/bundle/external.ts +285 -0
  231. package/src/bundle/format.ts +92 -0
  232. package/src/bundle/import.ts +157 -0
  233. package/src/cli/index.ts +1249 -0
  234. package/src/cli/init.ts +389 -0
  235. package/src/db/schema.ts +614 -0
  236. package/src/db/store.ts +4306 -0
  237. package/src/graph/pagerank.ts +53 -0
  238. package/src/indexer/architecture.ts +148 -0
  239. package/src/indexer/behavior.ts +466 -0
  240. package/src/indexer/boundaries.ts +374 -0
  241. package/src/indexer/churn.ts +58 -0
  242. package/src/indexer/classify.ts +96 -0
  243. package/src/indexer/context.ts +340 -0
  244. package/src/indexer/continuity.ts +322 -0
  245. package/src/indexer/detectchanges.ts +94 -0
  246. package/src/indexer/discovery.ts +176 -0
  247. package/src/indexer/externaldeps.ts +243 -0
  248. package/src/indexer/freshness.ts +166 -0
  249. package/src/indexer/git.ts +453 -0
  250. package/src/indexer/index.ts +1092 -0
  251. package/src/indexer/modules.ts +358 -0
  252. package/src/indexer/preflight.ts +548 -0
  253. package/src/indexer/protoScanner.ts +147 -0
  254. package/src/indexer/risk.ts +304 -0
  255. package/src/indexer/serviceHostScanner.ts +92 -0
  256. package/src/indexer/serviceLinks.ts +543 -0
  257. package/src/indexer/shapehash.ts +370 -0
  258. package/src/indexer/skeleton.ts +169 -0
  259. package/src/indexer/symbolhistory.ts +172 -0
  260. package/src/indexer/watcher.ts +206 -0
  261. package/src/mcp/server.ts +1659 -0
  262. package/src/parser/index.ts +37 -0
  263. package/src/parser/languages/cpp.ts +361 -0
  264. package/src/parser/languages/csharp.ts +235 -0
  265. package/src/parser/languages/go.ts +259 -0
  266. package/src/parser/languages/java.ts +382 -0
  267. package/src/parser/languages/python.ts +370 -0
  268. package/src/parser/languages/rust.ts +164 -0
  269. package/src/parser/languages/typescript.ts +1435 -0
  270. package/src/parser/parserContext.ts +392 -0
  271. package/src/parser/walker.ts +306 -0
  272. package/src/parser/worker.ts +181 -0
  273. package/src/parser/workerpool.ts +448 -0
  274. package/src/scip/format.ts +83 -0
  275. package/src/scip/import.ts +216 -0
  276. package/src/types.ts +457 -0
  277. package/tests/benchmark-service-links.ts +244 -0
  278. package/tests/bug-regressions.ts +626 -0
  279. package/tests/filters.ts +264 -0
  280. package/tests/fixtures/Counter.tsx +38 -0
  281. package/tests/fixtures/caller.ts +7 -0
  282. package/tests/fixtures/collisions.ts +23 -0
  283. package/tests/fixtures/local_helper.ts +5 -0
  284. package/tests/fixtures/overloads.java +17 -0
  285. package/tests/fixtures/remote_helper.ts +4 -0
  286. package/tests/fixtures/sample.c +15 -0
  287. package/tests/fixtures/sample.cpp +47 -0
  288. package/tests/fixtures/sample.cs +62 -0
  289. package/tests/fixtures/sample.go +68 -0
  290. package/tests/fixtures/sample.h +30 -0
  291. package/tests/fixtures/sample.java +85 -0
  292. package/tests/fixtures/sample.py +46 -0
  293. package/tests/fixtures/sample.rs +78 -0
  294. package/tests/fixtures/sample.ts +76 -0
  295. package/tests/fixtures-service/HttpClients.cs +30 -0
  296. package/tests/fixtures-service/HttpClients.java +24 -0
  297. package/tests/fixtures-service/billing.ts +15 -0
  298. package/tests/fixtures-service/docker-compose.yml +15 -0
  299. package/tests/fixtures-service/gateway.ts +10 -0
  300. package/tests/fixtures-service/get_user.ts +11 -0
  301. package/tests/fixtures-service/graphql_client.ts +63 -0
  302. package/tests/fixtures-service/graphql_server.ts +30 -0
  303. package/tests/fixtures-service/grpc_client.go +30 -0
  304. package/tests/fixtures-service/http_clients.go +23 -0
  305. package/tests/fixtures-service/http_clients.py +38 -0
  306. package/tests/fixtures-service/http_clients.ts +49 -0
  307. package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
  308. package/tests/fixtures-service/k8s_calls.ts +20 -0
  309. package/tests/fixtures-service/messaging.ts +87 -0
  310. package/tests/fixtures-service/trpc_client.ts +39 -0
  311. package/tests/fixtures-service/trpc_server.ts +39 -0
  312. package/tests/fixtures-service/user_service.proto +33 -0
  313. package/tests/fixtures-trackcd/Cargo.toml +11 -0
  314. package/tests/fixtures-trackcd/SpringController.java +36 -0
  315. package/tests/fixtures-trackcd/auth_service.ts +19 -0
  316. package/tests/fixtures-trackcd/complex_module.py +50 -0
  317. package/tests/fixtures-trackcd/express_app.js +30 -0
  318. package/tests/fixtures-trackcd/fastapi_app.py +49 -0
  319. package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
  320. package/tests/fixtures-trackcd/go.mod +8 -0
  321. package/tests/fixtures-trackcd/package.json +15 -0
  322. package/tests/fixtures-trackcd/requirements.txt +4 -0
  323. package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
  324. package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
  325. package/tests/fixtures-tracke/auth/crypto.ts +7 -0
  326. package/tests/fixtures-tracke/billing/Billing.ts +20 -0
  327. package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
  328. package/tests/fixtures-tracke/billing/server.ts +17 -0
  329. package/tests/fixtures-tracke/package.json +7 -0
  330. package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
  331. package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
  332. package/tests/fixtures-trackf/package.json +5 -0
  333. package/tests/fixtures-trackf/src/auth.ts +26 -0
  334. package/tests/fixtures-trackf/src/handlers.ts +35 -0
  335. package/tests/fixtures-tracki/billing/routes.ts +12 -0
  336. package/tests/fixtures-tracki/gateway/client.ts +13 -0
  337. package/tests/git-features.ts +267 -0
  338. package/tests/init.ts +141 -0
  339. package/tests/mcp-jit.ts +130 -0
  340. package/tests/mcp-smoke.ts +191 -0
  341. package/tests/mcp-trackcd.ts +169 -0
  342. package/tests/mcp-tracke.ts +229 -0
  343. package/tests/mcp-trackf.ts +330 -0
  344. package/tests/mcp-trackg.ts +219 -0
  345. package/tests/mcp-tracki.ts +174 -0
  346. package/tests/mcp-watcher.ts +126 -0
  347. package/tests/optspec.ts +194 -0
  348. package/tests/parallel-index.ts +333 -0
  349. package/tests/parallel-read.ts +125 -0
  350. package/tests/parallel-recovery.ts +241 -0
  351. package/tests/perf-callers.ts +145 -0
  352. package/tests/query-parity.ts +184 -0
  353. package/tests/query-perf.ts +55 -0
  354. package/tests/scale-parallel-parity.ts +225 -0
  355. package/tests/scale-test.ts +523 -0
  356. package/tests/smoke.ts +396 -0
  357. package/tests/trackcd.ts +325 -0
  358. package/tests/tracke-collisions.ts +255 -0
  359. package/tests/tracke.ts +314 -0
  360. package/tests/trackf-bugs.ts +406 -0
  361. package/tests/trackf.ts +390 -0
  362. package/tests/trackg.ts +1372 -0
  363. package/tests/tracki-boundaries.ts +202 -0
  364. package/tests/tracki-continuity.ts +253 -0
  365. package/tests/tracki-contract-diff.ts +249 -0
  366. package/tests/tracki-external-bundles.ts +341 -0
  367. package/tests/tracki-preflight.ts +251 -0
  368. package/tests/verify-roles.ts +51 -0
  369. package/tests/worker-parity.ts +286 -0
  370. package/tests/worker-pool.ts +262 -0
  371. package/tsconfig.json +20 -0
@@ -0,0 +1,374 @@
1
+ /**
2
+ * v10 — Monorepo package/service boundary detection.
3
+ *
4
+ * Source signals (in priority order — earlier sources win on overlap):
5
+ * 1. Nested manifest files:
6
+ * - package.json (npm/yarn/pnpm workspaces)
7
+ * - pyproject.toml
8
+ * - Cargo.toml
9
+ * - go.mod
10
+ * - composer.json
11
+ * 2. Workspace-declared globs:
12
+ * - package.json:workspaces
13
+ * - pnpm-workspace.yaml
14
+ * - turbo.json / nx.json pipelines
15
+ * - go.work
16
+ * - Cargo workspace members (parent Cargo.toml [workspace])
17
+ * 3. Convention fallback:
18
+ * - packages/<name>/
19
+ * - services/<name>/
20
+ * - apps/<name>/
21
+ * - libs/<name>/
22
+ *
23
+ * Each detected boundary owns a contiguous subtree of files. The TRUE root
24
+ * is the deepest manifest/glob root — so `packages/core/src/lib/foo.ts`
25
+ * belongs to `packages/core/` if a package.json sits there.
26
+ *
27
+ * Boundary `label` is derived from the manifest name (`@scope/pkg` → `pkg`,
28
+ * etc.) when present, else from the root_rel_path segment.
29
+ *
30
+ * Boundary dependencies come from cross-boundary call/import/service edges
31
+ * aggregated across resolved graphs. Strictly advisory — never gates anything.
32
+ */
33
+
34
+ import fs from 'fs';
35
+ import path from 'path';
36
+ import { Store } from '../db/store.js';
37
+
38
+ export interface BoundaryDef {
39
+ label: string;
40
+ kind: 'package' | 'service' | 'app' | 'lib' | 'workspace-root' | 'convention';
41
+ rootRelPath: string;
42
+ manifestPath: string | null;
43
+ ecosystem: string | null;
44
+ fileIds: number[];
45
+ }
46
+
47
+ export interface BoundaryEdgeDef {
48
+ fromIndex: number;
49
+ toIndex: number;
50
+ kind: 'call' | 'import' | 'service';
51
+ weight: number;
52
+ }
53
+
54
+ export interface BoundaryBuildResult {
55
+ boundaries: BoundaryDef[];
56
+ edges: BoundaryEdgeDef[];
57
+ /** Files that didn't match any boundary. */
58
+ orphanFiles: number;
59
+ }
60
+
61
+ interface ManifestHit {
62
+ relRoot: string;
63
+ manifestPath: string;
64
+ label: string;
65
+ kind: BoundaryDef['kind'];
66
+ ecosystem: string | null;
67
+ }
68
+
69
+ /**
70
+ * Detect boundaries by walking the workspace once for manifests + convention
71
+ * fallback, then assigning each indexed file to the deepest matching
72
+ * boundary root.
73
+ */
74
+ export function buildBoundaries(workspace: string, store: Store): BoundaryBuildResult {
75
+ const absRoot = path.resolve(workspace);
76
+
77
+ // Discover manifest hits.
78
+ const hits = discoverManifests(absRoot);
79
+ // Also seed convention-based hits when no manifest matches a directory.
80
+ seedConventionRoots(absRoot, hits);
81
+
82
+ // Materialize hits as boundary defs. De-dup by relRoot — manifest wins
83
+ // over convention.
84
+ const byRel = new Map<string, ManifestHit>();
85
+ for (const h of hits) {
86
+ const prev = byRel.get(h.relRoot);
87
+ if (!prev || rank(h) > rank(prev)) byRel.set(h.relRoot, h);
88
+ }
89
+ const sortedHits = Array.from(byRel.values()).sort((a, b) =>
90
+ b.relRoot.length - a.relRoot.length || (a.relRoot < b.relRoot ? -1 : 1));
91
+
92
+ // Assign every indexed file to the deepest matching hit.
93
+ const files = store.listFiles();
94
+ const fileToHit = new Map<number, ManifestHit | null>();
95
+ for (const f of files) {
96
+ let assigned: ManifestHit | null = null;
97
+ const rel = normalizePath(f.relPath);
98
+ for (const h of sortedHits) {
99
+ const root = h.relRoot;
100
+ if (root === '' || root === '.') continue;
101
+ if (rel === root || rel.startsWith(root + '/')) {
102
+ assigned = h;
103
+ break;
104
+ }
105
+ }
106
+ fileToHit.set(f.id, assigned);
107
+ }
108
+
109
+ // Build boundary list (only include hits that own at least one file).
110
+ const boundariesByRoot = new Map<string, BoundaryDef & { _index: number }>();
111
+ const definitions: BoundaryDef[] = [];
112
+ let nextIndex = 0;
113
+ for (const h of sortedHits) {
114
+ const def: BoundaryDef = {
115
+ label: h.label, kind: h.kind, rootRelPath: h.relRoot,
116
+ manifestPath: h.manifestPath || null, ecosystem: h.ecosystem,
117
+ fileIds: [],
118
+ };
119
+ boundariesByRoot.set(h.relRoot, { ...def, _index: nextIndex });
120
+ nextIndex++;
121
+ }
122
+ for (const [fileId, hit] of fileToHit) {
123
+ if (!hit) continue;
124
+ const b = boundariesByRoot.get(hit.relRoot);
125
+ if (!b) continue;
126
+ b.fileIds.push(fileId);
127
+ }
128
+ // Drop empty boundaries (e.g. convention `services/` parent with no files).
129
+ for (const b of boundariesByRoot.values()) {
130
+ if (b.fileIds.length === 0) continue;
131
+ definitions.push({
132
+ label: b.label, kind: b.kind, rootRelPath: b.rootRelPath,
133
+ manifestPath: b.manifestPath, ecosystem: b.ecosystem, fileIds: b.fileIds,
134
+ });
135
+ }
136
+ // Re-index after dropping.
137
+ const indexByRoot = new Map<string, number>();
138
+ definitions.forEach((d, i) => indexByRoot.set(d.rootRelPath, i));
139
+
140
+ // Build boundary→boundary edges from the resolved file-call / file-import /
141
+ // service-link graphs.
142
+ const edges = aggregateBoundaryEdges(store, fileToHit, indexByRoot);
143
+
144
+ let orphan = 0;
145
+ for (const [_id, h] of fileToHit) if (!h) orphan++;
146
+
147
+ return { boundaries: definitions, edges, orphanFiles: orphan };
148
+ }
149
+
150
+ function rank(h: ManifestHit): number {
151
+ // Manifest > convention.
152
+ return h.kind === 'convention' ? 0 : 1;
153
+ }
154
+
155
+ function aggregateBoundaryEdges(
156
+ store: Store,
157
+ fileToHit: Map<number, ManifestHit | null>,
158
+ indexByRoot: Map<string, number>,
159
+ ): BoundaryEdgeDef[] {
160
+ const buckets = new Map<string, BoundaryEdgeDef>();
161
+ const lookup = (fileId: number): number | null => {
162
+ const h = fileToHit.get(fileId) ?? null;
163
+ if (!h) return null;
164
+ const idx = indexByRoot.get(h.relRoot);
165
+ return idx == null ? null : idx;
166
+ };
167
+ const push = (from: number, to: number, kind: BoundaryEdgeDef['kind'], weight: number): void => {
168
+ if (from === to) return;
169
+ const key = `${from}|${to}|${kind}`;
170
+ const existing = buckets.get(key);
171
+ if (existing) existing.weight += weight;
172
+ else buckets.set(key, { fromIndex: from, toIndex: to, kind, weight });
173
+ };
174
+ for (const e of store.fileCallEdgeWeights()) {
175
+ const a = lookup(e.from); const b = lookup(e.to);
176
+ if (a != null && b != null) push(a, b, 'call', e.weight);
177
+ }
178
+ for (const e of store.fileImportEdgeWeights()) {
179
+ const a = lookup(e.from); const b = lookup(e.to);
180
+ if (a != null && b != null) push(a, b, 'import', e.weight);
181
+ }
182
+ try {
183
+ for (const e of store.fileServiceLinkEdgeWeights()) {
184
+ const a = lookup(e.from); const b = lookup(e.to);
185
+ if (a != null && b != null) push(a, b, 'service', e.weight);
186
+ }
187
+ } catch { /* */ }
188
+ return Array.from(buckets.values()).sort((a, b) =>
189
+ a.fromIndex - b.fromIndex || a.toIndex - b.toIndex || (a.kind < b.kind ? -1 : 1));
190
+ }
191
+
192
+ // ── Manifest discovery ──────────────────────────────────────────────────
193
+
194
+ function discoverManifests(absRoot: string): ManifestHit[] {
195
+ const hits: ManifestHit[] = [];
196
+ // Skip dirs that never own boundaries.
197
+ const SKIP = new Set([
198
+ 'node_modules', '.git', 'dist', 'build', 'out', '.next', '.nuxt',
199
+ 'target', 'obj', '.gradle', '__pycache__', '.cache', '.idea',
200
+ '.vs', '.seer',
201
+ ]);
202
+
203
+ function walk(absDir: string, relDir: string, depth: number): void {
204
+ if (depth > 6) return; // bound recursion — boundaries beyond ~6 levels are rare
205
+ let entries: string[];
206
+ try { entries = fs.readdirSync(absDir); }
207
+ catch { return; }
208
+ const fileSet = new Set(entries);
209
+ let claimed = false;
210
+
211
+ // package.json — may declare workspaces.
212
+ if (fileSet.has('package.json')) {
213
+ const manifestRel = relDir === '' ? 'package.json' : `${relDir}/package.json`;
214
+ try {
215
+ const pkg = JSON.parse(fs.readFileSync(path.join(absDir, 'package.json'), 'utf8'));
216
+ const label = derivePackageName(pkg, relDir);
217
+ const isRoot = Array.isArray(pkg.workspaces) || (pkg.workspaces && Array.isArray(pkg.workspaces.packages));
218
+ hits.push({
219
+ relRoot: relDir,
220
+ manifestPath: manifestRel,
221
+ label,
222
+ kind: isRoot ? 'workspace-root' : 'package',
223
+ ecosystem: 'npm',
224
+ });
225
+ claimed = true;
226
+ } catch { /* */ }
227
+ }
228
+ // pyproject.toml
229
+ if (fileSet.has('pyproject.toml')) {
230
+ const manifestRel = relDir === '' ? 'pyproject.toml' : `${relDir}/pyproject.toml`;
231
+ hits.push({
232
+ relRoot: relDir,
233
+ manifestPath: manifestRel,
234
+ label: derivePyProjectLabel(absDir, relDir),
235
+ kind: 'package',
236
+ ecosystem: 'pypi',
237
+ });
238
+ claimed = true;
239
+ }
240
+ // Cargo.toml
241
+ if (fileSet.has('Cargo.toml')) {
242
+ const manifestRel = relDir === '' ? 'Cargo.toml' : `${relDir}/Cargo.toml`;
243
+ hits.push({
244
+ relRoot: relDir,
245
+ manifestPath: manifestRel,
246
+ label: deriveCargoLabel(absDir, relDir),
247
+ kind: 'package',
248
+ ecosystem: 'cargo',
249
+ });
250
+ claimed = true;
251
+ }
252
+ // go.mod
253
+ if (fileSet.has('go.mod')) {
254
+ const manifestRel = relDir === '' ? 'go.mod' : `${relDir}/go.mod`;
255
+ hits.push({
256
+ relRoot: relDir,
257
+ manifestPath: manifestRel,
258
+ label: deriveGoModuleLabel(absDir, relDir),
259
+ kind: 'package',
260
+ ecosystem: 'go',
261
+ });
262
+ claimed = true;
263
+ }
264
+ // composer.json
265
+ if (fileSet.has('composer.json')) {
266
+ const manifestRel = relDir === '' ? 'composer.json' : `${relDir}/composer.json`;
267
+ hits.push({
268
+ relRoot: relDir,
269
+ manifestPath: manifestRel,
270
+ label: path.basename(relDir || '.'),
271
+ kind: 'package',
272
+ ecosystem: 'composer',
273
+ });
274
+ claimed = true;
275
+ }
276
+ void claimed;
277
+
278
+ // Recurse into subdirectories. Always recurse if THIS level didn't
279
+ // declare a non-root package — that's how packages/<x> work — but DO
280
+ // recurse anyway through workspace-root or convention dirs.
281
+ for (const entry of entries) {
282
+ if (SKIP.has(entry)) continue;
283
+ const abs = path.join(absDir, entry);
284
+ let st: fs.Stats;
285
+ try { st = fs.statSync(abs); }
286
+ catch { continue; }
287
+ if (!st.isDirectory()) continue;
288
+ const sub = relDir === '' ? entry : `${relDir}/${entry}`;
289
+ walk(abs, sub, depth + 1);
290
+ }
291
+ }
292
+
293
+ walk(absRoot, '', 0);
294
+ return hits;
295
+ }
296
+
297
+ function seedConventionRoots(absRoot: string, hits: ManifestHit[]): void {
298
+ // For each <conventionDir>/<sub>/ that exists and isn't already a manifest
299
+ // root, register a fallback boundary so services/* / packages/* still get
300
+ // surfaced even without a manifest.
301
+ const conventionDirs: Array<{ dir: string; kind: BoundaryDef['kind'] }> = [
302
+ { dir: 'services', kind: 'service' },
303
+ { dir: 'packages', kind: 'package' },
304
+ { dir: 'apps', kind: 'app' },
305
+ { dir: 'libs', kind: 'lib' },
306
+ ];
307
+ const existingRoots = new Set(hits.map(h => h.relRoot));
308
+ for (const c of conventionDirs) {
309
+ const abs = path.join(absRoot, c.dir);
310
+ if (!fs.existsSync(abs)) continue;
311
+ let entries: string[];
312
+ try { entries = fs.readdirSync(abs); }
313
+ catch { continue; }
314
+ for (const e of entries) {
315
+ const subAbs = path.join(abs, e);
316
+ try {
317
+ if (!fs.statSync(subAbs).isDirectory()) continue;
318
+ } catch { continue; }
319
+ const rel = `${c.dir}/${e}`;
320
+ if (existingRoots.has(rel)) continue;
321
+ hits.push({
322
+ relRoot: rel,
323
+ manifestPath: '',
324
+ label: e,
325
+ kind: c.kind,
326
+ ecosystem: null,
327
+ });
328
+ }
329
+ }
330
+ }
331
+
332
+ function derivePackageName(pkg: any, relDir: string): string {
333
+ const name = (pkg && pkg.name && typeof pkg.name === 'string') ? pkg.name : null;
334
+ if (!name) return path.basename(relDir || '.');
335
+ // Strip @scope/
336
+ const m = /^@[^/]+\/(.+)$/.exec(name);
337
+ return m ? m[1] : name;
338
+ }
339
+
340
+ function derivePyProjectLabel(absDir: string, relDir: string): string {
341
+ try {
342
+ const text = fs.readFileSync(path.join(absDir, 'pyproject.toml'), 'utf8');
343
+ const m = /^\s*name\s*=\s*['"]([^'"]+)['"]/m.exec(text);
344
+ if (m) return m[1];
345
+ } catch { /* */ }
346
+ return path.basename(relDir || '.');
347
+ }
348
+
349
+ function deriveCargoLabel(absDir: string, relDir: string): string {
350
+ try {
351
+ const text = fs.readFileSync(path.join(absDir, 'Cargo.toml'), 'utf8');
352
+ // Capture [package].name; skip [workspace] sections.
353
+ const pkgSection = /^\s*\[package\][\s\S]*?(?=^\s*\[)/m.exec(text)?.[0] ?? text;
354
+ const m = /^\s*name\s*=\s*['"]([^'"]+)['"]/m.exec(pkgSection);
355
+ if (m) return m[1];
356
+ } catch { /* */ }
357
+ return path.basename(relDir || '.');
358
+ }
359
+
360
+ function deriveGoModuleLabel(absDir: string, relDir: string): string {
361
+ try {
362
+ const text = fs.readFileSync(path.join(absDir, 'go.mod'), 'utf8');
363
+ const m = /^\s*module\s+([^\s]+)/m.exec(text);
364
+ if (m) {
365
+ // Take the last path segment.
366
+ return path.basename(m[1]);
367
+ }
368
+ } catch { /* */ }
369
+ return path.basename(relDir || '.');
370
+ }
371
+
372
+ function normalizePath(p: string): string {
373
+ return p.replace(/\\/g, '/');
374
+ }
@@ -0,0 +1,58 @@
1
+ import { Store } from '../db/store.js';
2
+ import { collectFileChurn, gitHeadSha, gitRemoteUrl, isGitRepo } from './git.js';
3
+
4
+ export interface ChurnResult {
5
+ filesAnalyzed: number;
6
+ filesWithChurn: number;
7
+ headSha: string | null;
8
+ elapsedMs: number;
9
+ }
10
+
11
+ /**
12
+ * File-level git churn pass — populates `file_churn` for every indexed file.
13
+ * Independent of the symbol-history pass: file churn is cheap (~one git log
14
+ * over the whole repo) and useful on its own as a "what's risky to edit"
15
+ * signal even before the per-symbol history pass.
16
+ */
17
+ export async function collectChurn(repoRoot: string, store: Store): Promise<ChurnResult> {
18
+ const start = Date.now();
19
+ if (!isGitRepo(repoRoot)) {
20
+ return { filesAnalyzed: 0, filesWithChurn: 0, headSha: null, elapsedMs: Date.now() - start };
21
+ }
22
+ const files = store.listFiles();
23
+ if (files.length === 0) {
24
+ return { filesAnalyzed: 0, filesWithChurn: 0, headSha: gitHeadSha(repoRoot), elapsedMs: Date.now() - start };
25
+ }
26
+ const churn = await collectFileChurn(repoRoot, files.map(f => f.path));
27
+ let withChurn = 0;
28
+ // Normalize path comparison the same way collectFileChurn does internally.
29
+ const norm = (p: string): string => {
30
+ const n = p.replace(/\\/g, '/');
31
+ return process.platform === 'win32' ? n.toLowerCase() : n;
32
+ };
33
+ const churnByNorm = new Map<string, ReturnType<typeof churn.get>>();
34
+ for (const [k, v] of churn) churnByNorm.set(norm(k), v);
35
+
36
+ for (const f of files) {
37
+ const stats = churnByNorm.get(norm(f.path));
38
+ if (!stats || stats.commitCount === 0) continue;
39
+ store.upsertFileChurn(
40
+ f.id, stats.commitCount, stats.lastCommitSha, stats.lastCommitAt,
41
+ stats.topAuthor, stats.secondAuthor,
42
+ );
43
+ withChurn++;
44
+ }
45
+
46
+ // Stamp git_index_state with HEAD so detect_changes can compute "since
47
+ // last index" diffs.
48
+ const head = gitHeadSha(repoRoot);
49
+ const remote = gitRemoteUrl(repoRoot);
50
+ store.setGitIndexState(repoRoot, head, remote);
51
+
52
+ return {
53
+ filesAnalyzed: files.length,
54
+ filesWithChurn: withChurn,
55
+ headSha: head,
56
+ elapsedMs: Date.now() - start,
57
+ };
58
+ }
@@ -0,0 +1,96 @@
1
+ import type { FileClassification, FileRole } from '../db/store.js';
2
+
3
+ /**
4
+ * Classify a discovered file path as project-owned, vendored, generated, or
5
+ * test. The result is stored on `files.role` and used to keep PageRank,
6
+ * top-symbol queries, and search defaults focused on first-party code while
7
+ * still letting users opt into vendored/generated results explicitly.
8
+ *
9
+ * Conservative defaults — when in doubt, return 'project' so we don't quietly
10
+ * hide first-party code. The patterns here are kept tight on purpose; broader
11
+ * heuristics (e.g. "anything inside a directory called `lib`") would cause
12
+ * too many false positives across the polyglot scale-test corpus.
13
+ *
14
+ * The function works purely on the relative path string; no filesystem
15
+ * access. That keeps it cheap to call once per discovered file in the
16
+ * indexer hot loop.
17
+ */
18
+
19
+ // Vendored dependency directories at any nesting depth. The discovery layer
20
+ // already excludes most of these from the glob, but a few make it through
21
+ // (e.g. project-local copies of small utilities placed under `lib/vendor/`).
22
+ // We still tag those that get past discovery so the stored classification is
23
+ // useful even on unusual repo layouts.
24
+ const VENDOR_DIR_PATTERNS = [
25
+ /(^|[\\/])vendor[\\/]/i,
26
+ /(^|[\\/])vendored[\\/]/i,
27
+ /(^|[\\/])third[_-]?party[\\/]/i,
28
+ /(^|[\\/])external[\\/]/i,
29
+ /(^|[\\/])node_modules[\\/]/i,
30
+ /(^|[\\/])bower_components[\\/]/i,
31
+ // Common engine-specific vendored locations.
32
+ /(^|[\\/])Engine[\\/]Source[\\/]ThirdParty[\\/]/i,
33
+ ];
34
+
35
+ // Generated boilerplate. Filename patterns covering protobuf, Unreal header
36
+ // tool, gRPC, gqlgen, and a handful of common code-generators. We also tag
37
+ // files that live under a `generated/` directory since most projects put
38
+ // emitter output there.
39
+ const GENERATED_DIR_PATTERNS = [
40
+ /(^|[\\/])generated[\\/]/i,
41
+ /(^|[\\/])Generated[\\/]/, // Unreal Engine convention
42
+ /(^|[\\/])\.next[\\/]/,
43
+ /(^|[\\/])\.nuxt[\\/]/,
44
+ /(^|[\\/])__generated__[\\/]/,
45
+ ];
46
+
47
+ const GENERATED_FILENAME_PATTERNS = [
48
+ /\.generated\.[a-z]+$/i,
49
+ /\.gen\.[a-z]+$/i,
50
+ /\.pb\.[a-z]+$/i, // protobuf .pb.go / .pb.h / .pb.ts
51
+ /\.pb\.go$/,
52
+ /_pb\.[a-z]+$/i, // gqlgen / Python grpc style
53
+ /\.min\.(js|css)$/i,
54
+ /\.bundle\.js$/i,
55
+ ];
56
+
57
+ // Test directories — exposed for completeness even though we don't yet use
58
+ // the 'test' role to filter anywhere. Future work: surface test files in
59
+ // `seer_behavior` as a behavioral contract.
60
+ const TEST_DIR_PATTERNS = [
61
+ /(^|[\\/])tests?[\\/]/i,
62
+ /(^|[\\/])__tests__[\\/]/,
63
+ /(^|[\\/])spec[\\/]/i,
64
+ ];
65
+
66
+ const TEST_FILENAME_PATTERNS = [
67
+ /\.test\.[a-z]+$/i,
68
+ /\.spec\.[a-z]+$/i,
69
+ /_test\.[a-z]+$/i, // Go convention: foo_test.go
70
+ ];
71
+
72
+ /**
73
+ * Compute classification flags for a discovered file. The role precedence is
74
+ * vendor → generated → test → project. Vendor wins over generated because a
75
+ * generated file inside a vendor tree is still vendored code we don't own;
76
+ * the `is_generated` flag remains true so users can still query for it.
77
+ */
78
+ export function classifyFile(relativePath: string): FileClassification {
79
+ const isVendor = VENDOR_DIR_PATTERNS.some(p => p.test(relativePath)) ? 1 : 0;
80
+ const isGenerated = (
81
+ GENERATED_DIR_PATTERNS.some(p => p.test(relativePath)) ||
82
+ GENERATED_FILENAME_PATTERNS.some(p => p.test(relativePath))
83
+ ) ? 1 : 0;
84
+ const isTest = (
85
+ TEST_DIR_PATTERNS.some(p => p.test(relativePath)) ||
86
+ TEST_FILENAME_PATTERNS.some(p => p.test(relativePath))
87
+ );
88
+
89
+ let role: FileRole;
90
+ if (isVendor) role = 'vendor';
91
+ else if (isGenerated) role = 'generated';
92
+ else if (isTest) role = 'test';
93
+ else role = 'project';
94
+
95
+ return { role, isVendor: isVendor as 0 | 1, isGenerated: isGenerated as 0 | 1 };
96
+ }