@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,100 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+ import { CLI_VERSION, PACKAGE_NAME } from './version.js';
5
+ import { detectSkillTargets, installSkillInto } from './skill-install.js';
6
+ import { resolveConfig } from '../store/config.js';
7
+ /**
8
+ * `mast docs` and `mast skill` — documentation that is version-matched by construction.
9
+ *
10
+ * Both read files shipped inside the installed package rather than pointing at a
11
+ * website, so what a user (or an agent) reads is what their binary does. Looking up
12
+ * "which release am I on, and which docs go with it" is exactly the step this removes,
13
+ * and it is the step where a reader silently ends up on docs for a different version.
14
+ */
15
+ export class DocsError extends Error {
16
+ }
17
+ // `dist/cli/` and `src/cli/` both sit two levels below the package root, so this
18
+ // resolves identically under vitest and in the published tarball.
19
+ const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
20
+ export const DOC_TOPICS = [
21
+ { name: 'readme', file: 'README.md', summary: 'Install, quick start, CLI and MCP tool reference' },
22
+ { name: 'spec', file: 'MAST_SPEC.md', summary: 'Full behavioural specification — schemas, tool contracts, invariants' },
23
+ { name: 'skill', file: 'assets/skill.md', summary: 'The instructions to paste into an agent prompt or skill file' },
24
+ ];
25
+ export function readDoc(name) {
26
+ const topic = DOC_TOPICS.find((t) => t.name === name);
27
+ if (topic === undefined) {
28
+ throw new DocsError(`unknown docs topic "${name}"; available topics: ${DOC_TOPICS.map((t) => t.name).join(', ')}`);
29
+ }
30
+ try {
31
+ return readFileSync(join(PACKAGE_ROOT, topic.file), 'utf8');
32
+ }
33
+ catch {
34
+ throw new DocsError(`${topic.file} is missing from this installation of ${PACKAGE_NAME}@${CLI_VERSION}`);
35
+ }
36
+ }
37
+ export function listDocs() {
38
+ const width = Math.max(...DOC_TOPICS.map((t) => t.name.length));
39
+ return [
40
+ `${PACKAGE_NAME} ${CLI_VERSION} — documentation shipped with this build`,
41
+ '',
42
+ ...DOC_TOPICS.map((t) => ` ${t.name.padEnd(width)} ${t.summary}`),
43
+ '',
44
+ 'Read one with `mast docs <topic>`.',
45
+ ].join('\n');
46
+ }
47
+ export function registerDocsCommand(program) {
48
+ program
49
+ .command('docs [topic]')
50
+ .description('Print the documentation shipped with this build — no version lookup needed')
51
+ .action((topic) => {
52
+ try {
53
+ process.stdout.write((topic === undefined ? listDocs() : readDoc(topic)) + '\n');
54
+ }
55
+ catch (err) {
56
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
57
+ process.exitCode = 1;
58
+ }
59
+ });
60
+ }
61
+ export function registerSkillCommand(program) {
62
+ program
63
+ .command('skill [path]')
64
+ .description('Print the MAST instructions to copy into an agent prompt or skill file')
65
+ .option('--install', 'Splice the instructions into this project\'s existing agent config files')
66
+ .option('--dry-run', 'With --install, report what would change without writing')
67
+ .action((projectPath, opts) => {
68
+ try {
69
+ const skill = readDoc('skill');
70
+ if (opts.install !== true) {
71
+ process.stdout.write(skill + '\n');
72
+ return;
73
+ }
74
+ const root = resolveConfig({ projectRoot: projectPath }).resolved_project_root;
75
+ const targets = detectSkillTargets(root);
76
+ if (targets.length === 0) {
77
+ // Not an error: a project with no agent config is a normal state, and the
78
+ // remedy is a decision for the user, not a file this command invents.
79
+ process.stdout.write([`No agent config files found in ${root}.`, '',
80
+ 'Create one of these and re-run, or pipe the skill wherever you keep prompts:',
81
+ ' CLAUDE.md AGENTS.md .cursorrules .windsurfrules .github/copilot-instructions.md', '',
82
+ ' mast skill >> CLAUDE.md'].join('\n') + '\n');
83
+ return;
84
+ }
85
+ const dryRun = opts.dryRun === true;
86
+ for (const { target, changed } of installSkillInto(targets, skill, { dryRun })) {
87
+ const verb = !changed ? 'unchanged' : dryRun ? 'would update' : 'updated';
88
+ process.stdout.write(` ${verb.padEnd(13)} ${target.file} (${target.agent})\n`);
89
+ }
90
+ process.stdout.write(dryRun
91
+ ? '\nDry run — nothing written. Re-run without --dry-run to apply.\n'
92
+ : '\nThe block is marked; re-running after an upgrade replaces it rather than adding a second copy.\n');
93
+ }
94
+ catch (err) {
95
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
96
+ process.exitCode = 1;
97
+ }
98
+ });
99
+ }
100
+ //# sourceMappingURL=docs-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-cmd.js","sourceRoot":"","sources":["../../src/cli/docs-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import type { Command } from 'commander';
2
+ import { type OpenDatabaseOptions } from '../graph/db.js';
3
+ /**
4
+ * Whether to print the per-phase breakdown.
5
+ *
6
+ * Extracted rather than inlined in the action so the gate is testable without spawning the
7
+ * built CLI: a spawn-based test resolves against `dist/`, and a negative assertion
8
+ * ("stdout does not contain phases:") passes trivially when the spawn produces no output at
9
+ * all. That false green is the D8 failure mode in miniature.
10
+ *
11
+ * This is the first environment flag in the codebase and so sets the convention:
12
+ * `ENABLE_`-prefixed, and the value is the word `true` or `false`, never `1`/`0`. Compared
13
+ * case-insensitively after trimming, because env vars are typed by hand. Anything that is
14
+ * not `true` is off, so a typo fails closed rather than silently switching instrumentation
15
+ * on in an ordinary run.
16
+ */
17
+ export declare function isPhaseTimingEnabled(flag: boolean | undefined, env?: NodeJS.ProcessEnv): boolean;
18
+ /**
19
+ * Parse an operator-supplied MiB figure for the SQLite tuning flags.
20
+ *
21
+ * Commander yields raw strings, so this is a trust boundary (§3.2) — the value
22
+ * is validated here and the pragma layer downstream receives only numbers it
23
+ * can apply verbatim. Fractions are refused rather than truncated so that an
24
+ * arm's declared size and its applied size are always the same number; a silent
25
+ * truncation would put a measurement's own label out of step with what ran.
26
+ *
27
+ * Exported for direct testing: a spawn-based CLI test resolves against `dist/`
28
+ * and would grade a different binary than the one under review (the D8 failure
29
+ * mode), which is why `isPhaseTimingEnabled` above is exported for the same
30
+ * reason.
31
+ *
32
+ * @throws Error naming the flag and the rejected value, if it is not a
33
+ * non-negative whole number.
34
+ */
35
+ export declare function parseMebibytes(flag: string, raw: string): number;
36
+ /**
37
+ * Whether to print the applied-pragma line.
38
+ *
39
+ * Emitted when the operator tuned something (they should see what actually took
40
+ * effect, not what they typed), and also under the phase-timing gate — which is
41
+ * what gives the A/B's *control* arm, which passes no tuning flag at all, a
42
+ * `pragmas:` line to be graded against. Without the second clause the one arm
43
+ * whose "un-pragma'd" claim carries the most weight would be the one arm with
44
+ * no evidence for it.
45
+ *
46
+ * Extracted for the same reason as {@link isPhaseTimingEnabled}: a spawn-based
47
+ * test resolves against `dist/`, where a negative assertion passes trivially on
48
+ * empty stdout.
49
+ */
50
+ export declare function shouldPrintPragmas(phaseTimingEnabled: boolean, dbOptions: OpenDatabaseOptions): boolean;
51
+ /**
52
+ * Whether this run skips the per-file FTS5 deletes — E1-FTS's arm G.
53
+ *
54
+ * The skip is only sound on a **cold** build, where those deletes match zero
55
+ * rows and the finished database is therefore identical to the control's. On an
56
+ * incremental run they are load-bearing: skipping them leaves the previous
57
+ * version's rows in `chunk_fts` / `identifier_fts` alongside the new ones while
58
+ * the ordinary tables replace correctly, so the index goes silently wrong — no
59
+ * error, no missing rows, just stale search hits.
60
+ *
61
+ * Refused here, at the trust boundary, rather than documented and hoped for.
62
+ * It **throws** instead of falling back to the safe path, because an operator
63
+ * who asked for arm G and quietly received arm A would collect a null and have
64
+ * no way to tell.
65
+ *
66
+ * Exported for direct testing, same reason as {@link parseMebibytes}.
67
+ *
68
+ * @throws Error naming both flags, if the skip is combined with `--incremental`.
69
+ */
70
+ export declare function resolveSkipFtsDeletes(flag: boolean | undefined, incremental: boolean): boolean;
71
+ export declare function registerIndexCommand(program: Command): void;
72
+ //# sourceMappingURL=index-cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/index-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAIxE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAGT;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAShE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,kBAAkB,EAAE,OAAO,EAC3B,SAAS,EAAE,mBAAmB,GAC7B,OAAO,CAET;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,WAAW,EAAE,OAAO,GACnB,OAAO,CAUT;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+M3D"}
@@ -0,0 +1,263 @@
1
+ import { resolveConfig } from '../store/config.js';
2
+ import { runIndex } from '../indexer/index.js';
3
+ import { newWriteSpans } from '../graph/populate.js';
4
+ import { openDatabase } from '../graph/db.js';
5
+ import { SqliteChunkStore } from '../store/sqliteChunkStore.js';
6
+ import { runCheckerPass } from '../graph/checker-resolver.js';
7
+ /**
8
+ * Whether to print the per-phase breakdown.
9
+ *
10
+ * Extracted rather than inlined in the action so the gate is testable without spawning the
11
+ * built CLI: a spawn-based test resolves against `dist/`, and a negative assertion
12
+ * ("stdout does not contain phases:") passes trivially when the spawn produces no output at
13
+ * all. That false green is the D8 failure mode in miniature.
14
+ *
15
+ * This is the first environment flag in the codebase and so sets the convention:
16
+ * `ENABLE_`-prefixed, and the value is the word `true` or `false`, never `1`/`0`. Compared
17
+ * case-insensitively after trimming, because env vars are typed by hand. Anything that is
18
+ * not `true` is off, so a typo fails closed rather than silently switching instrumentation
19
+ * on in an ordinary run.
20
+ */
21
+ export function isPhaseTimingEnabled(flag, env = process.env) {
22
+ if (flag === true)
23
+ return true;
24
+ return env.ENABLE_MAST_PHASE_TIMING?.trim().toLowerCase() === 'true';
25
+ }
26
+ /**
27
+ * Parse an operator-supplied MiB figure for the SQLite tuning flags.
28
+ *
29
+ * Commander yields raw strings, so this is a trust boundary (§3.2) — the value
30
+ * is validated here and the pragma layer downstream receives only numbers it
31
+ * can apply verbatim. Fractions are refused rather than truncated so that an
32
+ * arm's declared size and its applied size are always the same number; a silent
33
+ * truncation would put a measurement's own label out of step with what ran.
34
+ *
35
+ * Exported for direct testing: a spawn-based CLI test resolves against `dist/`
36
+ * and would grade a different binary than the one under review (the D8 failure
37
+ * mode), which is why `isPhaseTimingEnabled` above is exported for the same
38
+ * reason.
39
+ *
40
+ * @throws Error naming the flag and the rejected value, if it is not a
41
+ * non-negative whole number.
42
+ */
43
+ export function parseMebibytes(flag, raw) {
44
+ // Number('') is 0 — an empty value must be an error, not a silent zero.
45
+ const value = raw.trim() === '' ? Number.NaN : Number(raw);
46
+ if (!Number.isInteger(value) || value < 0) {
47
+ throw new Error(`${flag} expects a non-negative whole number of MiB, received "${raw}"`);
48
+ }
49
+ return value;
50
+ }
51
+ /**
52
+ * Whether to print the applied-pragma line.
53
+ *
54
+ * Emitted when the operator tuned something (they should see what actually took
55
+ * effect, not what they typed), and also under the phase-timing gate — which is
56
+ * what gives the A/B's *control* arm, which passes no tuning flag at all, a
57
+ * `pragmas:` line to be graded against. Without the second clause the one arm
58
+ * whose "un-pragma'd" claim carries the most weight would be the one arm with
59
+ * no evidence for it.
60
+ *
61
+ * Extracted for the same reason as {@link isPhaseTimingEnabled}: a spawn-based
62
+ * test resolves against `dist/`, where a negative assertion passes trivially on
63
+ * empty stdout.
64
+ */
65
+ export function shouldPrintPragmas(phaseTimingEnabled, dbOptions) {
66
+ return phaseTimingEnabled || Object.keys(dbOptions).length > 0;
67
+ }
68
+ /**
69
+ * Whether this run skips the per-file FTS5 deletes — E1-FTS's arm G.
70
+ *
71
+ * The skip is only sound on a **cold** build, where those deletes match zero
72
+ * rows and the finished database is therefore identical to the control's. On an
73
+ * incremental run they are load-bearing: skipping them leaves the previous
74
+ * version's rows in `chunk_fts` / `identifier_fts` alongside the new ones while
75
+ * the ordinary tables replace correctly, so the index goes silently wrong — no
76
+ * error, no missing rows, just stale search hits.
77
+ *
78
+ * Refused here, at the trust boundary, rather than documented and hoped for.
79
+ * It **throws** instead of falling back to the safe path, because an operator
80
+ * who asked for arm G and quietly received arm A would collect a null and have
81
+ * no way to tell.
82
+ *
83
+ * Exported for direct testing, same reason as {@link parseMebibytes}.
84
+ *
85
+ * @throws Error naming both flags, if the skip is combined with `--incremental`.
86
+ */
87
+ export function resolveSkipFtsDeletes(flag, incremental) {
88
+ if (flag !== true)
89
+ return false;
90
+ if (incremental) {
91
+ throw new Error('--unsafe-skip-fts-deletes cannot be combined with --incremental: the FTS deletes are ' +
92
+ 'only redundant on a cold build. On an incremental run they are what removes the ' +
93
+ 'previous version\'s rows, and skipping them corrupts the index silently.');
94
+ }
95
+ return true;
96
+ }
97
+ export function registerIndexCommand(program) {
98
+ program
99
+ .command('index [path]')
100
+ .description('Build or update the index')
101
+ .option('--state-dir <dir>', 'State directory (resolved from config if omitted)')
102
+ .option('--incremental', 'Only reindex files changed since last index run')
103
+ .option('--show-progress', 'Print indexing progress to stderr')
104
+ .option('--checker', 'Opt-in TypeScript-checker pass (Stage 1.2): upgrade heuristic potential_matches into verified edges ' +
105
+ 'or drop non-call-site/wrong-declaration noise. Holds one ts.Program at a time; can take tens of seconds ' +
106
+ 'on a large monorepo — not part of the default index path (MAST_SPEC §10.3.2).')
107
+ .option('--phase-timing', 'Print a machine-readable per-phase breakdown of the index run (walk/parse/write/edges/finalise). ' +
108
+ 'Also enabled by ENABLE_MAST_PHASE_TIMING=true. The timers themselves always run — this only ' +
109
+ 'controls the output line — so enabling it costs nothing but a line of stdout.')
110
+ .option('--cache-size-mib <mib>', 'SQLite page-cache budget for this run, in MiB (PRAGMA cache_size). Diagnostic lever for the ' +
111
+ 'E1-PHASE mechanism probe; omitted, SQLite\'s own default stands and nothing is set.')
112
+ .option('--mmap-size-mib <mib>', 'SQLite memory-map budget for this run, in MiB (PRAGMA mmap_size); 0 disables memory mapping. ' +
113
+ 'Diagnostic lever for the E1-PHASE mechanism probe; omitted, SQLite\'s own default stands.')
114
+ .option('--unsafe-skip-fts-deletes', 'EVAL ONLY — skip the per-file FTS5 delete statements. Sound ONLY on a cold build, where they ' +
115
+ 'match zero rows; on any other path this leaves stale FTS rows behind and corrupts the index ' +
116
+ 'silently, so it is refused with --incremental. E1-FTS arm G.')
117
+ .action(async (projectPath, opts) => {
118
+ const config = resolveConfig({
119
+ projectRoot: projectPath,
120
+ stateDirOverride: opts.stateDir,
121
+ });
122
+ // Built with conditional spreads, not `{ cacheSizeKib: maybeUndefined }`:
123
+ // `exactOptionalPropertyTypes` distinguishes an absent property from one
124
+ // explicitly set to undefined, and the A/B's control arm depends on the
125
+ // property being genuinely absent.
126
+ const dbOptions = {
127
+ ...(opts.cacheSizeMib !== undefined
128
+ ? { cacheSizeKib: parseMebibytes('--cache-size-mib', opts.cacheSizeMib) * 1024 }
129
+ : {}),
130
+ ...(opts.mmapSizeMib !== undefined
131
+ ? { mmapSizeBytes: parseMebibytes('--mmap-size-mib', opts.mmapSizeMib) * 1024 * 1024 }
132
+ : {}),
133
+ };
134
+ const onProgress = opts.showProgress
135
+ ? (processed, total) => {
136
+ if (total === 0)
137
+ return;
138
+ const pct = Math.round((processed / total) * 100);
139
+ const line = ` indexing ${processed}/${total} files (${pct}%)`;
140
+ // \r rewrites the line in place on a TTY; fall back to newlines otherwise.
141
+ process.stderr.write(process.stderr.isTTY ? `\r${line}` : `${line}\n`);
142
+ if (processed === total && process.stderr.isTTY)
143
+ process.stderr.write('\n');
144
+ }
145
+ : undefined;
146
+ const incremental = opts.incremental ?? false;
147
+ const skipFtsDeletes = resolveSkipFtsDeletes(opts.unsafeSkipFtsDeletes, incremental);
148
+ // Allocated only under the phase-timing gate, and passed only when
149
+ // allocated: `populateFile` reads no clock at all without an accumulator,
150
+ // so an ordinary `mast index` is untouched by this instrument.
151
+ const writeSpans = isPhaseTimingEnabled(opts.phaseTiming) ? newWriteSpans() : undefined;
152
+ if (skipFtsDeletes) {
153
+ // Loud on stderr as well as refused above: this run's database is a
154
+ // measurement artifact, and anyone reading the log later must be able
155
+ // to tell it apart from a production index.
156
+ process.stderr.write('[mast] WARN: --unsafe-skip-fts-deletes is set — FTS5 delete statements are being ' +
157
+ 'skipped. This is an eval instrument (E1-FTS arm G) and is sound only because this ' +
158
+ 'is a cold build. Do NOT reuse this index.\n');
159
+ }
160
+ const result = await runIndex(config, {
161
+ incremental,
162
+ onProgress,
163
+ dbOptions,
164
+ ...(writeSpans !== undefined ? { writeSpans } : {}),
165
+ ...(skipFtsDeletes ? { unsafeSkipFtsDeletes: true } : {}),
166
+ });
167
+ process.stdout.write(`files: ${result.filesIndexed} indexed, ${result.filesSkipped} skipped` +
168
+ ` chunks: +${result.chunksAdded} -${result.chunksRemoved}` +
169
+ ` duration: ${result.durationMs}ms` +
170
+ (result.parseErrors > 0 ? ` parse_errors: ${result.parseErrors}` : '') +
171
+ (result.writeErrors > 0 ? ` write_errors: ${result.writeErrors}` : '') +
172
+ (result.staleWriteRejections > 0 ? ` stale_write_rejections: ${result.staleWriteRejections}` : '') +
173
+ (result.miscasedImports.count > 0 ? ` miscased_imports: ${result.miscasedImports.count}` : '') +
174
+ '\n');
175
+ // A mis-cased import is a defect in the indexed repository, not in MAST:
176
+ // it compiles on APFS/NTFS and fails on a case-sensitive filesystem. MAST
177
+ // indexes it correctly regardless (the edge points at the on-disk file),
178
+ // so this is advisory and does not affect the exit code. stderr, because
179
+ // stdout carries the machine-readable summary above.
180
+ if (result.miscasedImports.count > 0) {
181
+ const { count, samples } = result.miscasedImports;
182
+ process.stderr.write(`warning: ${count} import${count === 1 ? '' : 's'} resolved only because this ` +
183
+ `filesystem ignores case; ${count === 1 ? 'it' : 'they'} will not resolve on a ` +
184
+ `case-sensitive one:\n` +
185
+ samples.map((m) => ` ${m.fromFile}: '${m.specifier}' -> ${m.onDiskPath}\n`).join('') +
186
+ (count > samples.length ? ` ... and ${count - samples.length} more\n` : ''));
187
+ }
188
+ // Machine-readable phase breakdown, opt-in. E1 measured a growth exponent of ~1.75
189
+ // from `duration` alone and could not say which phase carried it; the scaling harness
190
+ // parses this line to decompose the next ladder.
191
+ //
192
+ // Gated because it is instrumentation, not a user-facing summary, and an unconditional
193
+ // extra stdout line changes the CLI's output contract for everyone. The env var exists
194
+ // so a harness that spawns the shipped binary can enable it without every call site
195
+ // growing an argument — read HERE, at the CLI boundary, and never inside the indexer.
196
+ if (isPhaseTimingEnabled(opts.phaseTiming)) {
197
+ process.stdout.write(`phases: ${JSON.stringify(result.phaseMs)}\n`);
198
+ }
199
+ // The write phase's own decomposition (E1-FTS). A separate line from
200
+ // `phases:` on purpose — `phases:` is E1-PHASE's scored instrument and a
201
+ // finished record must not sit behind a moving definition, which is the
202
+ // same rule that gave E1-AB its own schedule module rather than an
203
+ // extension of E1's.
204
+ if (writeSpans !== undefined) {
205
+ process.stdout.write(`write_spans: ${JSON.stringify(writeSpans)}\n`);
206
+ }
207
+ // The pragmas SQLite reported for this run's own connection, not an echo
208
+ // of the flags. A flag that failed to reach the connection would make the
209
+ // A/B's two arms identical and produce a clean, credible-looking null —
210
+ // this line is what makes that failure visible instead.
211
+ if (shouldPrintPragmas(isPhaseTimingEnabled(opts.phaseTiming), dbOptions)) {
212
+ process.stdout.write(`pragmas: ${JSON.stringify(result.appliedPragmas)}\n`);
213
+ }
214
+ // Non-zero exit so CI/scripts catch a silently-amputated file — a
215
+ // chunk-store write failure must be impossible to miss, not just a
216
+ // console line a human happens to read (GITNEXUS_COMPARISON.md §16).
217
+ // `exitCode` (not `process.exit()`) lets stdout/stderr flush and any
218
+ // later steps in this action (Phase 2 embed, checker pass) still run.
219
+ if (result.writeErrors > 0)
220
+ process.exitCode = 1;
221
+ // Opt-in Stage 1.2 checker pass — runs after Phase 1 (parse/chunk/graph/
222
+ // FTS is all there is post-Stage-7.1) so it classifies against the
223
+ // freshest graph.db this invocation just wrote. Opens its own db handle
224
+ // (chunkStore wraps it, §15.1) and destroys it itself (runIndex already
225
+ // closed its own), matching the one-shot-process pattern this command
226
+ // already used for the now-removed Phase 2 embed step.
227
+ if (opts.checker) {
228
+ // Same tuning as the index run above: a flag that applied to one half
229
+ // of `mast index` and silently not the other would be a trap.
230
+ const db = openDatabase(config.resolved_state_dir, dbOptions);
231
+ const chunkStore = new SqliteChunkStore(db);
232
+ try {
233
+ const checkerResult = await runCheckerPass(db, chunkStore, config, {
234
+ onProject: opts.showProgress
235
+ ? (configDir, index, total) => {
236
+ const line = ` checker ${index}/${total}: ${configDir}`;
237
+ process.stderr.write(process.stderr.isTTY ? `\r${line}` : `${line}\n`);
238
+ }
239
+ : undefined,
240
+ });
241
+ if (opts.showProgress && process.stderr.isTTY)
242
+ process.stderr.write('\n');
243
+ // No silent caps: every project the pass skipped is named, not just counted.
244
+ for (const skip of checkerResult.projectsSkipped) {
245
+ process.stderr.write(`[mast] checker: skipped ${skip.configDir} (${skip.reason})\n`);
246
+ }
247
+ process.stdout.write(`checker: ${checkerResult.projectsChecked} project(s) checked, ${checkerResult.projectsSkipped.length} skipped` +
248
+ ` symbols_checked: ${checkerResult.symbolsChecked}` +
249
+ ` outside_ts_scope: ${checkerResult.potentialSitesOutsideScope}\n` +
250
+ ` edges_upgraded: ${checkerResult.edgesUpgraded}` +
251
+ ` non_call_site: ${checkerResult.classifiedNonCallSite}` +
252
+ ` different_declaration: ${checkerResult.classifiedDifferentDeclaration}` +
253
+ ` unresolved: ${checkerResult.unresolved}\n` +
254
+ ` duration: ${checkerResult.durationMs}ms` +
255
+ ` peak_rss_mb: ${Math.round(checkerResult.peakRssBytes / (1024 * 1024))}\n`);
256
+ }
257
+ finally {
258
+ await db.destroy();
259
+ }
260
+ }
261
+ });
262
+ }
263
+ //# sourceMappingURL=index-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-cmd.js","sourceRoot":"","sources":["../../src/cli/index-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { buildProgram } from './program.js';
3
+ await buildProgram().parseAsync(process.argv);
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,YAAY,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Parses `mast init --extensions <ext,...>` into `resolveConfig`'s `extensions` override.
4
+ * Returns `undefined` when the flag is absent, so flag absence keeps `resolveConfig`'s
5
+ * default-priority behavior byte-identical (F9, Stage 3.5).
6
+ */
7
+ export declare function parseExtensionsFlag(raw: string | undefined): readonly string[] | undefined;
8
+ /**
9
+ * Parses `mast init --exclude <pattern,...>` into `resolveConfig`'s `excludePatterns` override.
10
+ * Returns `undefined` when the flag is absent, same rationale as `parseExtensionsFlag`.
11
+ */
12
+ export declare function parseExcludeFlag(raw: string | undefined): readonly string[] | undefined;
13
+ export declare function registerInitCommand(program: Command): void;
14
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsBzC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAG1F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAGvF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkC1D"}
@@ -0,0 +1,64 @@
1
+ import { resolveConfig, writeStateConfig } from '../store/config.js';
2
+ import { initLockMarkers } from '../store/lock.js';
3
+ import { runIndex } from '../indexer/index.js';
4
+ /** Splits a comma-separated CLI flag value, trimming entries and dropping empties. */
5
+ function parseCommaSeparatedList(raw) {
6
+ return raw
7
+ .split(',')
8
+ .map((entry) => entry.trim())
9
+ .filter((entry) => entry.length > 0);
10
+ }
11
+ /**
12
+ * Normalizes a user-supplied extension to the leading-dot form `MastConfig.file_extensions`
13
+ * expects (`indexer/walker.ts`'s `walkProject` builds glob patterns as `**\/*${ext}`) — both
14
+ * '.py' and 'py' are accepted on the CLI so users don't have to remember the internal form.
15
+ */
16
+ function normalizeExtension(ext) {
17
+ return ext.startsWith('.') ? ext : `.${ext}`;
18
+ }
19
+ /**
20
+ * Parses `mast init --extensions <ext,...>` into `resolveConfig`'s `extensions` override.
21
+ * Returns `undefined` when the flag is absent, so flag absence keeps `resolveConfig`'s
22
+ * default-priority behavior byte-identical (F9, Stage 3.5).
23
+ */
24
+ export function parseExtensionsFlag(raw) {
25
+ if (raw === undefined)
26
+ return undefined;
27
+ return parseCommaSeparatedList(raw).map(normalizeExtension);
28
+ }
29
+ /**
30
+ * Parses `mast init --exclude <pattern,...>` into `resolveConfig`'s `excludePatterns` override.
31
+ * Returns `undefined` when the flag is absent, same rationale as `parseExtensionsFlag`.
32
+ */
33
+ export function parseExcludeFlag(raw) {
34
+ if (raw === undefined)
35
+ return undefined;
36
+ return parseCommaSeparatedList(raw);
37
+ }
38
+ export function registerInitCommand(program) {
39
+ program
40
+ .command('init [path]')
41
+ .description('Initialise mast for a project and run the initial index')
42
+ .option('--state-dir <dir>', 'Where to write index state (default: <path>/.mast)')
43
+ .option('--extensions <ext,...>', 'Comma-separated file extensions to index (default: .ts,.tsx,.js,.jsx)')
44
+ .option('--exclude <pattern,...>', 'Comma-separated glob patterns to exclude')
45
+ .option('--no-index', 'Create config only; skip initial indexing')
46
+ .action(async (projectPath, opts) => {
47
+ const config = resolveConfig({
48
+ projectRoot: projectPath,
49
+ stateDirOverride: opts.stateDir,
50
+ extensions: parseExtensionsFlag(opts.extensions),
51
+ excludePatterns: parseExcludeFlag(opts.exclude),
52
+ });
53
+ initLockMarkers(config.resolved_state_dir);
54
+ writeStateConfig(config.resolved_state_dir, config);
55
+ process.stdout.write(`Initialised ${config.resolved_state_dir}\n`);
56
+ if (opts.index) {
57
+ const result = await runIndex(config, { incremental: false });
58
+ process.stdout.write(`Indexed ${result.filesIndexed} files, ${result.chunksAdded} chunks in ${result.durationMs}ms` +
59
+ (result.parseErrors > 0 ? ` (${result.parseErrors} parse errors — check stderr)` : '') +
60
+ '\n');
61
+ }
62
+ });
63
+ }
64
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,sFAAsF;AACtF,SAAS,uBAAuB,CAAC,GAAW;IAC1C,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAuB;IACzD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,uBAAuB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAuB;IACtD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,uBAAuB,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,yDAAyD,CAAC;SACtE,MAAM,CAAC,mBAAmB,EAAE,oDAAoD,CAAC;SACjF,MAAM,CAAC,wBAAwB,EAAE,uEAAuE,CAAC;SACzG,MAAM,CAAC,yBAAyB,EAAE,0CAA0C,CAAC;SAC7E,MAAM,CAAC,YAAY,EAAE,2CAA2C,CAAC;SACjE,MAAM,CAAC,KAAK,EAAE,WAA+B,EAAE,IAK/C,EAAE,EAAE;QACH,MAAM,MAAM,GAAG,aAAa,CAAC;YAC3B,WAAW,EAAE,WAAW;YACxB,gBAAgB,EAAE,IAAI,CAAC,QAAQ;YAC/B,UAAU,EAAE,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC;YAChD,eAAe,EAAE,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;SAChD,CAAC,CAAC;QAEH,eAAe,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC3C,gBAAgB,CAAC,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,MAAM,CAAC,kBAAkB,IAAI,CAAC,CAAC;QAEnE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,WAAW,MAAM,CAAC,YAAY,WAAW,MAAM,CAAC,WAAW,cAAc,MAAM,CAAC,UAAU,IAAI;gBAC9F,CAAC,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,WAAW,+BAA+B,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtF,IAAI,CACL,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerInstallHooksCommand(program: Command): void;
3
+ //# sourceMappingURL=install-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hooks.d.ts","sourceRoot":"","sources":["../../src/cli/install-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAezC,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuBlE"}
@@ -0,0 +1,34 @@
1
+ import { existsSync, mkdirSync, writeFileSync, chmodSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ const HOOKS = [
4
+ {
5
+ name: 'post-commit',
6
+ body: '#!/bin/sh\nmast index --incremental\n',
7
+ },
8
+ {
9
+ name: 'post-checkout',
10
+ body: '#!/bin/sh\nmast index --incremental\n',
11
+ },
12
+ ];
13
+ export function registerInstallHooksCommand(program) {
14
+ program
15
+ .command('install-hooks [path]')
16
+ .description('Install git hooks that keep the index fresh after commits and checkouts')
17
+ .action((projectPath) => {
18
+ const root = resolve(projectPath ?? process.cwd());
19
+ const hooksDir = join(root, '.git', 'hooks');
20
+ if (!existsSync(join(root, '.git'))) {
21
+ process.stderr.write(`mast: no .git directory found at ${root}\n`);
22
+ process.exit(1);
23
+ }
24
+ mkdirSync(hooksDir, { recursive: true });
25
+ for (const hook of HOOKS) {
26
+ const hookPath = join(hooksDir, hook.name);
27
+ writeFileSync(hookPath, hook.body, { encoding: 'utf-8' });
28
+ // Git requires hooks to be executable.
29
+ chmodSync(hookPath, 0o755);
30
+ process.stdout.write(`Installed ${hookPath}\n`);
31
+ }
32
+ });
33
+ }
34
+ //# sourceMappingURL=install-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hooks.js","sourceRoot":"","sources":["../../src/cli/install-hooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,KAAK,GAAkD;IAC3D;QACE,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,uCAAuC;KAC9C;IACD;QACE,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,uCAAuC;KAC9C;CACF,CAAC;AAEF,MAAM,UAAU,2BAA2B,CAAC,OAAgB;IAC1D,OAAO;SACJ,OAAO,CAAC,sBAAsB,CAAC;SAC/B,WAAW,CAAC,yEAAyE,CAAC;SACtF,MAAM,CAAC,CAAC,WAA+B,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAE7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,IAAI,IAAI,CAAC,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YAC1D,uCAAuC;YACvC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,QAAQ,IAAI,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerMetricsCommand(program: Command): void;
3
+ //# sourceMappingURL=metrics-cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/metrics-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4IzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgE7D"}