@spikedpunch/mast 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 (257) hide show
  1. package/LICENSE +21 -0
  2. package/MAST_SPEC.md +2926 -0
  3. package/README.md +842 -0
  4. package/assets/skill.md +45 -0
  5. package/dist/ast/extract.d.ts +65 -0
  6. package/dist/ast/extract.d.ts.map +1 -0
  7. package/dist/ast/extract.js +147 -0
  8. package/dist/ast/extract.js.map +1 -0
  9. package/dist/ast/extractor.d.ts +85 -0
  10. package/dist/ast/extractor.d.ts.map +1 -0
  11. package/dist/ast/extractor.js +2 -0
  12. package/dist/ast/extractor.js.map +1 -0
  13. package/dist/ast/extractors/markdown.d.ts +40 -0
  14. package/dist/ast/extractors/markdown.d.ts.map +1 -0
  15. package/dist/ast/extractors/markdown.js +175 -0
  16. package/dist/ast/extractors/markdown.js.map +1 -0
  17. package/dist/ast/extractors/typescript.d.ts +179 -0
  18. package/dist/ast/extractors/typescript.d.ts.map +1 -0
  19. package/dist/ast/extractors/typescript.js +1439 -0
  20. package/dist/ast/extractors/typescript.js.map +1 -0
  21. package/dist/ast/parser.d.ts +5 -0
  22. package/dist/ast/parser.d.ts.map +1 -0
  23. package/dist/ast/parser.js +21 -0
  24. package/dist/ast/parser.js.map +1 -0
  25. package/dist/ast/types.d.ts +586 -0
  26. package/dist/ast/types.d.ts.map +1 -0
  27. package/dist/ast/types.js +3 -0
  28. package/dist/ast/types.js.map +1 -0
  29. package/dist/cli/docs-cmd.d.ts +22 -0
  30. package/dist/cli/docs-cmd.d.ts.map +1 -0
  31. package/dist/cli/docs-cmd.js +100 -0
  32. package/dist/cli/docs-cmd.js.map +1 -0
  33. package/dist/cli/index-cmd.d.ts +72 -0
  34. package/dist/cli/index-cmd.d.ts.map +1 -0
  35. package/dist/cli/index-cmd.js +263 -0
  36. package/dist/cli/index-cmd.js.map +1 -0
  37. package/dist/cli/index.d.ts +3 -0
  38. package/dist/cli/index.d.ts.map +1 -0
  39. package/dist/cli/index.js +4 -0
  40. package/dist/cli/index.js.map +1 -0
  41. package/dist/cli/init.d.ts +14 -0
  42. package/dist/cli/init.d.ts.map +1 -0
  43. package/dist/cli/init.js +64 -0
  44. package/dist/cli/init.js.map +1 -0
  45. package/dist/cli/install-hooks.d.ts +3 -0
  46. package/dist/cli/install-hooks.d.ts.map +1 -0
  47. package/dist/cli/install-hooks.js +34 -0
  48. package/dist/cli/install-hooks.js.map +1 -0
  49. package/dist/cli/metrics-cmd.d.ts +3 -0
  50. package/dist/cli/metrics-cmd.d.ts.map +1 -0
  51. package/dist/cli/metrics-cmd.js +164 -0
  52. package/dist/cli/metrics-cmd.js.map +1 -0
  53. package/dist/cli/program.d.ts +14 -0
  54. package/dist/cli/program.d.ts.map +1 -0
  55. package/dist/cli/program.js +44 -0
  56. package/dist/cli/program.js.map +1 -0
  57. package/dist/cli/query.d.ts +46 -0
  58. package/dist/cli/query.d.ts.map +1 -0
  59. package/dist/cli/query.js +153 -0
  60. package/dist/cli/query.js.map +1 -0
  61. package/dist/cli/search-cmd.d.ts +33 -0
  62. package/dist/cli/search-cmd.d.ts.map +1 -0
  63. package/dist/cli/search-cmd.js +114 -0
  64. package/dist/cli/search-cmd.js.map +1 -0
  65. package/dist/cli/serve.d.ts +3 -0
  66. package/dist/cli/serve.d.ts.map +1 -0
  67. package/dist/cli/serve.js +19 -0
  68. package/dist/cli/serve.js.map +1 -0
  69. package/dist/cli/skill-install.d.ts +39 -0
  70. package/dist/cli/skill-install.d.ts.map +1 -0
  71. package/dist/cli/skill-install.js +57 -0
  72. package/dist/cli/skill-install.js.map +1 -0
  73. package/dist/cli/status.d.ts +35 -0
  74. package/dist/cli/status.d.ts.map +1 -0
  75. package/dist/cli/status.js +117 -0
  76. package/dist/cli/status.js.map +1 -0
  77. package/dist/cli/upgrade-cmd.d.ts +48 -0
  78. package/dist/cli/upgrade-cmd.d.ts.map +1 -0
  79. package/dist/cli/upgrade-cmd.js +129 -0
  80. package/dist/cli/upgrade-cmd.js.map +1 -0
  81. package/dist/cli/version.d.ts +3 -0
  82. package/dist/cli/version.d.ts.map +1 -0
  83. package/dist/cli/version.js +19 -0
  84. package/dist/cli/version.js.map +1 -0
  85. package/dist/env.d.ts +17 -0
  86. package/dist/env.d.ts.map +1 -0
  87. package/dist/env.js +17 -0
  88. package/dist/env.js.map +1 -0
  89. package/dist/graph/checker-resolver.d.ts +156 -0
  90. package/dist/graph/checker-resolver.d.ts.map +1 -0
  91. package/dist/graph/checker-resolver.js +458 -0
  92. package/dist/graph/checker-resolver.js.map +1 -0
  93. package/dist/graph/db.d.ts +318 -0
  94. package/dist/graph/db.d.ts.map +1 -0
  95. package/dist/graph/db.js +266 -0
  96. package/dist/graph/db.js.map +1 -0
  97. package/dist/graph/index.d.ts +4 -0
  98. package/dist/graph/index.d.ts.map +1 -0
  99. package/dist/graph/index.js +5 -0
  100. package/dist/graph/index.js.map +1 -0
  101. package/dist/graph/local-type-env.d.ts +45 -0
  102. package/dist/graph/local-type-env.d.ts.map +1 -0
  103. package/dist/graph/local-type-env.js +59 -0
  104. package/dist/graph/local-type-env.js.map +1 -0
  105. package/dist/graph/path-range.d.ts +40 -0
  106. package/dist/graph/path-range.d.ts.map +1 -0
  107. package/dist/graph/path-range.js +42 -0
  108. package/dist/graph/path-range.js.map +1 -0
  109. package/dist/graph/populate.d.ts +263 -0
  110. package/dist/graph/populate.d.ts.map +1 -0
  111. package/dist/graph/populate.js +984 -0
  112. package/dist/graph/populate.js.map +1 -0
  113. package/dist/graph/queries.d.ts +114 -0
  114. package/dist/graph/queries.d.ts.map +1 -0
  115. package/dist/graph/queries.js +415 -0
  116. package/dist/graph/queries.js.map +1 -0
  117. package/dist/graph/sqliteBatch.d.ts +48 -0
  118. package/dist/graph/sqliteBatch.d.ts.map +1 -0
  119. package/dist/graph/sqliteBatch.js +93 -0
  120. package/dist/graph/sqliteBatch.js.map +1 -0
  121. package/dist/indexer/freshness.d.ts +48 -0
  122. package/dist/indexer/freshness.d.ts.map +1 -0
  123. package/dist/indexer/freshness.js +72 -0
  124. package/dist/indexer/freshness.js.map +1 -0
  125. package/dist/indexer/import-resolver.d.ts +50 -0
  126. package/dist/indexer/import-resolver.d.ts.map +1 -0
  127. package/dist/indexer/import-resolver.js +318 -0
  128. package/dist/indexer/import-resolver.js.map +1 -0
  129. package/dist/indexer/index.d.ts +249 -0
  130. package/dist/indexer/index.d.ts.map +1 -0
  131. package/dist/indexer/index.js +560 -0
  132. package/dist/indexer/index.js.map +1 -0
  133. package/dist/indexer/walker.d.ts +60 -0
  134. package/dist/indexer/walker.d.ts.map +1 -0
  135. package/dist/indexer/walker.js +128 -0
  136. package/dist/indexer/walker.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +80 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +151 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/context.d.ts +23 -0
  142. package/dist/mcp/context.d.ts.map +1 -0
  143. package/dist/mcp/context.js +2 -0
  144. package/dist/mcp/context.js.map +1 -0
  145. package/dist/mcp/register-tools.d.ts +21 -0
  146. package/dist/mcp/register-tools.d.ts.map +1 -0
  147. package/dist/mcp/register-tools.js +42 -0
  148. package/dist/mcp/register-tools.js.map +1 -0
  149. package/dist/mcp/server.d.ts +58 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/mcp/server.js +177 -0
  152. package/dist/mcp/server.js.map +1 -0
  153. package/dist/mcp/staleness.d.ts +72 -0
  154. package/dist/mcp/staleness.d.ts.map +1 -0
  155. package/dist/mcp/staleness.js +213 -0
  156. package/dist/mcp/staleness.js.map +1 -0
  157. package/dist/mcp/startup.d.ts +40 -0
  158. package/dist/mcp/startup.d.ts.map +1 -0
  159. package/dist/mcp/startup.js +114 -0
  160. package/dist/mcp/startup.js.map +1 -0
  161. package/dist/mcp/tools/_helpers.d.ts +26 -0
  162. package/dist/mcp/tools/_helpers.d.ts.map +1 -0
  163. package/dist/mcp/tools/_helpers.js +54 -0
  164. package/dist/mcp/tools/_helpers.js.map +1 -0
  165. package/dist/mcp/tools/callers.d.ts +4 -0
  166. package/dist/mcp/tools/callers.d.ts.map +1 -0
  167. package/dist/mcp/tools/callers.js +112 -0
  168. package/dist/mcp/tools/callers.js.map +1 -0
  169. package/dist/mcp/tools/dependencies.d.ts +4 -0
  170. package/dist/mcp/tools/dependencies.d.ts.map +1 -0
  171. package/dist/mcp/tools/dependencies.js +36 -0
  172. package/dist/mcp/tools/dependencies.js.map +1 -0
  173. package/dist/mcp/tools/efficiency.d.ts +4 -0
  174. package/dist/mcp/tools/efficiency.d.ts.map +1 -0
  175. package/dist/mcp/tools/efficiency.js +39 -0
  176. package/dist/mcp/tools/efficiency.js.map +1 -0
  177. package/dist/mcp/tools/exports.d.ts +4 -0
  178. package/dist/mcp/tools/exports.d.ts.map +1 -0
  179. package/dist/mcp/tools/exports.js +56 -0
  180. package/dist/mcp/tools/exports.js.map +1 -0
  181. package/dist/mcp/tools/implementors.d.ts +4 -0
  182. package/dist/mcp/tools/implementors.d.ts.map +1 -0
  183. package/dist/mcp/tools/implementors.js +47 -0
  184. package/dist/mcp/tools/implementors.js.map +1 -0
  185. package/dist/mcp/tools/project-skeleton.d.ts +4 -0
  186. package/dist/mcp/tools/project-skeleton.d.ts.map +1 -0
  187. package/dist/mcp/tools/project-skeleton.js +61 -0
  188. package/dist/mcp/tools/project-skeleton.js.map +1 -0
  189. package/dist/mcp/tools/reindex.d.ts +14 -0
  190. package/dist/mcp/tools/reindex.d.ts.map +1 -0
  191. package/dist/mcp/tools/reindex.js +32 -0
  192. package/dist/mcp/tools/reindex.js.map +1 -0
  193. package/dist/mcp/tools/rename-impact.d.ts +4 -0
  194. package/dist/mcp/tools/rename-impact.d.ts.map +1 -0
  195. package/dist/mcp/tools/rename-impact.js +124 -0
  196. package/dist/mcp/tools/rename-impact.js.map +1 -0
  197. package/dist/mcp/tools/search.d.ts +4 -0
  198. package/dist/mcp/tools/search.d.ts.map +1 -0
  199. package/dist/mcp/tools/search.js +69 -0
  200. package/dist/mcp/tools/search.js.map +1 -0
  201. package/dist/mcp/tools/signature.d.ts +4 -0
  202. package/dist/mcp/tools/signature.d.ts.map +1 -0
  203. package/dist/mcp/tools/signature.js +127 -0
  204. package/dist/mcp/tools/signature.js.map +1 -0
  205. package/dist/mcp/tools/status.d.ts +4 -0
  206. package/dist/mcp/tools/status.d.ts.map +1 -0
  207. package/dist/mcp/tools/status.js +30 -0
  208. package/dist/mcp/tools/status.js.map +1 -0
  209. package/dist/search/declex.d.ts +109 -0
  210. package/dist/search/declex.d.ts.map +1 -0
  211. package/dist/search/declex.js +242 -0
  212. package/dist/search/declex.js.map +1 -0
  213. package/dist/search/fts.d.ts +77 -0
  214. package/dist/search/fts.d.ts.map +1 -0
  215. package/dist/search/fts.js +223 -0
  216. package/dist/search/fts.js.map +1 -0
  217. package/dist/search/fused.d.ts +110 -0
  218. package/dist/search/fused.d.ts.map +1 -0
  219. package/dist/search/fused.js +323 -0
  220. package/dist/search/fused.js.map +1 -0
  221. package/dist/search/potential-matches.d.ts +88 -0
  222. package/dist/search/potential-matches.d.ts.map +1 -0
  223. package/dist/search/potential-matches.js +83 -0
  224. package/dist/search/potential-matches.js.map +1 -0
  225. package/dist/search/scope.d.ts +66 -0
  226. package/dist/search/scope.d.ts.map +1 -0
  227. package/dist/search/scope.js +71 -0
  228. package/dist/search/scope.js.map +1 -0
  229. package/dist/store/config.d.ts +57 -0
  230. package/dist/store/config.d.ts.map +1 -0
  231. package/dist/store/config.js +208 -0
  232. package/dist/store/config.js.map +1 -0
  233. package/dist/store/lock.d.ts +101 -0
  234. package/dist/store/lock.d.ts.map +1 -0
  235. package/dist/store/lock.js +182 -0
  236. package/dist/store/lock.js.map +1 -0
  237. package/dist/store/lockMetrics.d.ts +52 -0
  238. package/dist/store/lockMetrics.d.ts.map +1 -0
  239. package/dist/store/lockMetrics.js +29 -0
  240. package/dist/store/lockMetrics.js.map +1 -0
  241. package/dist/store/sqliteChunkStore.d.ts +44 -0
  242. package/dist/store/sqliteChunkStore.d.ts.map +1 -0
  243. package/dist/store/sqliteChunkStore.js +135 -0
  244. package/dist/store/sqliteChunkStore.js.map +1 -0
  245. package/dist/telemetry/lockMetricsSummary.d.ts +42 -0
  246. package/dist/telemetry/lockMetricsSummary.d.ts.map +1 -0
  247. package/dist/telemetry/lockMetricsSummary.js +139 -0
  248. package/dist/telemetry/lockMetricsSummary.js.map +1 -0
  249. package/dist/telemetry/metrics.d.ts +140 -0
  250. package/dist/telemetry/metrics.d.ts.map +1 -0
  251. package/dist/telemetry/metrics.js +312 -0
  252. package/dist/telemetry/metrics.js.map +1 -0
  253. package/dist/telemetry/tokenizer.d.ts +125 -0
  254. package/dist/telemetry/tokenizer.d.ts.map +1 -0
  255. package/dist/telemetry/tokenizer.js +174 -0
  256. package/dist/telemetry/tokenizer.js.map +1 -0
  257. package/package.json +81 -0
@@ -0,0 +1,26 @@
1
+ import type { Db } from '../../graph/db.js';
2
+ import type { ResolvedConfig } from '../../store/config.js';
3
+ import type { AppContext } from '../context.js';
4
+ import { type StalenessCheckResult } from '../staleness.js';
5
+ export { collectPotentialMatches, collectPotentialMatchCandidates, type ChunkByIdSource, type PotentialMatchCandidate, type PotentialMatchCandidatesResult, type PotentialMatchesResult, } from '../../search/potential-matches.js';
6
+ /**
7
+ * True when the `chunks` table has zero rows — i.e. nothing is indexed yet,
8
+ * whether that's a never-indexed state dir mid-background-reindex (the
9
+ * legitimate §7.4 startup-ladder window Part A deliberately leaves alone) or
10
+ * a genuinely empty corpus. Every read tool with a primary result array
11
+ * calls this ONLY on its empty-result path (`ast/types.ts`'s `index_empty`
12
+ * field) to distinguish "empty because nothing indexed" from "empty because
13
+ * no match" — never called when results were found, so a populated index
14
+ * pays nothing extra per call.
15
+ */
16
+ export declare function isIndexEmpty(ctx: AppContext): Promise<boolean>;
17
+ /** Extract the first JSDoc comment block from chunk content, if present. */
18
+ export declare function extractDoc(content: string): string | null;
19
+ export { globToRegex } from '../../indexer/walker.js';
20
+ /**
21
+ * Look up the stored mtime for `filePath` from the `files` table and run a JIT
22
+ * single-file re-parse if the disk version is newer. Returns `{ busy: false,
23
+ * refreshed: false }` when the file is not yet indexed.
24
+ */
25
+ export declare function jitRefreshFile(db: Db, config: ResolvedConfig, filePath: string): Promise<StalenessCheckResult>;
26
+ //# sourceMappingURL=_helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_helpers.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AASpF,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAC/B,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,GAC5B,MAAM,mCAAmC,CAAC;AAM3C;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpE;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAID,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,CAAC,CAQ/B"}
@@ -0,0 +1,54 @@
1
+ import { checkAndRefreshIfStale } from '../staleness.js';
2
+ // collectPotentialMatches/collectPotentialMatchCandidates moved to
3
+ // search/potential-matches.ts (Stage 1.2) so the `mast index --checker` pass
4
+ // (graph/checker-resolver.ts) can share the exact same "what counts as a
5
+ // potential match" definition without an mcp/tools → graph layering
6
+ // inversion — `search` already sits between `graph`/`store` and their
7
+ // consumers. Re-exported here so mast_callers/mast_rename_impact's existing
8
+ // `from './_helpers.js'` imports keep working unchanged.
9
+ export { collectPotentialMatches, collectPotentialMatchCandidates, } from '../../search/potential-matches.js';
10
+ // ---------------------------------------------------------------------------
11
+ // M6 Part B (eval/GITNEXUS_COMPARISON.md §13.8 item 4): empty-index signal.
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * True when the `chunks` table has zero rows — i.e. nothing is indexed yet,
15
+ * whether that's a never-indexed state dir mid-background-reindex (the
16
+ * legitimate §7.4 startup-ladder window Part A deliberately leaves alone) or
17
+ * a genuinely empty corpus. Every read tool with a primary result array
18
+ * calls this ONLY on its empty-result path (`ast/types.ts`'s `index_empty`
19
+ * field) to distinguish "empty because nothing indexed" from "empty because
20
+ * no match" — never called when results were found, so a populated index
21
+ * pays nothing extra per call.
22
+ */
23
+ export async function isIndexEmpty(ctx) {
24
+ return (await ctx.chunkStore.chunkCount()) === 0;
25
+ }
26
+ /** Extract the first JSDoc comment block from chunk content, if present. */
27
+ export function extractDoc(content) {
28
+ const match = /\/\*\*([\s\S]*?)\*\//.exec(content);
29
+ return match != null ? match[0].trim() : null;
30
+ }
31
+ // globToRegex moved to indexer/walker.ts (file-discovery domain) so watch mode
32
+ // can use it without an indexer → mcp dependency; re-exported for existing users.
33
+ export { globToRegex } from '../../indexer/walker.js';
34
+ /**
35
+ * Look up the stored mtime for `filePath` from the `files` table and run a JIT
36
+ * single-file re-parse if the disk version is newer. Returns `{ busy: false,
37
+ * refreshed: false }` when the file is not yet indexed.
38
+ */
39
+ export async function jitRefreshFile(db, config, filePath) {
40
+ const row = await db
41
+ .selectFrom('files')
42
+ .select('mtime')
43
+ .where('path', '=', filePath)
44
+ .executeTakeFirst();
45
+ if (row === undefined)
46
+ return { busy: false, refreshed: false };
47
+ return checkAndRefreshIfStale(db, config, filePath, row.mtime);
48
+ }
49
+ // countStaleFiles removed 2026-08-20 (D035). It enumerated the `files` table,
50
+ // so a file on disk that was never indexed was in no row and therefore
51
+ // invisible to it — `mast_status` reported a fresh index while `mast status`
52
+ // reported the same index stale. Freshness now has one producer for both
53
+ // surfaces: `indexer/freshness.ts` `measureFreshness`.
54
+ //# sourceMappingURL=_helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_helpers.js","sourceRoot":"","sources":["../../../src/mcp/tools/_helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAA6B,MAAM,iBAAiB,CAAC;AAEpF,mEAAmE;AACnE,6EAA6E;AAC7E,yEAAyE;AACzE,oEAAoE;AACpE,sEAAsE;AACtE,4EAA4E;AAC5E,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,+BAA+B,GAKhC,MAAM,mCAAmC,CAAC;AAE3C,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAe;IAChD,OAAO,CAAC,MAAM,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,+EAA+E;AAC/E,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,EAAM,EACN,MAAsB,EACtB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,UAAU,CAAC,OAAO,CAAC;SACnB,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC;SAC5B,gBAAgB,EAAE,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChE,OAAO,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,8EAA8E;AAC9E,uEAAuE;AACvE,6EAA6E;AAC7E,yEAAyE;AACzE,uDAAuD"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ export declare function registerCallersTool(server: McpServer, ctx: AppContext): void;
4
+ //# sourceMappingURL=callers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callers.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/callers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAYhD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CA2H5E"}
@@ -0,0 +1,112 @@
1
+ import { z } from 'zod';
2
+ import { buildToolStats, recordToolCall, buildArgsJson, buildResultsJson } from '../../telemetry/metrics.js';
3
+ import { countTokens, estimateFullFileBound } from '../../telemetry/tokenizer.js';
4
+ import { querySymbolByName, queryVerifiedCallers } from '../../graph/queries.js';
5
+ import { jitRefreshFile, collectPotentialMatches, isIndexEmpty } from './_helpers.js';
6
+ export function registerCallersTool(server, ctx) {
7
+ server.tool('mast_callers', 'Call sites of a named symbol, partitioned into verified (graph-resolved) and potential (identifier-FTS) sets. Always check both sets — the graph only covers statically-linked call sites. Verified entries with resolution "checker" were proven by the opt-in `mast index --checker` TypeScript pass; the same pass drops non-call-site / wrong-declaration noise out of potential_matches (summary.checker_classified_* report how many).', {
8
+ symbol: z.string().describe('Symbol name whose callers to find'),
9
+ file_path: z.string().nullable().optional().describe('File that defines the symbol (disambiguates overloaded names)'),
10
+ transitive: z.boolean().optional().describe('Follow callers-of-callers recursively via POTENTIAL_CALL edges (verified set only)'),
11
+ include_potential: z.boolean().optional().describe('Include identifier-FTS matches as potential_matches (default: true)'),
12
+ }, async (args) => {
13
+ const start = Date.now();
14
+ const filePath = args.file_path ?? null;
15
+ // §9.0 TOCTOU policy. This JITs the file that DECLARES the symbol (not
16
+ // each caller's file — see CallersResponse.file_busy_returning_stale_cache
17
+ // for why that puts the flag at the envelope, not per-VerifiedCaller).
18
+ let fileBusy = false;
19
+ if (filePath != null) {
20
+ const r = await jitRefreshFile(ctx.db, ctx.config, filePath);
21
+ fileBusy = r.busy;
22
+ }
23
+ const symbols = await querySymbolByName(ctx.db, args.symbol, filePath ?? undefined);
24
+ if (symbols.length === 0) {
25
+ const noSymbolFiles = filePath != null ? [filePath] : [];
26
+ // M6 (§13.8 item 4): both sets are unconditionally empty on this
27
+ // branch (no symbol to look up callers of), so the only question is
28
+ // whether the index itself is empty.
29
+ const indexEmptyField = await isIndexEmpty(ctx) ? { index_empty: true } : {};
30
+ const response = {
31
+ verified_callers: [],
32
+ potential_matches: [],
33
+ // §9.0 TOCTOU policy: omitted when false, never present-and-false.
34
+ ...(fileBusy ? { file_busy_returning_stale_cache: true } : {}),
35
+ ...indexEmptyField,
36
+ summary: {
37
+ verified_count: 0,
38
+ potential_count: 0,
39
+ transitive: args.transitive ?? false,
40
+ checker_classified_non_call_site: 0,
41
+ checker_classified_different_declaration: 0,
42
+ },
43
+ _stats: buildToolStats('mast_callers', 0, estimateFullFileBound(noSymbolFiles, ctx.config.resolved_project_root), noSymbolFiles, Date.now() - start),
44
+ };
45
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
46
+ }
47
+ // Use the first symbol (querySymbolByName orders by exported-first, then path).
48
+ const target = symbols[0];
49
+ const verifiedRows = await queryVerifiedCallers(ctx.db, target.id, args.transitive ?? false);
50
+ const verified_callers = verifiedRows.map((r) => ({
51
+ file_path: r.file_path,
52
+ line: r.line,
53
+ caller_symbol: r.caller_symbol,
54
+ context: r.context,
55
+ resolution: r.resolution,
56
+ }));
57
+ let potential_matches = [];
58
+ let checkerClassifiedNonCallSite = 0;
59
+ let checkerClassifiedDifferentDeclaration = 0;
60
+ let potentialTruncated;
61
+ if (args.include_potential !== false) {
62
+ const potentialResult = await collectPotentialMatches(ctx.db, ctx.chunkStore, target.id, args.symbol, verified_callers);
63
+ potential_matches = potentialResult.matches;
64
+ checkerClassifiedNonCallSite = potentialResult.checkerClassifiedNonCallSite;
65
+ checkerClassifiedDifferentDeclaration = potentialResult.checkerClassifiedDifferentDeclaration;
66
+ potentialTruncated = potentialResult.truncatedMatchCount;
67
+ }
68
+ const filesReferenced = [
69
+ ...new Set([
70
+ ...verified_callers.map((c) => c.file_path),
71
+ ...potential_matches.map((m) => m.file_path),
72
+ ]),
73
+ ];
74
+ const text = JSON.stringify({ verified_callers, potential_matches });
75
+ const tokens = countTokens(text);
76
+ const tokensFullFileBound = estimateFullFileBound(filesReferenced, ctx.config.resolved_project_root);
77
+ const durationMs = Date.now() - start;
78
+ // M6 (§13.8 item 4): checked ONLY when BOTH sets came back empty.
79
+ const indexEmptyField = verified_callers.length === 0 && potential_matches.length === 0 && await isIndexEmpty(ctx)
80
+ ? { index_empty: true }
81
+ : {};
82
+ const response = {
83
+ verified_callers,
84
+ potential_matches,
85
+ // §9.0 TOCTOU policy: omitted when false, never present-and-false.
86
+ ...(fileBusy ? { file_busy_returning_stale_cache: true } : {}),
87
+ ...indexEmptyField,
88
+ summary: {
89
+ verified_count: verified_callers.length,
90
+ potential_count: potential_matches.length,
91
+ transitive: args.transitive ?? false,
92
+ checker_classified_non_call_site: checkerClassifiedNonCallSite,
93
+ checker_classified_different_declaration: checkerClassifiedDifferentDeclaration,
94
+ // F10: omitted-when-false — present only when the identifier-FTS cap was hit.
95
+ ...(potentialTruncated !== undefined ? { potential_truncated: potentialTruncated } : {}),
96
+ },
97
+ _stats: buildToolStats('mast_callers', tokens, tokensFullFileBound, filesReferenced, durationMs),
98
+ };
99
+ const resultIdentities = [
100
+ ...verified_callers.map((c) => ({ file_path: c.file_path, symbol_name: c.caller_symbol })),
101
+ ...potential_matches.map((m) => ({ file_path: m.file_path, symbol_name: m.context || null })),
102
+ ];
103
+ void recordToolCall(ctx.db, {
104
+ toolName: 'mast_callers', tokensReturned: tokens, tokensFullFileBound,
105
+ durationMs, sessionId: ctx.sessionId, status: 'ok',
106
+ argsJson: buildArgsJson(args),
107
+ resultsJson: buildResultsJson(resultIdentities),
108
+ }).catch(() => { });
109
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
110
+ });
111
+ }
112
+ //# sourceMappingURL=callers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callers.js","sourceRoot":"","sources":["../../../src/mcp/tools/callers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC7G,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEtF,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,GAAe;IACpE,MAAM,CAAC,IAAI,CACT,cAAc,EACd,8aAA8a,EAC9a;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;QAChE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;QACrH,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oFAAoF,CAAC;QACjI,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qEAAqE,CAAC;KAC1H,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;QAExC,uEAAuE;QACvE,2EAA2E;QAC3E,uEAAuE;QACvE,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC7D,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC;QACpB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,IAAI,SAAS,CAAC,CAAC;QAEpF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,aAAa,GAAG,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,iEAAiE;YACjE,oEAAoE;YACpE,qCAAqC;YACrC,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtF,MAAM,QAAQ,GAAoB;gBAChC,gBAAgB,EAAE,EAAE;gBACpB,iBAAiB,EAAE,EAAE;gBACrB,mEAAmE;gBACnE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,+BAA+B,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvE,GAAG,eAAe;gBAClB,OAAO,EAAE;oBACP,cAAc,EAAE,CAAC;oBACjB,eAAe,EAAE,CAAC;oBAClB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,KAAK;oBACpC,gCAAgC,EAAE,CAAC;oBACnC,wCAAwC,EAAE,CAAC;iBAC5C;gBACD,MAAM,EAAE,cAAc,CACpB,cAAc,EAAE,CAAC,EACjB,qBAAqB,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,qBAAqB,CAAC,EACtE,aAAa,EACb,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CACnB;aACF,CAAC;YACF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QAClF,CAAC;QAED,gFAAgF;QAChF,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;QAE3B,MAAM,YAAY,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,CAAC;QAC7F,MAAM,gBAAgB,GAAqB,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClE,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,aAAa,EAAE,CAAC,CAAC,aAAa;YAC9B,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,UAAU,EAAE,CAAC,CAAC,UAA8B;SAC7C,CAAC,CAAC,CAAC;QAEJ,IAAI,iBAAiB,GAA8B,EAAE,CAAC;QACtD,IAAI,4BAA4B,GAAG,CAAC,CAAC;QACrC,IAAI,qCAAqC,GAAG,CAAC,CAAC;QAC9C,IAAI,kBAAsC,CAAC;QAC3C,IAAI,IAAI,CAAC,iBAAiB,KAAK,KAAK,EAAE,CAAC;YACrC,MAAM,eAAe,GAAG,MAAM,uBAAuB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;YACxH,iBAAiB,GAAG,eAAe,CAAC,OAAO,CAAC;YAC5C,4BAA4B,GAAG,eAAe,CAAC,4BAA4B,CAAC;YAC5E,qCAAqC,GAAG,eAAe,CAAC,qCAAqC,CAAC;YAC9F,kBAAkB,GAAG,eAAe,CAAC,mBAAmB,CAAC;QAC3D,CAAC;QAED,MAAM,eAAe,GAAG;YACtB,GAAG,IAAI,GAAG,CAAC;gBACT,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC3C,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;aAC7C,CAAC;SACH,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACrG,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAEtC,kEAAkE;QAClE,MAAM,eAAe,GAAG,gBAAgB,CAAC,MAAM,KAAK,CAAC,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,YAAY,CAAC,GAAG,CAAC;YAChH,CAAC,CAAC,EAAE,WAAW,EAAE,IAAa,EAAE;YAChC,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,QAAQ,GAAoB;YAChC,gBAAgB;YAChB,iBAAiB;YACjB,mEAAmE;YACnE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,+BAA+B,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,GAAG,eAAe;YAClB,OAAO,EAAE;gBACP,cAAc,EAAE,gBAAgB,CAAC,MAAM;gBACvC,eAAe,EAAE,iBAAiB,CAAC,MAAM;gBACzC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,KAAK;gBACpC,gCAAgC,EAAE,4BAA4B;gBAC9D,wCAAwC,EAAE,qCAAqC;gBAC/E,8EAA8E;gBAC9E,GAAG,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzF;YACD,MAAM,EAAE,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,CAAC;SACjG,CAAC;QACF,MAAM,gBAAgB,GAAG;YACvB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;YAC1F,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC;SAC9F,CAAC;QACF,KAAK,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE;YAC1B,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,mBAAmB;YACrE,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI;YAClD,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC;YAC7B,WAAW,EAAE,gBAAgB,CAAC,gBAAgB,CAAC;SAChD,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IAClF,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ export declare function registerDependenciesTool(server: McpServer, ctx: AppContext): void;
4
+ //# sourceMappingURL=dependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/dependencies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAOhD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAsCjF"}
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import { buildToolStats, recordToolCall } from '../../telemetry/metrics.js';
3
+ import { countTokens, estimateFullFileBound } from '../../telemetry/tokenizer.js';
4
+ import { queryDependencies } from '../../graph/queries.js';
5
+ import { jitRefreshFile, isIndexEmpty } from './_helpers.js';
6
+ export function registerDependenciesTool(server, ctx) {
7
+ server.tool('mast_dependencies', 'All imports for a file — what it depends on, whether each dependency is external or internal, and the resolved path for monorepo imports.', {
8
+ file_path: z.string().describe('Path to the file, relative to the project root'),
9
+ }, async (args) => {
10
+ const start = Date.now();
11
+ const { busy } = await jitRefreshFile(ctx.db, ctx.config, args.file_path);
12
+ const imports = await queryDependencies(ctx.db, args.file_path);
13
+ const text = JSON.stringify(imports);
14
+ const tokens = countTokens(text);
15
+ const tokensFullFileBound = estimateFullFileBound([args.file_path], ctx.config.resolved_project_root);
16
+ const durationMs = Date.now() - start;
17
+ // M6 (§13.8 item 4): checked ONLY on the empty-result path.
18
+ const indexEmptyField = imports.length === 0 && await isIndexEmpty(ctx)
19
+ ? { index_empty: true }
20
+ : {};
21
+ const response = {
22
+ file_path: args.file_path,
23
+ imports,
24
+ // §9.0 TOCTOU policy: omitted when false, never present-and-false.
25
+ ...(busy ? { file_busy_returning_stale_cache: true } : {}),
26
+ ...indexEmptyField,
27
+ _stats: buildToolStats('mast_dependencies', tokens, tokensFullFileBound, [args.file_path], durationMs),
28
+ };
29
+ void recordToolCall(ctx.db, {
30
+ toolName: 'mast_dependencies', tokensReturned: tokens, tokensFullFileBound,
31
+ durationMs, sessionId: ctx.sessionId, status: 'ok',
32
+ }).catch(() => { });
33
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
34
+ });
35
+ }
36
+ //# sourceMappingURL=dependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../../../src/mcp/tools/dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7D,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,GAAe;IACzE,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,2IAA2I,EAC3I;QACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;KACjF,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAE1E,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAEhE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACtG,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAEtC,4DAA4D;QAC5D,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,YAAY,CAAC,GAAG,CAAC;YACrE,CAAC,CAAC,EAAE,WAAW,EAAE,IAAa,EAAE;YAChC,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,QAAQ,GAAyB;YACrC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO;YACP,mEAAmE;YACnE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,+BAA+B,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,GAAG,eAAe;YAClB,MAAM,EAAE,cAAc,CAAC,mBAAmB,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC;SACvG,CAAC;QACF,KAAK,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE;YAC1B,QAAQ,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,EAAE,mBAAmB;YAC1E,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI;SACnD,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IAClF,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ export declare function registerEfficiencyTool(server: McpServer, ctx: AppContext): void;
4
+ //# sourceMappingURL=efficiency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"efficiency.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/efficiency.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAmD/E"}
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ import { queryMetricsSummary, querySessionSummary } from '../../telemetry/metrics.js';
3
+ import { TOKENIZER_LABEL } from '../../telemetry/tokenizer.js';
4
+ export function registerEfficiencyTool(server, ctx) {
5
+ server.tool('mast_efficiency', 'Token savings report: how many tokens MAST returned vs. how many a naive full-file read would have cost, for this session or globally.', {
6
+ scope: z.enum(['session', 'global']).describe('"session" = current MCP session only; "global" = all-time from metrics DB'),
7
+ since_minutes: z.number().int().min(1).optional().describe('Only include calls within the last N minutes (global scope only)'),
8
+ }, async (args) => {
9
+ const rows = args.scope === 'session'
10
+ ? await querySessionSummary(ctx.db, ctx.sessionId)
11
+ : await queryMetricsSummary(ctx.db, args.since_minutes !== undefined
12
+ ? Date.now() - args.since_minutes * 60_000
13
+ : 0);
14
+ const tokensReturned = rows.reduce((s, r) => s + r.tokens_returned_total, 0);
15
+ const tokensFullFile = rows.reduce((s, r) => s + r.tokens_full_file_total, 0);
16
+ const callsTotal = rows.reduce((s, r) => s + r.calls, 0);
17
+ const callsByTool = Object.fromEntries(rows.map((r) => [r.tool_name, r.calls]));
18
+ const efficiencyRatio = tokensFullFile > 0 ? 1 - tokensReturned / tokensFullFile : 0;
19
+ const saved = tokensFullFile - tokensReturned;
20
+ const windowStartedAt = args.scope === 'global' && args.since_minutes !== undefined
21
+ ? new Date(Date.now() - args.since_minutes * 60_000).toISOString()
22
+ : new Date(0).toISOString();
23
+ const result = {
24
+ scope: args.scope,
25
+ window_started_at: windowStartedAt,
26
+ tokens_returned: tokensReturned,
27
+ tokens_full_file_upper_bound: tokensFullFile,
28
+ efficiency_ratio: efficiencyRatio,
29
+ calls_total: callsTotal,
30
+ calls_by_tool: callsByTool,
31
+ tokenizer: TOKENIZER_LABEL,
32
+ counterfactual: tokensFullFile > 0
33
+ ? `Would have cost ~${tokensFullFile} tokens with naive full-file reads; saved ~${saved} tokens (${(efficiencyRatio * 100).toFixed(1)}%).`
34
+ : 'No tool calls recorded yet.',
35
+ };
36
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
37
+ });
38
+ }
39
+ //# sourceMappingURL=efficiency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"efficiency.js","sourceRoot":"","sources":["../../../src/mcp/tools/efficiency.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ export declare function registerExportsTool(server: McpServer, ctx: AppContext): void;
4
+ //# sourceMappingURL=exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/exports.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQhD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CA8D5E"}
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+ import { join } from 'node:path';
3
+ import { buildToolStats, recordToolCall, buildArgsJson, buildResultsJson } from '../../telemetry/metrics.js';
4
+ import { countTokens, estimateFullFileBound } from '../../telemetry/tokenizer.js';
5
+ import { extractFileSignatures } from '../../ast/extract.js';
6
+ import { extractDoc, jitRefreshFile, isIndexEmpty } from './_helpers.js';
7
+ export function registerExportsTool(server, ctx) {
8
+ server.tool('mast_exports', 'All exported symbols from a single file with type signatures. No function bodies. Use to answer "what does this file expose?" before deciding whether to open it.', {
9
+ file_path: z.string().describe('Path to the file, relative to the project root'),
10
+ }, async (args) => {
11
+ const start = Date.now();
12
+ const { busy } = await jitRefreshFile(ctx.db, ctx.config, args.file_path);
13
+ const chunks = await ctx.chunkStore.getChunksByFilePath(args.file_path);
14
+ // Body-free signatures (params/return stripped of bodies, §10.2), keyed
15
+ // by symbol name. Falls back to chunk content if a symbol isn't found.
16
+ const sigs = extractFileSignatures(join(ctx.config.resolved_project_root, args.file_path));
17
+ const sigByName = new Map(sigs.map((s) => [s.name, s]));
18
+ // Methods surface through their class_shell — omit them here.
19
+ const topLevel = chunks.filter((c) => c.is_exported && c.chunk_type !== 'method');
20
+ const exports = topLevel.map((c) => {
21
+ const sig = c.symbol_name !== null ? sigByName.get(c.symbol_name) : undefined;
22
+ return {
23
+ name: c.symbol_name ?? '',
24
+ kind: c.chunk_type === 'class_shell' ? 'class' : c.chunk_type,
25
+ signature: sig?.signature ?? c.content,
26
+ line: c.start_line,
27
+ doc: sig?.doc ?? extractDoc(c.content),
28
+ };
29
+ });
30
+ const text = JSON.stringify(exports);
31
+ const tokens = countTokens(text);
32
+ const tokensFullFileBound = estimateFullFileBound([args.file_path], ctx.config.resolved_project_root);
33
+ const durationMs = Date.now() - start;
34
+ // M6 (§13.8 item 4): checked ONLY on the empty-result path.
35
+ const indexEmptyField = exports.length === 0 && await isIndexEmpty(ctx)
36
+ ? { index_empty: true }
37
+ : {};
38
+ const response = {
39
+ file_path: args.file_path,
40
+ exports,
41
+ // §9.0 TOCTOU policy: omitted when false, never present-and-false.
42
+ ...(busy ? { file_busy_returning_stale_cache: true } : {}),
43
+ ...indexEmptyField,
44
+ _stats: buildToolStats('mast_exports', tokens, tokensFullFileBound, [args.file_path], durationMs),
45
+ };
46
+ const resultIdentities = exports.map((e) => ({ file_path: args.file_path, symbol_name: e.name }));
47
+ void recordToolCall(ctx.db, {
48
+ toolName: 'mast_exports', tokensReturned: tokens, tokensFullFileBound,
49
+ durationMs, sessionId: ctx.sessionId, status: 'ok',
50
+ argsJson: buildArgsJson(args),
51
+ resultsJson: buildResultsJson(resultIdentities),
52
+ }).catch(() => { });
53
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
54
+ });
55
+ }
56
+ //# sourceMappingURL=exports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.js","sourceRoot":"","sources":["../../../src/mcp/tools/exports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ export declare function registerImplementorsTool(server: McpServer, ctx: AppContext): void;
4
+ //# sourceMappingURL=implementors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implementors.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/implementors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQhD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CA+CjF"}
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ import { buildToolStats, recordToolCall } from '../../telemetry/metrics.js';
3
+ import { countTokens, estimateFullFileBound } from '../../telemetry/tokenizer.js';
4
+ import { queryImplementors } from '../../graph/queries.js';
5
+ import { findStaleFiles } from '../staleness.js';
6
+ import { isIndexEmpty } from './_helpers.js';
7
+ export function registerImplementorsTool(server, ctx) {
8
+ server.tool('mast_implementors', 'All classes that implement a named interface, with the list of methods each class provides.', {
9
+ interface_name: z.string().describe('Name of the interface to find implementors of'),
10
+ }, async (args) => {
11
+ const start = Date.now();
12
+ const results = await queryImplementors(ctx.db, args.interface_name);
13
+ const filesReferenced = [...new Set(results.map((r) => r.file_path))];
14
+ // F7/C1: stat-and-flag, not JIT refresh — surfaced as `stale`, not
15
+ // `file_busy_returning_stale_cache` (that name is reserved for the
16
+ // JIT-refresh tools, where a refresh is actually attempted). See
17
+ // staleness.ts's `findStaleFiles` WHY-comment. Flag before token
18
+ // counting so `_stats.tokens_returned` reflects the payload actually
19
+ // returned.
20
+ const staleFiles = await findStaleFiles(ctx.db, ctx.config, filesReferenced);
21
+ const flaggedResults = staleFiles.size === 0
22
+ ? results
23
+ : results.map((r) => ({
24
+ ...r,
25
+ ...(staleFiles.has(r.file_path) ? { stale: true } : {}),
26
+ }));
27
+ const text = JSON.stringify(flaggedResults);
28
+ const tokens = countTokens(text);
29
+ const tokensFullFileBound = estimateFullFileBound(filesReferenced, ctx.config.resolved_project_root);
30
+ const durationMs = Date.now() - start;
31
+ // M6 (§13.8 item 4): checked ONLY on the empty-result path.
32
+ const indexEmptyField = flaggedResults.length === 0 && await isIndexEmpty(ctx)
33
+ ? { index_empty: true }
34
+ : {};
35
+ const response = {
36
+ results: flaggedResults,
37
+ ...indexEmptyField,
38
+ _stats: buildToolStats('mast_implementors', tokens, tokensFullFileBound, filesReferenced, durationMs),
39
+ };
40
+ void recordToolCall(ctx.db, {
41
+ toolName: 'mast_implementors', tokensReturned: tokens, tokensFullFileBound,
42
+ durationMs, sessionId: ctx.sessionId, status: 'ok',
43
+ }).catch(() => { });
44
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
45
+ });
46
+ }
47
+ //# sourceMappingURL=implementors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implementors.js","sourceRoot":"","sources":["../../../src/mcp/tools/implementors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ export declare function registerProjectSkeletonTool(server: McpServer, ctx: AppContext): void;
4
+ //# sourceMappingURL=project-skeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-skeleton.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/project-skeleton.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAOhD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAkEpF"}
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ import { buildToolStats, recordToolCall } from '../../telemetry/metrics.js';
3
+ import { countTokens, estimateFullFileBound } from '../../telemetry/tokenizer.js';
4
+ import { queryProjectSkeleton } from '../../graph/queries.js';
5
+ import { globToRegex, isIndexEmpty } from './_helpers.js';
6
+ export function registerProjectSkeletonTool(server, ctx) {
7
+ server.tool('mast_project_skeleton', 'All file paths in the project with their exported top-level symbol names. Returns names only — no signatures or bodies. Use early in a task for architectural orientation.', {
8
+ directory: z.string().nullable().optional().describe('Scope to a subtree of the project (e.g. "api/services/auth")'),
9
+ max_depth: z.number().int().min(1).optional().describe('Max directory depth relative to `directory` (default: unlimited)'),
10
+ file_pattern: z.string().nullable().optional().describe('Glob to further restrict files returned'),
11
+ }, async (args) => {
12
+ const start = Date.now();
13
+ const directory = args.directory ?? undefined;
14
+ let rows = await queryProjectSkeleton(ctx.db, directory);
15
+ if (args.max_depth != null && directory != null) {
16
+ const prefix = directory.endsWith('/') ? directory : `${directory}/`;
17
+ rows = rows.filter((r) => {
18
+ const rel = r.file_path.startsWith(prefix)
19
+ ? r.file_path.slice(prefix.length)
20
+ : r.file_path.slice(directory.length).replace(/^\//, '');
21
+ const depth = (rel.match(/\//g) ?? []).length;
22
+ return depth < args.max_depth;
23
+ });
24
+ }
25
+ if (args.file_pattern != null) {
26
+ const rx = globToRegex(args.file_pattern);
27
+ rows = rows.filter((r) => rx.test(r.file_path));
28
+ }
29
+ // Group by file_path, preserving declaration order.
30
+ const fileMap = new Map();
31
+ for (const row of rows) {
32
+ const symbols = fileMap.get(row.file_path) ?? [];
33
+ symbols.push(row.symbol_name);
34
+ fileMap.set(row.file_path, symbols);
35
+ }
36
+ const files = [...fileMap.entries()].map(([file_path, exports]) => ({
37
+ file_path,
38
+ exports,
39
+ }));
40
+ const text = JSON.stringify(files);
41
+ const tokens = countTokens(text);
42
+ const durationMs = Date.now() - start;
43
+ const filesReferenced = files.map((f) => f.file_path);
44
+ const tokensFullFileBound = estimateFullFileBound(filesReferenced, ctx.config.resolved_project_root);
45
+ // M6 (§13.8 item 4): checked ONLY on the empty-result path.
46
+ const indexEmptyField = files.length === 0 && await isIndexEmpty(ctx)
47
+ ? { index_empty: true }
48
+ : {};
49
+ const response = {
50
+ files,
51
+ ...indexEmptyField,
52
+ _stats: buildToolStats('mast_project_skeleton', tokens, tokensFullFileBound, filesReferenced, durationMs),
53
+ };
54
+ void recordToolCall(ctx.db, {
55
+ toolName: 'mast_project_skeleton', tokensReturned: tokens, tokensFullFileBound,
56
+ durationMs, sessionId: ctx.sessionId, status: 'ok',
57
+ }).catch(() => { });
58
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
59
+ });
60
+ }
61
+ //# sourceMappingURL=project-skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-skeleton.js","sourceRoot":"","sources":["../../../src/mcp/tools/project-skeleton.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { AppContext } from '../context.js';
3
+ import type { ReindexResult } from '../../ast/types.js';
4
+ import { type IndexResult } from '../../indexer/index.js';
5
+ export declare function registerReindexTool(server: McpServer, ctx: AppContext): void;
6
+ /**
7
+ * Map the indexer's internal `IndexResult` to the wire `ReindexResult` DTO.
8
+ * Exported so the mapping's VALUES — not just the presence of `write_errors`
9
+ * on the type — are unit-tested directly (§14.6: shape-only assertions have
10
+ * hidden exactly this class of bug before), without needing to drive a real
11
+ * chunk-store failure through the MCP transport.
12
+ */
13
+ export declare function toReindexResult(result: IndexResult): ReindexResult;
14
+ //# sourceMappingURL=reindex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reindex.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/reindex.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAc5E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,CAYlE"}
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ import { runIndex } from '../../indexer/index.js';
3
+ export function registerReindexTool(server, ctx) {
4
+ server.tool('mast_reindex', 'Synchronously reindex changed files. Call this immediately after writing files so subsequent queries see your changes. Incremental by default — only re-parses files whose mtime changed.', {
5
+ full: z.boolean().optional().describe('Force a full reindex of all files (slow; use only after large structural changes)'),
6
+ }, async (args) => {
7
+ const result = await runIndex(ctx.config, { incremental: !(args.full ?? false) });
8
+ const reindexResult = toReindexResult(result);
9
+ return { content: [{ type: 'text', text: JSON.stringify(reindexResult) }] };
10
+ });
11
+ }
12
+ /**
13
+ * Map the indexer's internal `IndexResult` to the wire `ReindexResult` DTO.
14
+ * Exported so the mapping's VALUES — not just the presence of `write_errors`
15
+ * on the type — are unit-tested directly (§14.6: shape-only assertions have
16
+ * hidden exactly this class of bug before), without needing to drive a real
17
+ * chunk-store failure through the MCP transport.
18
+ */
19
+ export function toReindexResult(result) {
20
+ return {
21
+ files_indexed: result.filesIndexed,
22
+ files_skipped: result.filesSkipped,
23
+ chunks_added: result.chunksAdded,
24
+ chunks_removed: result.chunksRemoved,
25
+ parse_errors: result.parseErrors,
26
+ write_errors: result.writeErrors,
27
+ stale_write_rejections: result.staleWriteRejections,
28
+ miscased_imports: result.miscasedImports.count,
29
+ duration_ms: result.durationMs,
30
+ };
31
+ }
32
+ //# sourceMappingURL=reindex.js.map