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,1402 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const commander_1 = require("commander");
41
+ const path_1 = __importDefault(require("path"));
42
+ const fs_1 = __importDefault(require("fs"));
43
+ const index_js_1 = require("../indexer/index.js");
44
+ const store_js_1 = require("../db/store.js");
45
+ const behavior_js_1 = require("../indexer/behavior.js");
46
+ const risk_js_1 = require("../indexer/risk.js");
47
+ const context_js_1 = require("../indexer/context.js");
48
+ const init_js_1 = require("./init.js");
49
+ const VERSION = '0.1.0';
50
+ const KNOWN_CLIENTS = ['claude', 'cursor', 'vscode', 'codex', 'gemini', 'antigravity'];
51
+ function resolveDb(repoPath, customDb) {
52
+ if (customDb)
53
+ return path_1.default.resolve(customDb);
54
+ const seerDir = path_1.default.join(path_1.default.resolve(repoPath), '.seer');
55
+ if (!fs_1.default.existsSync(seerDir))
56
+ fs_1.default.mkdirSync(seerDir, { recursive: true });
57
+ return path_1.default.join(seerDir, 'graph.db');
58
+ }
59
+ function openStore(dbPath, mutable = false) {
60
+ if (!fs_1.default.existsSync(dbPath)) {
61
+ console.error(`No index found at ${dbPath}. Run "seer index <path>" first.`);
62
+ process.exit(1);
63
+ }
64
+ return mutable ? new store_js_1.Store(dbPath) : store_js_1.Store.openReadOnly(dbPath);
65
+ }
66
+ // ── Program ────────────────────────────────────────────────────────────────────
67
+ const program = new commander_1.Command();
68
+ program
69
+ .name('seer')
70
+ .description('Local-first AI codebase explainer')
71
+ .version(VERSION);
72
+ // ── seer index ───────────────────────────────────────────────────────────────
73
+ program
74
+ .command('index <repo-path>')
75
+ .description('Index a repository into a local SQLite graph')
76
+ .option('--db <path>', 'Custom database path (default: <repo>/.seer/graph.db)')
77
+ .option('-v, --verbose', 'Show per-file progress')
78
+ .option('--reset', 'Delete existing index before re-indexing')
79
+ .option('--max-file-kb <kb>', 'Skip files larger than this (KiB). 0 = no cap (default).', '0')
80
+ .option('--include-vendor', 'Index vendor/ vendored/ thirdparty/ directories')
81
+ .option('--include-generated', 'Index *.generated.* / *.pb.* / *.gen.* files')
82
+ .option('--mode <mode>', 'Discovery mode: full | standard | fast (default: standard).', 'standard')
83
+ .option('--parallel', 'Force worker-thread parsing even for tiny repositories')
84
+ .option('--no-parallel', 'Disable worker-thread parsing; auto mode uses workers for normal/large repos')
85
+ .option('--jobs <n>', 'Worker thread count when worker parsing is active (default: cores - 1, capped at 8)')
86
+ .action(async (repoPath, opts) => {
87
+ const absRepo = path_1.default.resolve(repoPath);
88
+ if (!fs_1.default.existsSync(absRepo)) {
89
+ console.error(`Path not found: ${absRepo}`);
90
+ process.exit(1);
91
+ }
92
+ const dbPath = resolveDb(absRepo, opts.db);
93
+ if (opts.reset && fs_1.default.existsSync(dbPath)) {
94
+ fs_1.default.unlinkSync(dbPath);
95
+ console.log(` Removed existing index: ${dbPath}`);
96
+ }
97
+ console.log(`\nSeer Index`);
98
+ console.log(` Repo: ${absRepo}`);
99
+ console.log(` DB: ${dbPath}\n`);
100
+ const store = new store_js_1.Store(dbPath);
101
+ const indexer = new index_js_1.Indexer(store);
102
+ try {
103
+ const maxKb = parseInt(opts.maxFileKb, 10);
104
+ const maxFileBytes = isNaN(maxKb) || maxKb <= 0 ? 0 : maxKb * 1024;
105
+ const mode = parseMode(opts.mode);
106
+ // `--parallel` / `--no-parallel` force the parser mode; otherwise the
107
+ // indexer uses auto mode (workers for normal/large repos, serial for tiny).
108
+ const jobsN = opts.jobs ? parseInt(opts.jobs, 10) : undefined;
109
+ const result = await indexer.indexDirectory(absRepo, {
110
+ verbose: opts.verbose,
111
+ reset: opts.reset,
112
+ maxFileBytes,
113
+ includeVendor: opts.includeVendor,
114
+ includeGenerated: opts.includeGenerated,
115
+ mode,
116
+ parallel: opts.parallel,
117
+ jobs: jobsN != null && !isNaN(jobsN) && jobsN > 0 ? jobsN : undefined,
118
+ });
119
+ console.log(`\n ✓ Indexed ${result.filesIndexed.toLocaleString()} files`);
120
+ if (result.filesReusedFromCache > 0)
121
+ console.log(` ${result.filesReusedFromCache.toLocaleString()} reused from cache`);
122
+ if (result.filesSkipped > 0)
123
+ console.log(` ${result.filesSkipped.toLocaleString()} skipped`);
124
+ if (result.filesSkippedTooLarge > 0)
125
+ console.log(` ${result.filesSkippedTooLarge.toLocaleString()} skipped (too large)`);
126
+ if (result.filesParseError > 0)
127
+ console.log(` ${result.filesParseError.toLocaleString()} parse errors`);
128
+ if (result.wasmResets > 0)
129
+ console.log(` ${result.wasmResets} WASM reset(s)`);
130
+ console.log(` ✓ ${result.symbols.toLocaleString()} symbols`);
131
+ console.log(` ✓ ${result.edges.toLocaleString()} edges (${result.resolvedEdges.toLocaleString()} resolved)`);
132
+ console.log(` ✓ ${result.resolvedImports.toLocaleString()} imports resolved`);
133
+ if ((result.routesResolved ?? 0) > 0)
134
+ console.log(` ✓ ${result.routesResolved} routes linked to handlers`);
135
+ if ((result.testEdgesAdded ?? 0) > 0)
136
+ console.log(` ✓ ${result.testEdgesAdded} test edges synthesized`);
137
+ if ((result.externalDependencies ?? 0) > 0)
138
+ console.log(` ✓ ${result.externalDependencies} external deps`);
139
+ if (result.pagerankRecomputed)
140
+ console.log(` ✓ PageRank computed`);
141
+ else
142
+ console.log(` ↻ PageRank reused (graph unchanged)`);
143
+ console.log(`\n Done in ${(result.elapsedMs / 1000).toFixed(1)}s`);
144
+ }
145
+ finally {
146
+ store.close();
147
+ }
148
+ });
149
+ // ── seer init ──────────────────────────────────────────────────────────────
150
+ program
151
+ .command('init [workspace]')
152
+ .description('Wire Seer in as an MCP server for your AI agents and write an AGENTS.md usage guide')
153
+ .option('--db <path>', 'Custom database path passed through to the MCP launcher')
154
+ .option('--client <names>', 'Comma-separated clients: claude,cursor,vscode,codex,gemini,antigravity,all (default: claude,cursor,vscode,codex,gemini)')
155
+ .option('--global', 'Write user-level config instead of project-local config')
156
+ .option('--npx', 'Emit a portable "npx -y <pkg> mcp" launcher instead of an absolute node path')
157
+ .option('--pkg <name>', 'npm package name used by the --npx launcher', 'seer-mcp')
158
+ .option('--command <cmd>', 'Override the launch command entirely (advanced)')
159
+ .option('--no-agents', 'Do not write the AGENTS.md guidance block')
160
+ .option('--print', 'Print the plan without writing any files')
161
+ .option('--force', 'Overwrite an existing seer entry / agents block')
162
+ .action((workspace, opts) => {
163
+ const ws = path_1.default.resolve(workspace ?? process.cwd());
164
+ if (!fs_1.default.existsSync(ws)) {
165
+ console.error(`Workspace not found: ${ws}`);
166
+ process.exit(1);
167
+ }
168
+ let clients;
169
+ if (opts.client) {
170
+ const names = opts.client.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean);
171
+ if (names.includes('all')) {
172
+ clients = KNOWN_CLIENTS;
173
+ }
174
+ else {
175
+ const bad = names.filter((n) => !KNOWN_CLIENTS.includes(n));
176
+ if (bad.length) {
177
+ console.error(`Unknown client(s): ${bad.join(', ')}. Known: ${KNOWN_CLIENTS.join(', ')}, all`);
178
+ process.exit(1);
179
+ }
180
+ clients = names;
181
+ }
182
+ }
183
+ const result = (0, init_js_1.runInit)({
184
+ workspace: ws,
185
+ clients,
186
+ global: opts.global,
187
+ npx: opts.npx,
188
+ pkg: opts.pkg,
189
+ command: opts.command,
190
+ agents: opts.agents,
191
+ print: opts.print,
192
+ force: opts.force,
193
+ db: opts.db,
194
+ });
195
+ console.log(`\nSeer Init ${opts.print ? '(dry run — nothing written)' : ''}`);
196
+ console.log(` Workspace: ${ws}`);
197
+ console.log(` Launcher: ${result.launch.command} ${result.launch.args.join(' ')}\n`);
198
+ const mark = opts.print
199
+ ? { wrote: '+ would write ', updated: '~ would update', skipped: '· would skip ', manual: '! manual ' }
200
+ : { wrote: '✓ wrote ', updated: '✓ updated', skipped: '· skipped', manual: '! manual ' };
201
+ for (const e of result.entries) {
202
+ console.log(` ${mark[e.action] ?? e.action} ${e.label.padEnd(28)} ${e.file}`);
203
+ if (e.note)
204
+ console.log(` ${e.note}`);
205
+ if (e.snippet && (opts.print || e.action === 'manual')) {
206
+ console.log(e.snippet.split('\n').map((l) => ' ' + l).join('\n'));
207
+ }
208
+ }
209
+ if (result.agents) {
210
+ console.log(` ${mark[result.agents.action] ?? result.agents.action} ${'AGENTS.md (agent guide)'.padEnd(28)} ${result.agents.file}`);
211
+ }
212
+ console.log(`\n Next:`);
213
+ console.log(` 1. Reload / restart your agent so it picks up the new MCP server.`);
214
+ console.log(` 2. Seer indexes this workspace automatically on first query.`);
215
+ console.log(` 3. Ask your agent to call seer_health to confirm it is connected.\n`);
216
+ });
217
+ // ── seer callers / callees / symbols / stats / health ──────────────────────────
218
+ program
219
+ .command('callers <symbol>')
220
+ .description('Find all callers of a symbol')
221
+ .option('--db <path>', 'Database path')
222
+ .option('-n, --limit <n>', 'Max results', '40')
223
+ // Callers query is keyed by `edges.to_name`, not by symbol_role / vendor /
224
+ // test flags. The include-* options are accepted for surface consistency
225
+ // with the rest of the CLI but don't currently change results.
226
+ .action((symbol, opts) => {
227
+ const dbPath = opts.db ?? findDbFromCwd();
228
+ const store = openStore(dbPath);
229
+ try {
230
+ const total = store.countCallers(symbol);
231
+ if (total === 0) {
232
+ console.log(`No callers found for "${symbol}"`);
233
+ return;
234
+ }
235
+ const limit = Math.max(1, parseInt(opts.limit, 10) || 40);
236
+ const callers = store.findCallers(symbol, limit);
237
+ console.log(`\nCallers of '${symbol}' (${total} found)\n`);
238
+ for (const c of callers) {
239
+ const loc = `${c.callerFile}:${c.callerLine + 1}`;
240
+ console.log(` ${c.callerName.padEnd(32)} ${c.callerKind.padEnd(12)} ${loc}`);
241
+ }
242
+ if (total > callers.length)
243
+ console.log(` … and ${total - callers.length} more`);
244
+ }
245
+ finally {
246
+ store.close();
247
+ }
248
+ });
249
+ program
250
+ .command('callees <symbol>')
251
+ .description('Find all symbols called by a symbol')
252
+ .option('--db <path>', 'Database path')
253
+ .option('-n, --limit <n>', 'Max results', '40')
254
+ .action((symbol, opts) => {
255
+ const dbPath = opts.db ?? findDbFromCwd();
256
+ const store = openStore(dbPath);
257
+ try {
258
+ const callees = store.findCallees(symbol);
259
+ if (callees.length === 0) {
260
+ console.log(`No callees found for "${symbol}"`);
261
+ return;
262
+ }
263
+ console.log(`\nCallees of '${symbol}' (${callees.length} found)\n`);
264
+ const limit = Math.min(parseInt(opts.limit, 10), callees.length);
265
+ for (const c of callees.slice(0, limit)) {
266
+ const loc = c.calleeFile ? `${c.calleeFile}:${(c.calleeLineStart ?? 0) + 1}` : '(unresolved)';
267
+ const kind = c.calleeKind ?? '?';
268
+ console.log(` ${c.calleeName.padEnd(32)} ${kind.padEnd(12)} ${loc}`);
269
+ }
270
+ if (callees.length > limit)
271
+ console.log(` … and ${callees.length - limit} more`);
272
+ }
273
+ finally {
274
+ store.close();
275
+ }
276
+ });
277
+ program
278
+ .command('symbols [query]')
279
+ .description('Search symbols by name, or list top symbols by PageRank')
280
+ .option('--db <path>', 'Database path')
281
+ .option('--file <path>', 'Filter to symbols in a specific file')
282
+ .option('-n, --top <n>', 'Show top N symbols by PageRank (default: 20)', '20')
283
+ .option('--include-vendor', 'Include vendored code (off by default)')
284
+ .option('--include-generated', 'Include generated code (off by default)')
285
+ .option('--include-tests', 'Include symbols from test files (off by default)')
286
+ .option('--include-declarations', 'Include forward / class-body declarations (off by default)')
287
+ .option('--include-type-refs', 'Include bare type-reference rows (off by default; not yet emitted)')
288
+ .action((query, opts) => {
289
+ const dbPath = opts.db ?? findDbFromCwd();
290
+ const store = openStore(dbPath);
291
+ try {
292
+ const limit = parseInt(opts.top, 10);
293
+ const includeOpts = {
294
+ includeVendor: opts.includeVendor,
295
+ includeGenerated: opts.includeGenerated,
296
+ includeTests: opts.includeTests,
297
+ includeDeclarations: opts.includeDeclarations,
298
+ includeTypeRefs: opts.includeTypeRefs,
299
+ };
300
+ let symbols;
301
+ if (opts.file) {
302
+ symbols = store.listSymbolsInFile(opts.file, limit);
303
+ console.log(`\nSymbols in ${opts.file}\n`);
304
+ }
305
+ else if (query) {
306
+ symbols = store.findSymbols(query, includeOpts);
307
+ console.log(`\nSymbols matching '${query}'\n`);
308
+ }
309
+ else {
310
+ symbols = store.getTopSymbols(limit, includeOpts);
311
+ console.log(`\nTop ${limit} symbols by PageRank\n`);
312
+ }
313
+ if (symbols.length === 0) {
314
+ console.log(' (none found)');
315
+ return;
316
+ }
317
+ console.log(` ${'Name'.padEnd(32)} ${'Kind'.padEnd(12)} ${'Line'.padEnd(6)} ${'PageRank'.padEnd(10)} ${'Role'.padEnd(11)} File`);
318
+ console.log(' ' + '─'.repeat(102));
319
+ for (const s of symbols) {
320
+ const pr = s.pagerank.toFixed(4);
321
+ const loc = String(s.lineStart + 1).padEnd(6);
322
+ const relFile = s.filePath.replace(/\\/g, '/');
323
+ const role = (s.symbolRole ?? 'definition').padEnd(11);
324
+ console.log(` ${s.name.padEnd(32)} ${s.kind.padEnd(12)} ${loc} ${pr.padEnd(10)} ${role} ${relFile}`);
325
+ }
326
+ }
327
+ finally {
328
+ store.close();
329
+ }
330
+ });
331
+ program
332
+ .command('stats')
333
+ .description('Show index statistics')
334
+ .option('--db <path>', 'Database path')
335
+ .action((opts) => {
336
+ const dbPath = opts.db ?? findDbFromCwd();
337
+ const store = openStore(dbPath);
338
+ try {
339
+ const stats = store.getStats();
340
+ console.log('\nSeer Index Stats');
341
+ console.log('──────────────────');
342
+ console.log(` Files: ${stats.files.toLocaleString()}`);
343
+ console.log(` Symbols: ${stats.symbols.toLocaleString()}`);
344
+ console.log(` Edges: ${stats.edges.toLocaleString()}`);
345
+ console.log(` Resolved edges: ${stats.resolvedEdges.toLocaleString()}`);
346
+ if (stats.routes != null)
347
+ console.log(` Routes: ${stats.routes.toLocaleString()}`);
348
+ if (stats.externalDependencies != null)
349
+ console.log(` External deps: ${stats.externalDependencies.toLocaleString()}`);
350
+ if (stats.configKeys != null)
351
+ console.log(` Config keys: ${stats.configKeys.toLocaleString()}`);
352
+ if (stats.symbolHistory != null)
353
+ console.log(` Symbol history: ${stats.symbolHistory.toLocaleString()}`);
354
+ console.log(` Languages:`);
355
+ for (const [lang, count] of Object.entries(stats.languages).sort((a, b) => b[1] - a[1])) {
356
+ console.log(` ${lang.padEnd(14)} ${count}`);
357
+ }
358
+ console.log(`\n DB: ${dbPath}`);
359
+ }
360
+ finally {
361
+ store.close();
362
+ }
363
+ });
364
+ program
365
+ .command('health')
366
+ .description('Show Seer index health')
367
+ .option('--db <path>', 'Database path')
368
+ .action((opts) => {
369
+ const dbPath = opts.db ?? findDbFromCwd();
370
+ const store = openStore(dbPath);
371
+ try {
372
+ const schema = store.schemaInfo();
373
+ const stats = store.getStats();
374
+ console.log('\nSeer Health');
375
+ console.log('─────────────');
376
+ console.log(` DB path: ${dbPath}`);
377
+ console.log(` Read-only: ${store.isReadOnly()}`);
378
+ console.log(` Schema version: ${schema.dbVersion} (build expects ${schema.buildVersion})`);
379
+ if (!schema.current)
380
+ console.log(` ⚠ Schema is behind. Run \`seer index <path>\` to migrate.`);
381
+ else
382
+ console.log(` ✓ Schema is up to date.`);
383
+ console.log(` Files: ${stats.files.toLocaleString()}`);
384
+ console.log(` Symbols: ${stats.symbols.toLocaleString()}`);
385
+ console.log(` Edges: ${stats.edges.toLocaleString()} (${stats.resolvedEdges.toLocaleString()} resolved)`);
386
+ if (stats.roles) {
387
+ const t = stats.roles.project + stats.roles.vendor + stats.roles.generated + stats.roles.test;
388
+ console.log(` File roles: project ${stats.roles.project} vendor ${stats.roles.vendor} generated ${stats.roles.generated} test ${stats.roles.test} (${t} total)`);
389
+ }
390
+ if (stats.routes != null && stats.routes > 0)
391
+ console.log(` Routes: ${stats.routes.toLocaleString()}`);
392
+ if (stats.externalDependencies != null && stats.externalDependencies > 0)
393
+ console.log(` External deps: ${stats.externalDependencies.toLocaleString()}`);
394
+ if (stats.configKeys != null && stats.configKeys > 0)
395
+ console.log(` Config keys: ${stats.configKeys.toLocaleString()}`);
396
+ if (stats.symbolHistory != null && stats.symbolHistory > 0)
397
+ console.log(` Symbol history: ${stats.symbolHistory.toLocaleString()} rows`);
398
+ }
399
+ finally {
400
+ store.close();
401
+ }
402
+ });
403
+ // ── seer routes ──────────────────────────────────────────────────────────────
404
+ program
405
+ .command('routes')
406
+ .description('List HTTP routes detected in the codebase')
407
+ .option('--db <path>', 'Database path')
408
+ .option('--method <m>', 'Filter by HTTP method (GET/POST/...)')
409
+ .option('--framework <f>', 'Filter by framework (express/fastapi/flask/spring)')
410
+ .option('--path <substr>', 'Filter by path substring')
411
+ .option('-n, --limit <n>', 'Max results', '50')
412
+ .action((opts) => {
413
+ const dbPath = opts.db ?? findDbFromCwd();
414
+ const store = openStore(dbPath);
415
+ try {
416
+ const rows = store.listRoutes({
417
+ method: opts.method,
418
+ framework: opts.framework,
419
+ pathSubstr: opts.path,
420
+ limit: parseInt(opts.limit, 10) || 50,
421
+ });
422
+ if (rows.length === 0) {
423
+ console.log('No routes found.');
424
+ return;
425
+ }
426
+ console.log(`\nRoutes (${rows.length} shown)\n`);
427
+ for (const r of rows) {
428
+ const h = r.handlerSymbol ? `→ ${r.handlerSymbol}` : (r.handlerName ? `→ ${r.handlerName} (unresolved)` : '');
429
+ console.log(` ${r.method.padEnd(6)} ${r.path.padEnd(40)} ${r.framework.padEnd(10)} ${h}`);
430
+ }
431
+ }
432
+ finally {
433
+ store.close();
434
+ }
435
+ });
436
+ // ── seer service-calls / service-links / trace-service ──────────────────────
437
+ program
438
+ .command('service-calls')
439
+ .description('List outbound HTTP/service client calls detected in the codebase')
440
+ .option('--db <path>', 'Database path')
441
+ .option('--protocol <p>', 'Filter by protocol (http)')
442
+ .option('--method <m>', 'Filter by HTTP method (GET/POST/...)')
443
+ .option('--framework <f>', 'Filter by client framework (fetch/axios/requests/...)')
444
+ .option('--path <substr>', 'Filter by normalized path substring')
445
+ .option('--min-confidence <c>', 'Minimum confidence 0..1', '0')
446
+ .option('-n, --limit <n>', 'Max results', '100')
447
+ .option('--offset <n>', 'Skip first N results', '0')
448
+ .action((opts) => {
449
+ const dbPath = opts.db ?? findDbFromCwd();
450
+ const store = openStore(dbPath);
451
+ try {
452
+ const rows = store.listServiceCalls({
453
+ protocol: opts.protocol,
454
+ method: opts.method,
455
+ framework: opts.framework,
456
+ pathSubstr: opts.path,
457
+ minConfidence: parseFloat(opts.minConfidence) || 0,
458
+ limit: parseInt(opts.limit, 10) || 100,
459
+ offset: parseInt(opts.offset, 10) || 0,
460
+ });
461
+ if (rows.length === 0) {
462
+ console.log('No service calls found.');
463
+ return;
464
+ }
465
+ console.log(`\nService calls (${rows.length} shown)\n`);
466
+ for (const r of rows) {
467
+ const caller = r.callerQualifiedName ?? r.callerName ?? '(module-level)';
468
+ const target = r.normalizedPath ?? r.rawTarget;
469
+ const host = r.hostHint ? ` host=${r.hostHint}` : '';
470
+ const env = r.envKey ? ` env=${r.envKey}` : '';
471
+ console.log(` ${(r.method ?? 'ANY').padEnd(6)} ${target.padEnd(40)} ` +
472
+ `${r.framework.padEnd(12)} ${caller.padEnd(28)} ${r.filePath}:${r.line + 1}${host}${env}`);
473
+ }
474
+ }
475
+ finally {
476
+ store.close();
477
+ }
478
+ });
479
+ program
480
+ .command('service-links')
481
+ .description('List deterministic service-link rendezvous between client calls and route handlers')
482
+ .option('--db <path>', 'Database path')
483
+ .option('--protocol <p>', 'Filter by protocol (http)')
484
+ .option('--method <m>', 'Filter by HTTP method')
485
+ .option('--path <substr>', 'Filter by call/route path substring')
486
+ .option('--match-kind <k>', 'Filter by match_kind (literal_path/env_base/route_pattern)')
487
+ .option('--min-confidence <c>', 'Minimum confidence 0..1', '0')
488
+ .option('-n, --limit <n>', 'Max results', '100')
489
+ .option('--offset <n>', 'Skip first N results', '0')
490
+ .action((opts) => {
491
+ const dbPath = opts.db ?? findDbFromCwd();
492
+ const store = openStore(dbPath);
493
+ try {
494
+ const rows = store.listServiceLinks({
495
+ protocol: opts.protocol,
496
+ method: opts.method,
497
+ pathSubstr: opts.path,
498
+ matchKind: opts.matchKind,
499
+ minConfidence: parseFloat(opts.minConfidence) || 0,
500
+ limit: parseInt(opts.limit, 10) || 100,
501
+ offset: parseInt(opts.offset, 10) || 0,
502
+ });
503
+ if (rows.length === 0) {
504
+ console.log('No service links found.');
505
+ return;
506
+ }
507
+ console.log(`\nService links (${rows.length} shown)\n`);
508
+ for (const r of rows) {
509
+ const caller = r.callerQualifiedName ?? r.callerName ?? '(module-level)';
510
+ const handler = r.handlerQualifiedName ?? r.handlerName ?? '(no handler)';
511
+ const route = r.routePath ?? r.callNormalizedPath ?? r.callRawTarget;
512
+ console.log(` ${(r.callMethod ?? 'ANY').padEnd(6)} ${(route ?? '').padEnd(36)} ` +
513
+ `${caller.padEnd(22)} → ${handler.padEnd(22)} ` +
514
+ `[${r.matchKind} ${r.confidence.toFixed(2)}]`);
515
+ }
516
+ }
517
+ finally {
518
+ store.close();
519
+ }
520
+ });
521
+ program
522
+ .command('trace-service <from> <to>')
523
+ .description('Find a shortest service-link path between two symbols (bounded BFS)')
524
+ .option('--db <path>', 'Database path')
525
+ .option('--depth <n>', 'Max BFS depth', '6')
526
+ .action((from, to, opts) => {
527
+ const dbPath = opts.db ?? findDbFromCwd();
528
+ const store = openStore(dbPath);
529
+ try {
530
+ const fromRows = store.getDefinition(from);
531
+ const toRows = store.getDefinition(to);
532
+ if (fromRows.length === 0) {
533
+ console.log(`Source symbol "${from}" not found.`);
534
+ return;
535
+ }
536
+ if (toRows.length === 0) {
537
+ console.log(`Target symbol "${to}" not found.`);
538
+ return;
539
+ }
540
+ const path = store.traceServicePath(fromRows[0].id, toRows[0].id, parseInt(opts.depth, 10) || 6);
541
+ if (path.length === 0) {
542
+ console.log('No service-link path found.');
543
+ return;
544
+ }
545
+ console.log(`\nService path (${path.length} hops):\n`);
546
+ for (const id of path) {
547
+ const row = store.rawDb().prepare(`SELECT qualified_name, name FROM symbols WHERE id = ?`).get(id);
548
+ const label = row?.qualified_name ?? row?.name ?? `#${id}`;
549
+ console.log(` → ${label}`);
550
+ }
551
+ }
552
+ finally {
553
+ store.close();
554
+ }
555
+ });
556
+ // ── seer deps ────────────────────────────────────────────────────────────────
557
+ program
558
+ .command('deps')
559
+ .description('List external dependencies declared in manifests')
560
+ .option('--db <path>', 'Database path')
561
+ .option('--ecosystem <e>', 'Filter (npm/cargo/pypi/go)')
562
+ .option('--name <substr>', 'Filter by name substring')
563
+ .option('-n, --limit <n>', 'Max results', '100')
564
+ .action((opts) => {
565
+ const dbPath = opts.db ?? findDbFromCwd();
566
+ const store = openStore(dbPath);
567
+ try {
568
+ const rows = store.listExternalDeps({
569
+ ecosystem: opts.ecosystem, nameSubstr: opts.name,
570
+ limit: parseInt(opts.limit, 10) || 100,
571
+ });
572
+ if (rows.length === 0) {
573
+ console.log('No external dependencies indexed.');
574
+ return;
575
+ }
576
+ console.log(`\nExternal dependencies (${rows.length} shown)\n`);
577
+ for (const r of rows) {
578
+ console.log(` ${r.ecosystem.padEnd(8)} ${r.name.padEnd(40)} ${r.versionRange ?? ''}${r.isDev ? ' (dev)' : ''}`);
579
+ }
580
+ }
581
+ finally {
582
+ store.close();
583
+ }
584
+ });
585
+ // ── seer config ──────────────────────────────────────────────────────────────
586
+ program
587
+ .command('config')
588
+ .description('List config / env reads detected in the codebase')
589
+ .option('--db <path>', 'Database path')
590
+ .option('--key <substr>', 'Filter by key substring')
591
+ .option('-n, --limit <n>', 'Max results', '50')
592
+ .action((opts) => {
593
+ const dbPath = opts.db ?? findDbFromCwd();
594
+ const store = openStore(dbPath);
595
+ try {
596
+ const rows = store.listConfigKeys({ key: opts.key, limit: parseInt(opts.limit, 10) || 50 });
597
+ if (rows.length === 0) {
598
+ console.log('No config keys indexed.');
599
+ return;
600
+ }
601
+ console.log(`\nConfig keys (${rows.length} shown)\n`);
602
+ for (const r of rows) {
603
+ console.log(` ${r.source.padEnd(6)} ${r.key.padEnd(30)} ${r.filePath}:${r.line + 1} ${r.symbolName ?? ''}`);
604
+ }
605
+ }
606
+ finally {
607
+ store.close();
608
+ }
609
+ });
610
+ // ── seer churn (file-level git churn pass) ──────────────────────────────────
611
+ program
612
+ .command('churn')
613
+ .description('Collect file-level git churn (commits, last commit, top authors)')
614
+ .option('--db <path>', 'Database path')
615
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
616
+ .action(async (opts) => {
617
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
618
+ const dbPath = opts.db ?? findDbFromCwd();
619
+ if (!fs_1.default.existsSync(dbPath)) {
620
+ console.error(`No index at ${dbPath}`);
621
+ process.exit(1);
622
+ }
623
+ const store = new store_js_1.Store(dbPath);
624
+ try {
625
+ const { collectChurn } = await Promise.resolve().then(() => __importStar(require('../indexer/churn.js')));
626
+ const r = await collectChurn(workspace, store);
627
+ console.log(`\nChurn pass: ${r.filesWithChurn}/${r.filesAnalyzed} files have history (HEAD ${r.headSha?.slice(0, 8) ?? '—'}), ${r.elapsedMs}ms`);
628
+ }
629
+ finally {
630
+ store.close();
631
+ }
632
+ });
633
+ // ── seer history (Track D) ──────────────────────────────────────────────────
634
+ program
635
+ .command('history <symbol>')
636
+ .description('Show per-symbol commit history (requires `seer symbol-history` to have run)')
637
+ .option('--db <path>', 'Database path')
638
+ .option('-n, --limit <n>', 'Max commits', '20')
639
+ .action((symbol, opts) => {
640
+ const dbPath = opts.db ?? findDbFromCwd();
641
+ const store = openStore(dbPath);
642
+ try {
643
+ const matches = store.getDefinition(symbol);
644
+ if (matches.length === 0) {
645
+ console.log(`No symbol named "${symbol}"`);
646
+ return;
647
+ }
648
+ const limit = parseInt(opts.limit, 10) || 20;
649
+ for (const m of matches.slice(0, 3)) {
650
+ const history = store.getSymbolHistory(m.id, { limit });
651
+ const total = store.countSymbolHistory(m.id);
652
+ console.log(`\n${m.qualifiedName ?? m.name} (${m.kind}) ${m.filePath}:${m.lineStart + 1}`);
653
+ if (history.length === 0) {
654
+ console.log(` (no history — run \`seer symbol-history\` first)`);
655
+ continue;
656
+ }
657
+ console.log(` ${total} commits in history${total > history.length ? ` (showing ${history.length})` : ''}`);
658
+ for (const h of history) {
659
+ const date = new Date(h.committedAt * 1000).toISOString().slice(0, 10);
660
+ const author = h.authorName ?? '?';
661
+ const pr = h.prNumber ? ` #${h.prNumber}` : '';
662
+ const msg = (h.message ?? '').split('\n')[0].slice(0, 60);
663
+ console.log(` ${h.commitSha.slice(0, 8)} ${date} +${h.linesAdded}/-${h.linesRemoved}${pr} ${author.padEnd(20)} ${msg}`);
664
+ }
665
+ }
666
+ }
667
+ finally {
668
+ store.close();
669
+ }
670
+ });
671
+ program
672
+ .command('symbol-history')
673
+ .description('Index per-symbol git history (opt-in; can take a few minutes)')
674
+ .option('--db <path>', 'Database path')
675
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
676
+ .option('--max-commits <n>', 'Max commits per file', '200')
677
+ .option('--force', 'Re-run even if HEAD unchanged')
678
+ .action(async (opts) => {
679
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
680
+ const dbPath = opts.db ?? findDbFromCwd();
681
+ if (!fs_1.default.existsSync(dbPath)) {
682
+ console.error(`No index at ${dbPath}`);
683
+ process.exit(1);
684
+ }
685
+ const store = new store_js_1.Store(dbPath);
686
+ try {
687
+ const { buildSymbolHistory } = await Promise.resolve().then(() => __importStar(require('../indexer/symbolhistory.js')));
688
+ const r = await buildSymbolHistory(workspace, store, {
689
+ maxCommitsPerFile: parseInt(opts.maxCommits, 10) || 200,
690
+ skipIfHeadUnchanged: !opts.force,
691
+ log: (m) => console.log(` ${m}`),
692
+ });
693
+ console.log(`\nSymbol history: ${r.historyRowsInserted} rows across ${r.filesProcessed} files (${r.elapsedMs}ms)`);
694
+ }
695
+ finally {
696
+ store.close();
697
+ }
698
+ });
699
+ // ── seer continuity (rename/move continuity evidence) ────────────────────
700
+ program
701
+ .command('continuity <symbol>')
702
+ .description('v10 — Show rename/move continuity evidence (advisory; confidence-labelled).')
703
+ .option('--db <path>', 'Database path')
704
+ .action(async (symbol, opts) => {
705
+ const dbPath = opts.db ?? findDbFromCwd();
706
+ const store = openStore(dbPath);
707
+ try {
708
+ const { getContinuityForSymbol } = await Promise.resolve().then(() => __importStar(require('../indexer/continuity.js')));
709
+ const defs = store.getDefinition(symbol);
710
+ if (defs.length === 0) {
711
+ console.log(`No symbol "${symbol}"`);
712
+ return;
713
+ }
714
+ for (const d of defs.slice(0, 3)) {
715
+ console.log(`\n${d.qualifiedName ?? d.name} (${d.kind}) ${d.filePath}:${d.lineStart + 1}`);
716
+ const rows = getContinuityForSymbol(store, d.id);
717
+ if (rows.length === 0) {
718
+ console.log(` (no continuity candidates)`);
719
+ continue;
720
+ }
721
+ for (const r of rows) {
722
+ console.log(` ← previous: ${r.previousName.padEnd(28)} conf=${r.confidence.toFixed(2)} [${r.matchReasons.join(', ')}]`);
723
+ console.log(` in: ${r.previousFile}`);
724
+ }
725
+ }
726
+ }
727
+ finally {
728
+ store.close();
729
+ }
730
+ });
731
+ // ── seer architecture ──────────────────────────────────────────────────────
732
+ program
733
+ .command('architecture')
734
+ .alias('arch')
735
+ .description('Show a one-page architecture snapshot of the codebase')
736
+ .option('--db <path>', 'Database path')
737
+ .action(async (opts) => {
738
+ const dbPath = opts.db ?? findDbFromCwd();
739
+ const store = openStore(dbPath);
740
+ try {
741
+ const { buildArchitecture } = await Promise.resolve().then(() => __importStar(require('../indexer/architecture.js')));
742
+ const a = buildArchitecture(path_1.default.dirname(path_1.default.dirname(dbPath)), store);
743
+ console.log(`\nArchitecture snapshot`);
744
+ console.log(`─────────────────────`);
745
+ console.log(` Workspace: ${a.workspace}`);
746
+ console.log(` Totals: files=${a.totals.files} symbols=${a.totals.symbols} edges=${a.totals.edges} routes=${a.totals.routes} deps=${a.totals.externalDependencies} configKeys=${a.totals.configKeys}`);
747
+ console.log(`\n Languages:`);
748
+ for (const l of a.languages)
749
+ console.log(` ${l.language.padEnd(14)} files=${l.files} symbols=${l.symbols}`);
750
+ console.log(`\n Top modules:`);
751
+ for (const m of a.topModules)
752
+ console.log(` ${m.name.padEnd(20)} files=${m.files} symbols=${m.symbols}`);
753
+ console.log(`\n Top symbols:`);
754
+ for (const s of a.topSymbols.slice(0, 10))
755
+ console.log(` ${s.pagerank.toFixed(4)} ${(s.qualifiedName ?? s.name).padEnd(40)} (${s.kind})`);
756
+ if (a.entryPoints.length > 0) {
757
+ console.log(`\n Entry points:`);
758
+ for (const e of a.entryPoints)
759
+ console.log(` ${(e.qualifiedName ?? e.name).padEnd(30)} ${e.file}`);
760
+ }
761
+ if (a.hotspots.length > 0) {
762
+ console.log(`\n Hotspots:`);
763
+ for (const h of a.hotspots.slice(0, 10))
764
+ console.log(` ${h.commits.toString().padStart(5)} commits ${h.file}`);
765
+ }
766
+ if (a.routes.total > 0)
767
+ console.log(`\n Routes by framework: ${JSON.stringify(a.routes.byFramework)}`);
768
+ }
769
+ finally {
770
+ store.close();
771
+ }
772
+ });
773
+ // ── seer detect-changes ────────────────────────────────────────────────────
774
+ program
775
+ .command('detect-changes')
776
+ .description('Show blast radius of an uncommitted (or between-refs) diff')
777
+ .option('--db <path>', 'Database path')
778
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
779
+ .option('--from <ref>', 'From ref (default: working tree)')
780
+ .option('--to <ref>', 'To ref')
781
+ .option('--depth <n>', 'Reverse-caller depth', '2')
782
+ .action(async (opts) => {
783
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
784
+ const dbPath = opts.db ?? findDbFromCwd();
785
+ const store = openStore(dbPath);
786
+ try {
787
+ const { detectChanges } = await Promise.resolve().then(() => __importStar(require('../indexer/detectchanges.js')));
788
+ const r = detectChanges(workspace, store, {
789
+ fromRef: opts.from, toRef: opts.to,
790
+ callerDepth: parseInt(opts.depth, 10) || 2,
791
+ });
792
+ console.log(`\nDetected ${r.changedFiles.length} changed file(s), ${r.directlyChanged.length} directly-changed symbol(s)`);
793
+ for (const f of r.changedFiles) {
794
+ console.log(`\n ${f.path} (${f.hunks} hunk(s))`);
795
+ for (const s of f.symbols) {
796
+ console.log(` → ${(s.symbol.qualifiedName ?? s.symbol.name).padEnd(40)} ${s.symbol.kind}`);
797
+ }
798
+ }
799
+ if (r.transitivelyAffected.length > 0) {
800
+ console.log(`\n Transitively-affected (top 15 by PageRank):`);
801
+ for (const s of r.transitivelyAffected.slice(0, 15)) {
802
+ console.log(` ${s.pagerank.toFixed(4)} ${(s.qualifiedName ?? s.name).padEnd(40)} ${s.kind} ${s.filePath}`);
803
+ }
804
+ }
805
+ console.log(`\n ${r.elapsedMs}ms`);
806
+ }
807
+ finally {
808
+ store.close();
809
+ }
810
+ });
811
+ // ── Track-E: modules / behavior / risk / context ──────────────────────────
812
+ program
813
+ .command('modules')
814
+ .description('List clustered modules (Louvain) by centrality / size / label')
815
+ .option('--db <path>', 'Database path')
816
+ .option('-n, --limit <n>', 'Max results', '40')
817
+ .option('--sort <by>', 'centrality | size | label', 'centrality')
818
+ .action((opts) => {
819
+ const dbPath = opts.db ?? findDbFromCwd();
820
+ const store = openStore(dbPath);
821
+ try {
822
+ const sortBy = opts.sort === 'size' || opts.sort === 'label' ? opts.sort : 'centrality';
823
+ const rows = store.listModules({ limit: parseInt(opts.limit, 10) || 40, sortBy });
824
+ if (rows.length === 0) {
825
+ console.log('No modules — run `seer index` to build the clustering.');
826
+ return;
827
+ }
828
+ console.log(`\nModules (${rows.length} shown, sorted by ${sortBy})\n`);
829
+ console.log(` ${'Label'.padEnd(28)} ${'Files'.padStart(5)} ${'Symbols'.padStart(7)} ${'Lang'.padEnd(12)} ${'Cohesion'.padStart(8)} ${'Central'.padStart(8)}`);
830
+ console.log(' ' + '─'.repeat(80));
831
+ for (const m of rows) {
832
+ console.log(` ${m.label.padEnd(28)} ${String(m.sizeFiles).padStart(5)} ${String(m.sizeSymbols).padStart(7)} ${(m.primaryLanguage ?? '').padEnd(12)} ${m.cohesion.toFixed(2).padStart(8)} ${m.centrality.toFixed(4).padStart(8)}`);
833
+ }
834
+ }
835
+ finally {
836
+ store.close();
837
+ }
838
+ });
839
+ program
840
+ .command('module <label>')
841
+ .description('Show files and top symbols inside a module (by label or id)')
842
+ .option('--db <path>', 'Database path')
843
+ .option('-n, --files <n>', 'Max files', '50')
844
+ .option('-s, --symbols <n>', 'Max symbols', '20')
845
+ .action((label, opts) => {
846
+ const dbPath = opts.db ?? findDbFromCwd();
847
+ const store = openStore(dbPath);
848
+ try {
849
+ const asId = parseInt(label, 10);
850
+ const mod = !isNaN(asId) && String(asId) === label
851
+ ? store.getModuleById(asId)
852
+ : store.getModuleByLabel(label);
853
+ if (!mod) {
854
+ console.log(`No module "${label}"`);
855
+ return;
856
+ }
857
+ console.log(`\nModule "${mod.label}" id=${mod.id} files=${mod.sizeFiles} symbols=${mod.sizeSymbols} cohesion=${mod.cohesion.toFixed(2)} centrality=${mod.centrality.toFixed(4)}`);
858
+ const files = store.listModuleMembers(mod.id, parseInt(opts.files, 10) || 50);
859
+ console.log(`\n Files (${files.length}):`);
860
+ for (const f of files)
861
+ console.log(` ${f.language.padEnd(12)} ${f.role.padEnd(9)} ${f.relPath}`);
862
+ const syms = store.listModuleTopSymbols(mod.id, parseInt(opts.symbols, 10) || 20);
863
+ console.log(`\n Top symbols (${syms.length}):`);
864
+ for (const s of syms)
865
+ console.log(` ${s.pagerank.toFixed(4)} ${(s.qualifiedName ?? s.name).padEnd(40)} ${s.kind} ${s.filePath}`);
866
+ const out = store.moduleDependencies(mod.id, { direction: 'out', limit: 10 });
867
+ const inn = store.moduleDependencies(mod.id, { direction: 'in', limit: 10 });
868
+ if (out.length > 0) {
869
+ console.log(`\n Depends on (out):`);
870
+ for (const d of out)
871
+ console.log(` ${d.label.padEnd(28)} kind=${d.kind.padEnd(8)} weight=${d.weight}`);
872
+ }
873
+ if (inn.length > 0) {
874
+ console.log(`\n Depended on by (in):`);
875
+ for (const d of inn)
876
+ console.log(` ${d.label.padEnd(28)} kind=${d.kind.padEnd(8)} weight=${d.weight}`);
877
+ }
878
+ }
879
+ finally {
880
+ store.close();
881
+ }
882
+ });
883
+ program
884
+ .command('behavior <symbol>')
885
+ .description('Show ranked behavioral contract (tests) for a symbol')
886
+ .option('--db <path>', 'Database path')
887
+ .option('-n, --limit <n>', 'Max results', '20')
888
+ .option('--depth <n>', 'BFS depth for indirect coverage', '2')
889
+ .action((symbol, opts) => {
890
+ const dbPath = opts.db ?? findDbFromCwd();
891
+ const store = openStore(dbPath);
892
+ try {
893
+ const r = (0, behavior_js_1.rankedBehavior)(store, symbol, {
894
+ limit: parseInt(opts.limit, 10) || 20,
895
+ indirectDepth: parseInt(opts.depth, 10) || 2,
896
+ });
897
+ if (!r) {
898
+ console.log(`No symbol "${symbol}"`);
899
+ return;
900
+ }
901
+ console.log(`\nBehavior for ${r.symbol.qualifiedName ?? r.symbol.name} (${r.symbol.kind}) ${r.symbol.file}`);
902
+ console.log(` direct=${r.direct} indirect=${r.indirect} naming=${r.namingMatches} same-file=${r.sameFileMatches}\n`);
903
+ for (const t of r.tests) {
904
+ const dist = t.graphDistance == null ? ' ' : String(t.graphDistance).padStart(2);
905
+ console.log(` spec=${t.specificity.toString().padStart(4)} d=${dist} asserts=${String(t.assertionCount).padStart(2)} ${t.relationship.padEnd(18)} ${(t.testSymbol.qualifiedName ?? t.testSymbol.name).padEnd(40)} ${t.testSymbol.file}:${t.testSymbol.lineStart + 1}`);
906
+ }
907
+ }
908
+ finally {
909
+ store.close();
910
+ }
911
+ });
912
+ program
913
+ .command('risk <symbol>')
914
+ .description('Deterministic edit-risk profile for a symbol')
915
+ .option('--db <path>', 'Database path')
916
+ .option('--depth <n>', 'BFS depth for transitive callers', '3')
917
+ .action((symbol, opts) => {
918
+ const dbPath = opts.db ?? findDbFromCwd();
919
+ const store = openStore(dbPath);
920
+ try {
921
+ const r = (0, risk_js_1.computeRisk)(store, symbol, { callerDepth: parseInt(opts.depth, 10) || 3 });
922
+ if (!r) {
923
+ console.log(`No symbol "${symbol}"`);
924
+ return;
925
+ }
926
+ console.log(`\nRisk: ${r.risk.toUpperCase()} (score ${r.score.toFixed(2)})`);
927
+ console.log(` ${r.symbol.qualifiedName ?? r.symbol.name} (${r.symbol.kind}) ${r.symbol.file}:${r.symbol.lineStart + 1}`);
928
+ if (r.module)
929
+ console.log(` module=${r.module.label}`);
930
+ console.log(`\n Signal contributions:`);
931
+ for (const c of r.signalContributions) {
932
+ const sign = c.contribution > 0 ? '+' : '';
933
+ console.log(` ${c.signal.padEnd(28)} value=${String(c.value).padEnd(8)} ${sign}${c.contribution.toFixed(2)}`);
934
+ }
935
+ if (r.signals.routes.length > 0) {
936
+ console.log(` Routes:`);
937
+ for (const rt of r.signals.routes)
938
+ console.log(` ${rt.method} ${rt.path} (${rt.framework})`);
939
+ }
940
+ }
941
+ finally {
942
+ store.close();
943
+ }
944
+ });
945
+ program
946
+ .command('context <symbol>')
947
+ .description('One compact pre-edit packet: definition, callers, callees, routes, config, behavior, history, complexity, module, blast radius, risk')
948
+ .option('--db <path>', 'Database path')
949
+ .option('--file <path>', 'Disambiguate by file')
950
+ .action((symbol, opts) => {
951
+ const dbPath = opts.db ?? findDbFromCwd();
952
+ const store = openStore(dbPath);
953
+ try {
954
+ const c = (0, context_js_1.buildContext)(store, symbol, { filePath: opts.file });
955
+ if (!c) {
956
+ console.log(`No symbol "${symbol}"`);
957
+ return;
958
+ }
959
+ console.log(`\nContext for ${c.symbol.qualifiedName ?? c.symbol.name} (${c.symbol.kind}) ${c.symbol.file}:${c.symbol.lineStart + 1}`);
960
+ if (c.module)
961
+ console.log(` Module: ${c.module.label}`);
962
+ console.log(` Complexity: loc=${c.complexity.loc ?? '—'} cyclomatic=${c.complexity.cyclomatic ?? '—'} cognitive=${c.complexity.cognitive ?? '—'}`);
963
+ console.log(` Callers: ${c.callers.total} total; Callees: ${c.callees.total}; Blast radius (depth ${c.blastRadius.maxDepth}): direct=${c.blastRadius.directCallers}, transitive=${c.blastRadius.transitiveCallers}`);
964
+ console.log(` Behavior: direct=${c.behavior.direct} indirect=${c.behavior.indirect} naming=${c.behavior.namingMatches} same-file=${c.behavior.sameFileMatches}`);
965
+ console.log(` Routes: ${c.routes.length} Config: ${c.configKeys.length} History: ${c.recentHistory.total}`);
966
+ console.log(` Risk: ${c.risk.risk.toUpperCase()} (score ${c.risk.score.toFixed(2)})`);
967
+ console.log(`\n Signal contributions:`);
968
+ for (const sc of c.risk.signalContributions) {
969
+ const sign = sc.contribution > 0 ? '+' : '';
970
+ console.log(` ${sc.signal.padEnd(28)} ${sign}${sc.contribution.toFixed(2)}`);
971
+ }
972
+ }
973
+ finally {
974
+ store.close();
975
+ }
976
+ });
977
+ // ── Track-F: bundle export/import + CI pipeline ──────────────────────────
978
+ const bundleCmd = program
979
+ .command('bundle')
980
+ .description('Portable .seer index bundles (export, import, info)');
981
+ bundleCmd
982
+ .command('export')
983
+ .description('Export the current index as a portable .seerbundle file')
984
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
985
+ .option('--db <path>', 'Database path')
986
+ .option('--out <path>', 'Output bundle path (default: <workspace>/.seer/index.seerbundle)')
987
+ .option('--level <n>', 'Gzip compression level 0-9 (default: 6)', '6')
988
+ .option('--built-at <ms>', 'Pin manifest.builtAt to a fixed Unix-millis value for reproducible bundles')
989
+ .action(async (opts) => {
990
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
991
+ const dbPath = opts.db ?? path_1.default.join(workspace, '.seer', 'graph.db');
992
+ const { exportBundle } = await Promise.resolve().then(() => __importStar(require('../bundle/export.js')));
993
+ const level = Math.max(0, Math.min(9, parseInt(opts.level, 10) || 6));
994
+ const builtAt = opts.builtAt ? parseInt(opts.builtAt, 10) : undefined;
995
+ const r = await exportBundle(dbPath, workspace, {
996
+ out: opts.out, compressionLevel: level,
997
+ builtAt: (builtAt != null && !isNaN(builtAt)) ? builtAt : undefined,
998
+ log: (m) => console.log(` ${m}`),
999
+ });
1000
+ console.log(`\n ✓ Bundle exported to ${r.bundlePath}`);
1001
+ console.log(` ${r.bytes.toLocaleString()} bytes schemaVersion=${r.manifest.schemaVersion} symbols=${r.manifest.index.symbols} edges=${r.manifest.index.edges}`);
1002
+ console.log(` DB sha256=${r.manifest.dbSha256.slice(0, 16)}... built in ${r.elapsedMs}ms`);
1003
+ });
1004
+ bundleCmd
1005
+ .command('import <bundle>')
1006
+ .description('Import a .seerbundle into a workspace. Add --external to import additively as a peer-repo evidence layer (does not replace the local DB).')
1007
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
1008
+ .option('--db <path>', 'Database path (default: <workspace>/.seer/graph.db)')
1009
+ .option('--overwrite', 'Allow overwriting an existing index')
1010
+ .option('--skip-integrity-check', 'Skip sha256 check (forensics only)')
1011
+ .option('--skip-schema-check', 'Skip schemaVersion compatibility check (use only if you KNOW the bundle is safe)')
1012
+ .option('--external', 'Additive external import — adds routes/service endpoints as a read-only external layer, never replaces local rows.')
1013
+ .option('--alias <name>', 'Optional alias for the external bundle (defaults to manifest.gitBranch or filename).')
1014
+ .option('--force', 'Force re-import even if the same hash is already present (external mode only).')
1015
+ .action(async (bundle, opts) => {
1016
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
1017
+ if (opts.external) {
1018
+ const dbPath = opts.db ?? path_1.default.join(workspace, '.seer', 'graph.db');
1019
+ if (!fs_1.default.existsSync(dbPath)) {
1020
+ console.error(`No index at ${dbPath}. Run "seer index <path>" first before importing an external bundle.`);
1021
+ process.exit(1);
1022
+ }
1023
+ const { importExternalBundle } = await Promise.resolve().then(() => __importStar(require('../bundle/external.js')));
1024
+ const store = new store_js_1.Store(dbPath);
1025
+ try {
1026
+ const r = await importExternalBundle(path_1.default.resolve(bundle), store, {
1027
+ alias: opts.alias, force: opts.force,
1028
+ log: (m) => console.log(` ${m}`),
1029
+ });
1030
+ if (r.alreadyImported) {
1031
+ console.log(`\n ↻ External bundle already imported (hash unchanged); no-op.`);
1032
+ }
1033
+ else {
1034
+ console.log(`\n ✓ External bundle imported as layer #${r.bundleId} (${r.externalProject ?? 'unnamed'}).`);
1035
+ console.log(` routes=${r.routesImported} serviceEndpoints=${r.serviceEndpointsImported} schemaVersion=${r.schemaVersion}`);
1036
+ console.log(` hash=${r.externalHash.slice(0, 12)}... took ${r.elapsedMs}ms`);
1037
+ }
1038
+ }
1039
+ catch (err) {
1040
+ console.error(`\n ✗ External import failed: ${err.message}`);
1041
+ process.exit(1);
1042
+ }
1043
+ finally {
1044
+ store.close();
1045
+ }
1046
+ return;
1047
+ }
1048
+ const { importBundle } = await Promise.resolve().then(() => __importStar(require('../bundle/import.js')));
1049
+ try {
1050
+ const r = await importBundle(path_1.default.resolve(bundle), {
1051
+ repoRoot: workspace,
1052
+ dbOut: opts.db,
1053
+ overwrite: opts.overwrite,
1054
+ skipIntegrityCheck: opts.skipIntegrityCheck,
1055
+ skipSchemaCheck: opts.skipSchemaCheck,
1056
+ log: (m) => console.log(` ${m}`),
1057
+ });
1058
+ console.log(`\n ✓ Bundle imported to ${r.dbPath}`);
1059
+ console.log(` builtAt=${new Date(r.manifest.builtAt).toISOString()} schemaVersion=${r.manifest.schemaVersion}`);
1060
+ console.log(` symbols=${r.manifest.index.symbols} edges=${r.manifest.index.edges} modules=${r.manifest.index.modules}`);
1061
+ console.log(` Took ${r.elapsedMs}ms`);
1062
+ }
1063
+ catch (err) {
1064
+ console.error(`\n ✗ Import failed: ${err.message}`);
1065
+ process.exit(1);
1066
+ }
1067
+ });
1068
+ // ── seer boundaries (Feature 4: monorepo boundary detection) ─────────────
1069
+ program
1070
+ .command('boundaries')
1071
+ .description('List monorepo package/service boundaries detected at index time.')
1072
+ .option('--db <path>', 'Database path')
1073
+ .option('-n, --limit <n>', 'Max results', '50')
1074
+ .action((opts) => {
1075
+ const dbPath = opts.db ?? findDbFromCwd();
1076
+ const store = openStore(dbPath);
1077
+ try {
1078
+ const rows = store.listBoundaries(parseInt(opts.limit, 10) || 50);
1079
+ if (rows.length === 0) {
1080
+ console.log('No boundaries detected — workspace has no nested package manifests or convention dirs.');
1081
+ return;
1082
+ }
1083
+ console.log(`\nBoundaries (${rows.length} shown)\n`);
1084
+ for (const b of rows) {
1085
+ const eco = b.ecosystem ? `[${b.ecosystem}]` : '';
1086
+ console.log(` ${b.kind.padEnd(16)} ${String(b.sizeFiles).padStart(5)} ${b.label.padEnd(20)} ${eco} ${b.rootRelPath || '.'}`);
1087
+ }
1088
+ }
1089
+ finally {
1090
+ store.close();
1091
+ }
1092
+ });
1093
+ // ── seer preflight ────────────────────────────────────────────────────────
1094
+ program
1095
+ .command('preflight')
1096
+ .description('One compact "should I edit this?" evidence packet for an agent. Pass --symbol <X> for a single-symbol packet, or --from <ref> --to <ref> for a diff-range packet.')
1097
+ .option('--db <path>', 'Database path')
1098
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
1099
+ .option('--symbol <name>', 'Build a packet for the named symbol.')
1100
+ .option('--file <path>', 'Optional file to disambiguate the symbol.')
1101
+ .option('--from <ref>', 'Build a range packet from this git ref.')
1102
+ .option('--to <ref>', 'Build a range packet to this git ref.')
1103
+ .option('--old-bundle <path>', 'Optional old .seerbundle to include contract changes.')
1104
+ .option('--new-bundle <path>', 'Optional new .seerbundle to include contract changes.')
1105
+ .option('--max-symbols <n>', 'Cap on touched symbols (default 12)', '12')
1106
+ .option('--max-tests <n>', 'Cap on likely tests (default 8)', '8')
1107
+ .option('--max-history <n>', 'Cap on history rows (default 8)', '8')
1108
+ .option('--json', 'Print machine-readable JSON.')
1109
+ .action(async (opts) => {
1110
+ const dbPath = opts.db ?? findDbFromCwd();
1111
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
1112
+ const store = openStore(dbPath);
1113
+ try {
1114
+ const { preflight } = await Promise.resolve().then(() => __importStar(require('../indexer/preflight.js')));
1115
+ const r = await preflight(store, {
1116
+ symbol: opts.symbol,
1117
+ filePath: opts.file,
1118
+ fromRef: opts.from,
1119
+ toRef: opts.to,
1120
+ workspace,
1121
+ oldBundle: opts.oldBundle,
1122
+ newBundle: opts.newBundle,
1123
+ maxSymbols: parseInt(opts.maxSymbols, 10) || 12,
1124
+ maxTests: parseInt(opts.maxTests, 10) || 8,
1125
+ maxHistory: parseInt(opts.maxHistory, 10) || 8,
1126
+ });
1127
+ if (opts.json) {
1128
+ process.stdout.write(JSON.stringify(r, null, 2) + '\n');
1129
+ }
1130
+ else {
1131
+ printPreflight(r);
1132
+ }
1133
+ // Advisory: never raise non-zero exit when preflight finds risk.
1134
+ process.exit(r.ok ? 0 : 1);
1135
+ }
1136
+ finally {
1137
+ store.close();
1138
+ }
1139
+ });
1140
+ function printPreflight(r) {
1141
+ if (!r.ok) {
1142
+ console.log(`\n ✗ preflight failed: ${r.reason}`);
1143
+ return;
1144
+ }
1145
+ console.log(`\nPreflight (${r.mode})`);
1146
+ if (r.symbol) {
1147
+ console.log(` Symbol: ${r.symbol.qualifiedName ?? r.symbol.name} ${r.symbol.file}:${r.symbol.lineStart + 1}`);
1148
+ }
1149
+ if (r.range) {
1150
+ console.log(` Range: ${r.range.fromRef ?? '(working tree)'} → ${r.range.toRef ?? 'HEAD'}`);
1151
+ console.log(` ${r.range.changedFiles} file(s), ${r.range.directHunkCount} hunk(s)`);
1152
+ }
1153
+ console.log(` Risk: ${r.risk.overall.toUpperCase()}`);
1154
+ for (const r2 of r.risk.perSymbol.slice(0, 5)) {
1155
+ console.log(` - ${r2.symbol.qualifiedName ?? r2.symbol.name} score=${r2.score.toFixed(2)} ${r2.risk}`);
1156
+ }
1157
+ if (r.likelyTests.length > 0) {
1158
+ console.log(` Likely tests (${r.likelyTests.length}):`);
1159
+ for (const t of r.likelyTests.slice(0, 8)) {
1160
+ console.log(` • ${(t.testSymbol.qualifiedName ?? t.testSymbol.name).padEnd(40)} [${t.relationship}] spec=${t.specificity}`);
1161
+ }
1162
+ }
1163
+ if (r.serviceImpact.inbound.length + r.serviceImpact.outbound.length > 0) {
1164
+ console.log(` Service impact: in=${r.serviceImpact.inbound.length} out=${r.serviceImpact.outbound.length}`);
1165
+ }
1166
+ if (r.history.length > 0) {
1167
+ console.log(` Recent commits (${r.history.length}):`);
1168
+ for (const h of r.history.slice(0, 5)) {
1169
+ const date = new Date(h.committedAt * 1000).toISOString().slice(0, 10);
1170
+ console.log(` ${h.sha.slice(0, 8)} ${date} ${(h.author ?? '?').slice(0, 24).padEnd(24)} ${(h.message ?? '').split('\n')[0].slice(0, 60)}`);
1171
+ }
1172
+ }
1173
+ if (r.warnings.length > 0) {
1174
+ console.log(` Warnings:`);
1175
+ for (const w of r.warnings)
1176
+ console.log(` ⚠ ${w}`);
1177
+ }
1178
+ }
1179
+ // ── seer contract diff ────────────────────────────────────────────────────
1180
+ const contractCmd = program
1181
+ .command('contract')
1182
+ .description('API/service contract diffing across exported .seerbundle files (advisory).');
1183
+ contractCmd
1184
+ .command('diff <old-bundle> <new-bundle>')
1185
+ .description('Diff API/service contracts (routes, tRPC/GraphQL/gRPC ops, topics, queues) between two bundles. Exit 0 even when breaking changes appear — advisory only.')
1186
+ .option('--json', 'Emit machine-readable JSON instead of a compact table.')
1187
+ .option('--include-callers', 'Include affectedCallers using service-link evidence from both bundles.')
1188
+ .action(async (oldBundle, newBundle, opts) => {
1189
+ const { contractDiff, formatContractDiffTable } = await Promise.resolve().then(() => __importStar(require('../bundle/contract.js')));
1190
+ try {
1191
+ const diff = await contractDiff(path_1.default.resolve(oldBundle), path_1.default.resolve(newBundle), { includeAffectedCallers: opts.includeCallers });
1192
+ if (opts.json) {
1193
+ process.stdout.write(JSON.stringify(diff, null, 2) + '\n');
1194
+ }
1195
+ else {
1196
+ process.stdout.write(formatContractDiffTable(diff));
1197
+ }
1198
+ // Advisory: always exit 0.
1199
+ process.exit(0);
1200
+ }
1201
+ catch (err) {
1202
+ console.error(`\n ✗ contract diff failed: ${err.message}`);
1203
+ process.exit(1);
1204
+ }
1205
+ });
1206
+ bundleCmd
1207
+ .command('external')
1208
+ .description('List external bundle layers imported into this workspace.')
1209
+ .option('--db <path>', 'Database path')
1210
+ .action((opts) => {
1211
+ const dbPath = opts.db ?? findDbFromCwd();
1212
+ const store = openStore(dbPath);
1213
+ try {
1214
+ const rows = store.listExternalBundles();
1215
+ if (rows.length === 0) {
1216
+ console.log('No external bundles imported.');
1217
+ return;
1218
+ }
1219
+ console.log(`\nExternal bundle layers (${rows.length}):\n`);
1220
+ for (const r of rows) {
1221
+ console.log(` #${r.id} ${r.externalProject ?? '(unnamed)'} routes=${r.routesImported}`);
1222
+ console.log(` path=${r.bundlePath}`);
1223
+ console.log(` hash=${(r.externalHash ?? '').slice(0, 12)}... imported=${new Date(r.importedAt).toISOString()}`);
1224
+ }
1225
+ }
1226
+ finally {
1227
+ store.close();
1228
+ }
1229
+ });
1230
+ bundleCmd
1231
+ .command('info <bundle>')
1232
+ .description('Show a bundle\'s manifest without unpacking the DB')
1233
+ .action(async (bundle) => {
1234
+ const { readBundleManifest } = await Promise.resolve().then(() => __importStar(require('../bundle/import.js')));
1235
+ try {
1236
+ const manifest = readBundleManifest(path_1.default.resolve(bundle));
1237
+ console.log(JSON.stringify(manifest, null, 2));
1238
+ }
1239
+ catch (err) {
1240
+ console.error(`\n ✗ ${err.message}`);
1241
+ process.exit(1);
1242
+ }
1243
+ });
1244
+ const ciCmd = program
1245
+ .command('ci')
1246
+ .description('CI helpers: bundle generation, workflow templates');
1247
+ ciCmd
1248
+ .command('bundle')
1249
+ .description('Fresh-index the repo and emit a portable bundle (designed for CI)')
1250
+ .option('--workspace <path>', 'Repo to index (defaults to cwd)')
1251
+ .option('--out <path>', 'Output path (default: <workspace>/.seer/index.seerbundle)')
1252
+ .option('--mode <mode>', 'Discovery mode: full | standard | fast (default: standard)', 'standard')
1253
+ .option('--no-reset', 'Keep existing DB before indexing (default: wipe)')
1254
+ .option('--no-parallel', 'Disable parallel parsing')
1255
+ .option('--git-head <sha>', 'Override gitHead in the manifest')
1256
+ .option('--git-branch <name>', 'Override gitBranch in the manifest')
1257
+ .option('--built-at <ms>', 'Pin manifest.builtAt to a fixed Unix-millis value for reproducible bundles')
1258
+ .action(async (opts) => {
1259
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
1260
+ const { buildCiBundle } = await Promise.resolve().then(() => __importStar(require('../bundle/ci.js')));
1261
+ try {
1262
+ const builtAt = opts.builtAt ? parseInt(opts.builtAt, 10) : undefined;
1263
+ const r = await buildCiBundle({
1264
+ repoRoot: workspace, out: opts.out,
1265
+ mode: parseMode(opts.mode),
1266
+ reset: opts.reset, parallel: opts.parallel,
1267
+ gitHead: opts.gitHead, gitBranch: opts.gitBranch,
1268
+ builtAt: (builtAt != null && !isNaN(builtAt)) ? builtAt : undefined,
1269
+ });
1270
+ console.log(`\n ✓ CI bundle: ${r.bundle.bundlePath}`);
1271
+ console.log(` ${r.index.symbols.toLocaleString()} symbols / ${r.index.edges.toLocaleString()} edges in ${r.totalElapsedMs}ms`);
1272
+ }
1273
+ catch (err) {
1274
+ console.error(`\n ✗ CI bundle failed: ${err.message}`);
1275
+ process.exit(1);
1276
+ }
1277
+ });
1278
+ ciCmd
1279
+ .command('workflow')
1280
+ .description('Emit a ready-to-paste GitHub Actions workflow that builds a bundle on push')
1281
+ .action(async () => {
1282
+ const { workflowTemplate } = await Promise.resolve().then(() => __importStar(require('../bundle/ci.js')));
1283
+ process.stdout.write(workflowTemplate());
1284
+ });
1285
+ // ── Track-F: SCIP import ────────────────────────────────────────────────
1286
+ program
1287
+ .command('scip-import <scip-path>')
1288
+ .description('Import a SCIP precision index. Adds source-labelled precise edges over the tree-sitter baseline.')
1289
+ .option('--workspace <path>', 'Workspace path (defaults to cwd)')
1290
+ .option('--db <path>', 'Database path')
1291
+ .option('--require-file-in-index', 'Skip SCIP docs whose file isn\'t already indexed (default: on)')
1292
+ .option('--no-require-file-in-index', 'Accept SCIP docs for files outside the tree-sitter index')
1293
+ .action(async (scipPath, opts) => {
1294
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
1295
+ const dbPath = opts.db ?? path_1.default.join(workspace, '.seer', 'graph.db');
1296
+ if (!fs_1.default.existsSync(dbPath)) {
1297
+ console.error(`No index at ${dbPath}. Run "seer index <path>" first.`);
1298
+ process.exit(1);
1299
+ }
1300
+ const { importScip } = await Promise.resolve().then(() => __importStar(require('../scip/import.js')));
1301
+ const store = new store_js_1.Store(dbPath);
1302
+ try {
1303
+ const r = await importScip(path_1.default.resolve(scipPath), store, {
1304
+ repoRoot: workspace,
1305
+ requireFileInIndex: opts.requireFileInIndex ?? true,
1306
+ log: (m) => console.log(` ${m}`),
1307
+ });
1308
+ console.log(`\n ✓ SCIP import done in ${r.elapsedMs}ms`);
1309
+ console.log(` docs=${r.documentsProcessed} symbols=${r.symbolsInserted} new, ${r.symbolsMerged} merged`);
1310
+ console.log(` edges=${r.edgesInserted} filesMissing=${r.filesMissing}`);
1311
+ console.log(` tool=${r.tool ?? '—'} sha=${r.sha256.slice(0, 12)}...`);
1312
+ }
1313
+ finally {
1314
+ store.close();
1315
+ }
1316
+ });
1317
+ // ── Track-F: duplicate detection ────────────────────────────────────────
1318
+ program
1319
+ .command('duplicates')
1320
+ .alias('dupes')
1321
+ .description('Find clusters of structurally-similar functions (SimHash)')
1322
+ .option('--db <path>', 'Database path')
1323
+ .option('--max-distance <n>', 'Max Hamming distance for two symbols to cluster (default: 6)', '6')
1324
+ .option('--min-loc <n>', 'Minimum lines-of-code to consider (default: 4)', '4')
1325
+ .option('--include-tests', 'Include test files (off by default)')
1326
+ .option('-n, --limit <n>', 'Max clusters to show', '40')
1327
+ .action(async (opts) => {
1328
+ const dbPath = opts.db ?? findDbFromCwd();
1329
+ const store = openStore(dbPath);
1330
+ try {
1331
+ const { findDuplicates } = await Promise.resolve().then(() => __importStar(require('../indexer/shapehash.js')));
1332
+ const clusters = findDuplicates(store, {
1333
+ maxDistance: parseInt(opts.maxDistance, 10) || 6,
1334
+ minLoc: parseInt(opts.minLoc, 10) || 4,
1335
+ includeTests: opts.includeTests,
1336
+ maxClusters: parseInt(opts.limit, 10) || 40,
1337
+ });
1338
+ if (clusters.length === 0) {
1339
+ console.log('No duplicate clusters found (have you run `seer index`? — shape hashes are built during indexing).');
1340
+ return;
1341
+ }
1342
+ console.log(`\nFound ${clusters.length} duplicate cluster(s):\n`);
1343
+ for (const c of clusters) {
1344
+ console.log(` Cluster (${c.symbols.length} symbols, fingerprint=${c.fingerprint.toString(16).slice(0, 8)}...)`);
1345
+ for (const s of c.symbols) {
1346
+ console.log(` [d=${s.hammingFromAnchor.toString().padStart(2)}] ${(s.qualifiedName ?? s.name).padEnd(40)} ${s.kind.padEnd(10)} loc=${(s.loc ?? '?').toString().padStart(3)} ${s.file}:${s.lineStart + 1}`);
1347
+ }
1348
+ console.log();
1349
+ }
1350
+ }
1351
+ finally {
1352
+ store.close();
1353
+ }
1354
+ });
1355
+ // ── seer mcp ─────────────────────────────────────────────────────────────────
1356
+ program
1357
+ .command('mcp')
1358
+ .description('Run an MCP server (stdio JSON-RPC) over the index.')
1359
+ .option('--workspace <path>', 'Workspace path (defaults to current directory)')
1360
+ .option('--db <path>', 'Custom database path')
1361
+ .option('--no-watch', 'Disable the background file watcher')
1362
+ .option('--no-jit', 'Disable JIT freshness checks before each query')
1363
+ .action(async (opts) => {
1364
+ const workspace = path_1.default.resolve(opts.workspace ?? process.cwd());
1365
+ if (!fs_1.default.existsSync(workspace)) {
1366
+ console.error(`Workspace not found: ${workspace}`);
1367
+ process.exit(1);
1368
+ }
1369
+ const { runMcp } = await Promise.resolve().then(() => __importStar(require('../mcp/server.js')));
1370
+ await runMcp({
1371
+ workspace,
1372
+ dbPath: opts.db,
1373
+ watch: opts.watch !== false,
1374
+ jit: opts.jit !== false,
1375
+ });
1376
+ });
1377
+ // ── DB auto-detection ──────────────────────────────────────────────────────────
1378
+ function parseMode(input) {
1379
+ if (!input)
1380
+ return undefined;
1381
+ const v = input.toLowerCase();
1382
+ if (v === 'full' || v === 'standard' || v === 'fast')
1383
+ return v;
1384
+ console.error(`Invalid --mode: ${input}.`);
1385
+ process.exit(1);
1386
+ }
1387
+ function findDbFromCwd() {
1388
+ let dir = process.cwd();
1389
+ for (let i = 0; i < 6; i++) {
1390
+ const candidate = path_1.default.join(dir, '.seer', 'graph.db');
1391
+ if (fs_1.default.existsSync(candidate))
1392
+ return candidate;
1393
+ const parent = path_1.default.dirname(dir);
1394
+ if (parent === dir)
1395
+ break;
1396
+ dir = parent;
1397
+ }
1398
+ console.error('Could not find .seer/graph.db. Run "seer index <path>" first.');
1399
+ process.exit(1);
1400
+ }
1401
+ program.parse(process.argv);
1402
+ //# sourceMappingURL=index.js.map