@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.
- package/LICENSE +21 -0
- package/MAST_SPEC.md +2926 -0
- package/README.md +842 -0
- package/assets/skill.md +45 -0
- package/dist/ast/extract.d.ts +65 -0
- package/dist/ast/extract.d.ts.map +1 -0
- package/dist/ast/extract.js +147 -0
- package/dist/ast/extract.js.map +1 -0
- package/dist/ast/extractor.d.ts +85 -0
- package/dist/ast/extractor.d.ts.map +1 -0
- package/dist/ast/extractor.js +2 -0
- package/dist/ast/extractor.js.map +1 -0
- package/dist/ast/extractors/markdown.d.ts +40 -0
- package/dist/ast/extractors/markdown.d.ts.map +1 -0
- package/dist/ast/extractors/markdown.js +175 -0
- package/dist/ast/extractors/markdown.js.map +1 -0
- package/dist/ast/extractors/typescript.d.ts +179 -0
- package/dist/ast/extractors/typescript.d.ts.map +1 -0
- package/dist/ast/extractors/typescript.js +1439 -0
- package/dist/ast/extractors/typescript.js.map +1 -0
- package/dist/ast/parser.d.ts +5 -0
- package/dist/ast/parser.d.ts.map +1 -0
- package/dist/ast/parser.js +21 -0
- package/dist/ast/parser.js.map +1 -0
- package/dist/ast/types.d.ts +586 -0
- package/dist/ast/types.d.ts.map +1 -0
- package/dist/ast/types.js +3 -0
- package/dist/ast/types.js.map +1 -0
- package/dist/cli/docs-cmd.d.ts +22 -0
- package/dist/cli/docs-cmd.d.ts.map +1 -0
- package/dist/cli/docs-cmd.js +100 -0
- package/dist/cli/docs-cmd.js.map +1 -0
- package/dist/cli/index-cmd.d.ts +72 -0
- package/dist/cli/index-cmd.d.ts.map +1 -0
- package/dist/cli/index-cmd.js +263 -0
- package/dist/cli/index-cmd.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +14 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +64 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/install-hooks.d.ts +3 -0
- package/dist/cli/install-hooks.d.ts.map +1 -0
- package/dist/cli/install-hooks.js +34 -0
- package/dist/cli/install-hooks.js.map +1 -0
- package/dist/cli/metrics-cmd.d.ts +3 -0
- package/dist/cli/metrics-cmd.d.ts.map +1 -0
- package/dist/cli/metrics-cmd.js +164 -0
- package/dist/cli/metrics-cmd.js.map +1 -0
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +44 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/query.d.ts +46 -0
- package/dist/cli/query.d.ts.map +1 -0
- package/dist/cli/query.js +153 -0
- package/dist/cli/query.js.map +1 -0
- package/dist/cli/search-cmd.d.ts +33 -0
- package/dist/cli/search-cmd.d.ts.map +1 -0
- package/dist/cli/search-cmd.js +114 -0
- package/dist/cli/search-cmd.js.map +1 -0
- package/dist/cli/serve.d.ts +3 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +19 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/skill-install.d.ts +39 -0
- package/dist/cli/skill-install.d.ts.map +1 -0
- package/dist/cli/skill-install.js +57 -0
- package/dist/cli/skill-install.js.map +1 -0
- package/dist/cli/status.d.ts +35 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +117 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/upgrade-cmd.d.ts +48 -0
- package/dist/cli/upgrade-cmd.d.ts.map +1 -0
- package/dist/cli/upgrade-cmd.js +129 -0
- package/dist/cli/upgrade-cmd.js.map +1 -0
- package/dist/cli/version.d.ts +3 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +19 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/env.d.ts +17 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +17 -0
- package/dist/env.js.map +1 -0
- package/dist/graph/checker-resolver.d.ts +156 -0
- package/dist/graph/checker-resolver.d.ts.map +1 -0
- package/dist/graph/checker-resolver.js +458 -0
- package/dist/graph/checker-resolver.js.map +1 -0
- package/dist/graph/db.d.ts +318 -0
- package/dist/graph/db.d.ts.map +1 -0
- package/dist/graph/db.js +266 -0
- package/dist/graph/db.js.map +1 -0
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +5 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/local-type-env.d.ts +45 -0
- package/dist/graph/local-type-env.d.ts.map +1 -0
- package/dist/graph/local-type-env.js +59 -0
- package/dist/graph/local-type-env.js.map +1 -0
- package/dist/graph/path-range.d.ts +40 -0
- package/dist/graph/path-range.d.ts.map +1 -0
- package/dist/graph/path-range.js +42 -0
- package/dist/graph/path-range.js.map +1 -0
- package/dist/graph/populate.d.ts +263 -0
- package/dist/graph/populate.d.ts.map +1 -0
- package/dist/graph/populate.js +984 -0
- package/dist/graph/populate.js.map +1 -0
- package/dist/graph/queries.d.ts +114 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +415 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/sqliteBatch.d.ts +48 -0
- package/dist/graph/sqliteBatch.d.ts.map +1 -0
- package/dist/graph/sqliteBatch.js +93 -0
- package/dist/graph/sqliteBatch.js.map +1 -0
- package/dist/indexer/freshness.d.ts +48 -0
- package/dist/indexer/freshness.d.ts.map +1 -0
- package/dist/indexer/freshness.js +72 -0
- package/dist/indexer/freshness.js.map +1 -0
- package/dist/indexer/import-resolver.d.ts +50 -0
- package/dist/indexer/import-resolver.d.ts.map +1 -0
- package/dist/indexer/import-resolver.js +318 -0
- package/dist/indexer/import-resolver.js.map +1 -0
- package/dist/indexer/index.d.ts +249 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +560 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/walker.d.ts +60 -0
- package/dist/indexer/walker.d.ts.map +1 -0
- package/dist/indexer/walker.js +128 -0
- package/dist/indexer/walker.js.map +1 -0
- package/dist/indexer/watcher.d.ts +80 -0
- package/dist/indexer/watcher.d.ts.map +1 -0
- package/dist/indexer/watcher.js +151 -0
- package/dist/indexer/watcher.js.map +1 -0
- package/dist/mcp/context.d.ts +23 -0
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/context.js +2 -0
- package/dist/mcp/context.js.map +1 -0
- package/dist/mcp/register-tools.d.ts +21 -0
- package/dist/mcp/register-tools.d.ts.map +1 -0
- package/dist/mcp/register-tools.js +42 -0
- package/dist/mcp/register-tools.js.map +1 -0
- package/dist/mcp/server.d.ts +58 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +177 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/staleness.d.ts +72 -0
- package/dist/mcp/staleness.d.ts.map +1 -0
- package/dist/mcp/staleness.js +213 -0
- package/dist/mcp/staleness.js.map +1 -0
- package/dist/mcp/startup.d.ts +40 -0
- package/dist/mcp/startup.d.ts.map +1 -0
- package/dist/mcp/startup.js +114 -0
- package/dist/mcp/startup.js.map +1 -0
- package/dist/mcp/tools/_helpers.d.ts +26 -0
- package/dist/mcp/tools/_helpers.d.ts.map +1 -0
- package/dist/mcp/tools/_helpers.js +54 -0
- package/dist/mcp/tools/_helpers.js.map +1 -0
- package/dist/mcp/tools/callers.d.ts +4 -0
- package/dist/mcp/tools/callers.d.ts.map +1 -0
- package/dist/mcp/tools/callers.js +112 -0
- package/dist/mcp/tools/callers.js.map +1 -0
- package/dist/mcp/tools/dependencies.d.ts +4 -0
- package/dist/mcp/tools/dependencies.d.ts.map +1 -0
- package/dist/mcp/tools/dependencies.js +36 -0
- package/dist/mcp/tools/dependencies.js.map +1 -0
- package/dist/mcp/tools/efficiency.d.ts +4 -0
- package/dist/mcp/tools/efficiency.d.ts.map +1 -0
- package/dist/mcp/tools/efficiency.js +39 -0
- package/dist/mcp/tools/efficiency.js.map +1 -0
- package/dist/mcp/tools/exports.d.ts +4 -0
- package/dist/mcp/tools/exports.d.ts.map +1 -0
- package/dist/mcp/tools/exports.js +56 -0
- package/dist/mcp/tools/exports.js.map +1 -0
- package/dist/mcp/tools/implementors.d.ts +4 -0
- package/dist/mcp/tools/implementors.d.ts.map +1 -0
- package/dist/mcp/tools/implementors.js +47 -0
- package/dist/mcp/tools/implementors.js.map +1 -0
- package/dist/mcp/tools/project-skeleton.d.ts +4 -0
- package/dist/mcp/tools/project-skeleton.d.ts.map +1 -0
- package/dist/mcp/tools/project-skeleton.js +61 -0
- package/dist/mcp/tools/project-skeleton.js.map +1 -0
- package/dist/mcp/tools/reindex.d.ts +14 -0
- package/dist/mcp/tools/reindex.d.ts.map +1 -0
- package/dist/mcp/tools/reindex.js +32 -0
- package/dist/mcp/tools/reindex.js.map +1 -0
- package/dist/mcp/tools/rename-impact.d.ts +4 -0
- package/dist/mcp/tools/rename-impact.d.ts.map +1 -0
- package/dist/mcp/tools/rename-impact.js +124 -0
- package/dist/mcp/tools/rename-impact.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +4 -0
- package/dist/mcp/tools/search.d.ts.map +1 -0
- package/dist/mcp/tools/search.js +69 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/mcp/tools/signature.d.ts +4 -0
- package/dist/mcp/tools/signature.d.ts.map +1 -0
- package/dist/mcp/tools/signature.js +127 -0
- package/dist/mcp/tools/signature.js.map +1 -0
- package/dist/mcp/tools/status.d.ts +4 -0
- package/dist/mcp/tools/status.d.ts.map +1 -0
- package/dist/mcp/tools/status.js +30 -0
- package/dist/mcp/tools/status.js.map +1 -0
- package/dist/search/declex.d.ts +109 -0
- package/dist/search/declex.d.ts.map +1 -0
- package/dist/search/declex.js +242 -0
- package/dist/search/declex.js.map +1 -0
- package/dist/search/fts.d.ts +77 -0
- package/dist/search/fts.d.ts.map +1 -0
- package/dist/search/fts.js +223 -0
- package/dist/search/fts.js.map +1 -0
- package/dist/search/fused.d.ts +110 -0
- package/dist/search/fused.d.ts.map +1 -0
- package/dist/search/fused.js +323 -0
- package/dist/search/fused.js.map +1 -0
- package/dist/search/potential-matches.d.ts +88 -0
- package/dist/search/potential-matches.d.ts.map +1 -0
- package/dist/search/potential-matches.js +83 -0
- package/dist/search/potential-matches.js.map +1 -0
- package/dist/search/scope.d.ts +66 -0
- package/dist/search/scope.d.ts.map +1 -0
- package/dist/search/scope.js +71 -0
- package/dist/search/scope.js.map +1 -0
- package/dist/store/config.d.ts +57 -0
- package/dist/store/config.d.ts.map +1 -0
- package/dist/store/config.js +208 -0
- package/dist/store/config.js.map +1 -0
- package/dist/store/lock.d.ts +101 -0
- package/dist/store/lock.d.ts.map +1 -0
- package/dist/store/lock.js +182 -0
- package/dist/store/lock.js.map +1 -0
- package/dist/store/lockMetrics.d.ts +52 -0
- package/dist/store/lockMetrics.d.ts.map +1 -0
- package/dist/store/lockMetrics.js +29 -0
- package/dist/store/lockMetrics.js.map +1 -0
- package/dist/store/sqliteChunkStore.d.ts +44 -0
- package/dist/store/sqliteChunkStore.d.ts.map +1 -0
- package/dist/store/sqliteChunkStore.js +135 -0
- package/dist/store/sqliteChunkStore.js.map +1 -0
- package/dist/telemetry/lockMetricsSummary.d.ts +42 -0
- package/dist/telemetry/lockMetricsSummary.d.ts.map +1 -0
- package/dist/telemetry/lockMetricsSummary.js +139 -0
- package/dist/telemetry/lockMetricsSummary.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +140 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/metrics.js +312 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/telemetry/tokenizer.d.ts +125 -0
- package/dist/telemetry/tokenizer.d.ts.map +1 -0
- package/dist/telemetry/tokenizer.js +174 -0
- package/dist/telemetry/tokenizer.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { resolveConfig } from '../store/config.js';
|
|
2
|
+
import { openDatabase } from '../graph/db.js';
|
|
3
|
+
import { queryMetricsSummaryWithPercentiles, rollupMetrics, vacuumMetrics, } from '../telemetry/metrics.js';
|
|
4
|
+
import { TOKENIZER_LABEL } from '../telemetry/tokenizer.js';
|
|
5
|
+
import { readLockMetricsSummary, } from '../telemetry/lockMetricsSummary.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Duration string parser ("7d", "24h", "30m")
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
function parseSince(value) {
|
|
10
|
+
const match = /^(\d+)([dhm])$/.exec(value);
|
|
11
|
+
if (match === null) {
|
|
12
|
+
throw new Error(`Invalid --since value "${value}". Expected format: 7d, 24h, 30m`);
|
|
13
|
+
}
|
|
14
|
+
const n = match[1];
|
|
15
|
+
const unit = match[2];
|
|
16
|
+
const multipliers = { d: 86_400_000, h: 3_600_000, m: 60_000 };
|
|
17
|
+
return Number(n) * multipliers[unit];
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Table printer — per-tool efficiency (with D6's p50/p95 duration columns)
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function printTable(rows) {
|
|
23
|
+
if (rows.length === 0) {
|
|
24
|
+
process.stdout.write('No metrics recorded in the requested window.\n');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const header = ['Tool', 'Calls', 'Tokens', 'Avg ms', 'p50 ms', 'p95 ms', 'Efficiency'];
|
|
28
|
+
const widths = [
|
|
29
|
+
Math.max(header[0].length, ...rows.map((r) => r.tool_name.length)),
|
|
30
|
+
Math.max(header[1].length, ...rows.map((r) => String(r.calls).length)),
|
|
31
|
+
Math.max(header[2].length, ...rows.map((r) => String(r.tokens_returned_total).length)),
|
|
32
|
+
Math.max(header[3].length, ...rows.map((r) => r.avg_duration_ms.toFixed(1).length)),
|
|
33
|
+
Math.max(header[4].length, ...rows.map((r) => String(r.p50_duration_ms).length)),
|
|
34
|
+
Math.max(header[5].length, ...rows.map((r) => String(r.p95_duration_ms).length)),
|
|
35
|
+
header[6].length,
|
|
36
|
+
];
|
|
37
|
+
const pad = (s, w) => s.padEnd(w);
|
|
38
|
+
const sep = widths.map((w) => '-'.repeat(w)).join(' ');
|
|
39
|
+
const line = (cols) => cols.map((c, i) => pad(c, widths[i])).join(' ');
|
|
40
|
+
process.stdout.write(line(header) + '\n');
|
|
41
|
+
process.stdout.write(sep + '\n');
|
|
42
|
+
for (const r of rows) {
|
|
43
|
+
process.stdout.write(line([
|
|
44
|
+
r.tool_name,
|
|
45
|
+
String(r.calls),
|
|
46
|
+
String(r.tokens_returned_total),
|
|
47
|
+
r.avg_duration_ms.toFixed(1),
|
|
48
|
+
String(r.p50_duration_ms),
|
|
49
|
+
String(r.p95_duration_ms),
|
|
50
|
+
`${(r.efficiency_ratio * 100).toFixed(1)}%`,
|
|
51
|
+
]) + '\n');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Table printer — lock-hold summary (D6 deliverable 2, `mast metrics --locks`)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
function printLocksTable(callers) {
|
|
58
|
+
const header = ['Caller', 'Count', 'Hold p50', 'Hold p95', 'Hold max', 'Wait p50', 'Wait p95', 'Wait max', 'Failed'];
|
|
59
|
+
const cell = (c) => [
|
|
60
|
+
c.caller,
|
|
61
|
+
String(c.count),
|
|
62
|
+
String(c.hold_p50_ms),
|
|
63
|
+
String(c.hold_p95_ms),
|
|
64
|
+
String(c.hold_max_ms),
|
|
65
|
+
String(c.wait_p50_ms),
|
|
66
|
+
String(c.wait_p95_ms),
|
|
67
|
+
String(c.wait_max_ms),
|
|
68
|
+
String(c.failed_count),
|
|
69
|
+
];
|
|
70
|
+
const rows = callers.map(cell);
|
|
71
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
72
|
+
const pad = (s, w) => s.padEnd(w);
|
|
73
|
+
const line = (cols) => cols.map((c, i) => pad(c, widths[i])).join(' ');
|
|
74
|
+
const sep = widths.map((w) => '-'.repeat(w)).join(' ');
|
|
75
|
+
process.stdout.write(line(header) + '\n');
|
|
76
|
+
process.stdout.write(sep + '\n');
|
|
77
|
+
for (const r of rows)
|
|
78
|
+
process.stdout.write(line(r) + '\n');
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Print the `--locks` report: human table or `--json`, for either a real
|
|
82
|
+
* summary or the "nothing recorded" case (missing/empty JSONL).
|
|
83
|
+
*
|
|
84
|
+
* Never throws on malformed input — {@link readLockMetricsSummary} already
|
|
85
|
+
* skips malformed lines inside {@link summarizeLockMetricsJsonl}; this
|
|
86
|
+
* function surfaces the skipped-line count as a visible warning rather than
|
|
87
|
+
* silently dropping it.
|
|
88
|
+
*/
|
|
89
|
+
function printLocksReport(stateDir, json) {
|
|
90
|
+
const summary = readLockMetricsSummary(stateDir) ?? {
|
|
91
|
+
callers: [],
|
|
92
|
+
malformed_line_count: 0,
|
|
93
|
+
};
|
|
94
|
+
if (json) {
|
|
95
|
+
process.stdout.write(JSON.stringify(summary, null, 2) + '\n');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (summary.callers.length === 0) {
|
|
99
|
+
process.stdout.write('No lock metrics recorded.\n');
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
printLocksTable(summary.callers);
|
|
103
|
+
}
|
|
104
|
+
if (summary.malformed_line_count > 0) {
|
|
105
|
+
process.stdout.write(`\nWarning: skipped ${summary.malformed_line_count} malformed line(s) in lock-metrics.jsonl.\n`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Command registration
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
export function registerMetricsCommand(program) {
|
|
112
|
+
program
|
|
113
|
+
.command('metrics [path]')
|
|
114
|
+
.description('Show token-efficiency metrics from the metrics database')
|
|
115
|
+
.option('--since <window>', 'Time window: 7d, 24h, 30m (default: 7d)', '7d')
|
|
116
|
+
.option('--by-tool', 'Break down by tool name (default: true)', true)
|
|
117
|
+
.option('--rollup', 'Collapse raw rows older than --keep-days into daily roll-ups')
|
|
118
|
+
.option('--vacuum', 'Delete daily roll-up rows older than --keep-days')
|
|
119
|
+
.option('--keep-days <n>', 'Retention days for --rollup / --vacuum (default: 7 for rollup, 90 for vacuum)', undefined)
|
|
120
|
+
.option('--locks', 'Summarize structure-lock hold/wait timing by caller (store/lockMetrics.ts JSONL)')
|
|
121
|
+
.option('--json', 'Machine-readable output')
|
|
122
|
+
.option('--state-dir <dir>', 'State directory override')
|
|
123
|
+
.action(async (projectPath, opts) => {
|
|
124
|
+
const config = resolveConfig({ projectRoot: projectPath, stateDirOverride: opts.stateDir });
|
|
125
|
+
// --locks reads a flat JSONL file, not graph.db — no need to open the
|
|
126
|
+
// database at all for this branch.
|
|
127
|
+
if (opts.locks === true) {
|
|
128
|
+
printLocksReport(config.resolved_state_dir, opts.json === true);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const db = openDatabase(config.resolved_state_dir);
|
|
132
|
+
try {
|
|
133
|
+
if (opts.rollup === true) {
|
|
134
|
+
const keepDays = opts.keepDays !== undefined ? Number(opts.keepDays) : 7;
|
|
135
|
+
const deleted = await rollupMetrics(db, keepDays);
|
|
136
|
+
process.stdout.write(`Rolled up ${deleted} raw metric rows (kept last ${keepDays} days).\n`);
|
|
137
|
+
}
|
|
138
|
+
if (opts.vacuum === true) {
|
|
139
|
+
const keepDays = opts.keepDays !== undefined ? Number(opts.keepDays) : 90;
|
|
140
|
+
const deleted = await vacuumMetrics(db, keepDays);
|
|
141
|
+
process.stdout.write(`Vacuumed ${deleted} daily roll-up rows (kept last ${keepDays} days).\n`);
|
|
142
|
+
}
|
|
143
|
+
if (opts.rollup !== true && opts.vacuum !== true) {
|
|
144
|
+
const sinceMs = Date.now() - parseSince(opts.since);
|
|
145
|
+
const rows = await queryMetricsSummaryWithPercentiles(db, sinceMs);
|
|
146
|
+
if (opts.json === true) {
|
|
147
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + '\n');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
printTable(rows);
|
|
151
|
+
if (rows.length > 0) {
|
|
152
|
+
// Honesty footer (§14.5): counts are approximate for current
|
|
153
|
+
// models; the savings ratio is robust to the per-count error.
|
|
154
|
+
process.stdout.write(`\nTokenizer: ${TOKENIZER_LABEL}\n`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
await db.destroy();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=metrics-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-cmd.js","sourceRoot":"","sources":["../../src/cli/metrics-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the CLI. This is the *only* place commands are registered.
|
|
4
|
+
*
|
|
5
|
+
* `cli/index.ts` parses the program this returns, and the README drift guard in
|
|
6
|
+
* `docs-cmd.test.ts` enumerates it. Both read the same builder on purpose: a
|
|
7
|
+
* separate list of commands maintained for the test would be a second producer of
|
|
8
|
+
* one value (shape S-05), and a guard that can fall out of step with the thing it
|
|
9
|
+
* guards is worse than none — it reports green while the README rots.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildProgram(): Command;
|
|
12
|
+
/** Every registered command name, sorted. Derived from `buildProgram`, never restated. */
|
|
13
|
+
export declare function registeredCommandNames(): readonly string[];
|
|
14
|
+
//# sourceMappingURL=program.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAmBtC;AAED,0FAA0F;AAC1F,wBAAgB,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAE1D"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { CLI_VERSION } from './version.js';
|
|
3
|
+
import { registerInitCommand } from './init.js';
|
|
4
|
+
import { registerIndexCommand } from './index-cmd.js';
|
|
5
|
+
import { registerSearchCommand } from './search-cmd.js';
|
|
6
|
+
import { registerServeCommand } from './serve.js';
|
|
7
|
+
import { registerStatusCommand } from './status.js';
|
|
8
|
+
import { registerInstallHooksCommand } from './install-hooks.js';
|
|
9
|
+
import { registerMetricsCommand } from './metrics-cmd.js';
|
|
10
|
+
import { registerQueryCommand } from './query.js';
|
|
11
|
+
import { registerDocsCommand, registerSkillCommand } from './docs-cmd.js';
|
|
12
|
+
import { registerUpgradeCommand } from './upgrade-cmd.js';
|
|
13
|
+
/**
|
|
14
|
+
* Builds the CLI. This is the *only* place commands are registered.
|
|
15
|
+
*
|
|
16
|
+
* `cli/index.ts` parses the program this returns, and the README drift guard in
|
|
17
|
+
* `docs-cmd.test.ts` enumerates it. Both read the same builder on purpose: a
|
|
18
|
+
* separate list of commands maintained for the test would be a second producer of
|
|
19
|
+
* one value (shape S-05), and a guard that can fall out of step with the thing it
|
|
20
|
+
* guards is worse than none — it reports green while the README rots.
|
|
21
|
+
*/
|
|
22
|
+
export function buildProgram() {
|
|
23
|
+
const program = new Command()
|
|
24
|
+
.name('mast')
|
|
25
|
+
.description('Monorepo AST Search Tool — lexical + declaration-exact code search over an MCP or CLI surface')
|
|
26
|
+
.version(CLI_VERSION);
|
|
27
|
+
registerInitCommand(program);
|
|
28
|
+
registerIndexCommand(program);
|
|
29
|
+
registerSearchCommand(program);
|
|
30
|
+
registerServeCommand(program);
|
|
31
|
+
registerStatusCommand(program);
|
|
32
|
+
registerInstallHooksCommand(program);
|
|
33
|
+
registerMetricsCommand(program);
|
|
34
|
+
registerQueryCommand(program);
|
|
35
|
+
registerDocsCommand(program);
|
|
36
|
+
registerSkillCommand(program);
|
|
37
|
+
registerUpgradeCommand(program);
|
|
38
|
+
return program;
|
|
39
|
+
}
|
|
40
|
+
/** Every registered command name, sorted. Derived from `buildProgram`, never restated. */
|
|
41
|
+
export function registeredCommandNames() {
|
|
42
|
+
return buildProgram().commands.map((c) => c.name()).sort();
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=program.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;SAC1B,IAAI,CAAC,MAAM,CAAC;SACZ,WAAW,CAAC,+FAA+F,CAAC;SAC5G,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,2BAA2B,CAAC,OAAO,CAAC,CAAC;IACrC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,sBAAsB;IACpC,OAAO,YAAY,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* D0 (IMPLEMENTATION_PLAN.md "Stage 4: Determinism, hygiene, and the
|
|
4
|
+
* measurement harness", "### D0 — CLI query surface"): `mast query <tool>
|
|
5
|
+
* <json>` dispatches through the exact registered MCP tool handlers instead
|
|
6
|
+
* of re-implementing tool logic, so CLI output can never drift from the MCP
|
|
7
|
+
* transport's JIT/staleness/`_stats` behavior.
|
|
8
|
+
*
|
|
9
|
+
* Every user-facing failure mode below is thrown as `QueryError` (project
|
|
10
|
+
* CLAUDE.md §8.2: throw Error subclasses, never strings) so
|
|
11
|
+
* `registerQueryCommand`'s action can map it uniformly to a stderr message +
|
|
12
|
+
* exit 1, distinct from a tool-internal error propagating unchanged.
|
|
13
|
+
*/
|
|
14
|
+
export declare class QueryError extends Error {
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Every tool name the MCP surface registers, sorted.
|
|
18
|
+
*
|
|
19
|
+
* Registration is pure: each `registerXTool(server, ctx)` passes `ctx` into the
|
|
20
|
+
* async handler's closure and never dereferences it while registering (verified
|
|
21
|
+
* across all 11). This enumerates without a database by handing registration a
|
|
22
|
+
* proxy that throws on ANY property access — so if a future tool starts reading
|
|
23
|
+
* `ctx` at registration time, this fails loudly instead of quietly returning a
|
|
24
|
+
* short list, and the drift guard in `docs-cmd.test.ts` keeps its meaning.
|
|
25
|
+
*/
|
|
26
|
+
export declare function listRegisteredToolNames(): readonly string[];
|
|
27
|
+
export interface RunQueryOptions {
|
|
28
|
+
/** State directory override, same semantics as every other CLI command's `--state-dir`. */
|
|
29
|
+
readonly stateDir?: string;
|
|
30
|
+
/** Project root path (the CLI's positional `[path]`); defaults to cwd via `resolveConfig`. */
|
|
31
|
+
readonly path?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Dispatch a single CLI query through the exact registered MCP tool handler
|
|
35
|
+
* and return its serialized response text — byte-identical to what an MCP
|
|
36
|
+
* client would receive for the same tool + args.
|
|
37
|
+
*
|
|
38
|
+
* @throws QueryError when the resolved state dir has never been indexed, the
|
|
39
|
+
* JSON argument string fails to parse, `toolName` does not match a
|
|
40
|
+
* registered tool, or the parsed args fail that tool's own zod schema. Any
|
|
41
|
+
* other error (a tool-internal failure) propagates unchanged, same as it
|
|
42
|
+
* would over the MCP transport.
|
|
43
|
+
*/
|
|
44
|
+
export declare function runQuery(toolName: string, jsonArgs: string, options?: RunQueryOptions): Promise<string>;
|
|
45
|
+
export declare function registerQueryCommand(program: Command): void;
|
|
46
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/cli/query.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQzC;;;;;;;;;;;GAWG;AACH,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAoCxC;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,IAAI,SAAS,MAAM,EAAE,CAW3D;AAED,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6DjH;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwB3D"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z, ZodError } from 'zod';
|
|
5
|
+
import { resolveConfig } from '../store/config.js';
|
|
6
|
+
import { openDatabase } from '../graph/db.js';
|
|
7
|
+
import { SqliteChunkStore } from '../store/sqliteChunkStore.js';
|
|
8
|
+
import { registerAllTools } from '../mcp/register-tools.js';
|
|
9
|
+
/**
|
|
10
|
+
* D0 (IMPLEMENTATION_PLAN.md "Stage 4: Determinism, hygiene, and the
|
|
11
|
+
* measurement harness", "### D0 — CLI query surface"): `mast query <tool>
|
|
12
|
+
* <json>` dispatches through the exact registered MCP tool handlers instead
|
|
13
|
+
* of re-implementing tool logic, so CLI output can never drift from the MCP
|
|
14
|
+
* transport's JIT/staleness/`_stats` behavior.
|
|
15
|
+
*
|
|
16
|
+
* Every user-facing failure mode below is thrown as `QueryError` (project
|
|
17
|
+
* CLAUDE.md §8.2: throw Error subclasses, never strings) so
|
|
18
|
+
* `registerQueryCommand`'s action can map it uniformly to a stderr message +
|
|
19
|
+
* exit 1, distinct from a tool-internal error propagating unchanged.
|
|
20
|
+
*/
|
|
21
|
+
export class QueryError extends Error {
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Structural stand-in for `McpServer` that records each tool's zod schema
|
|
25
|
+
* shape and handler instead of opening a real MCP transport.
|
|
26
|
+
*
|
|
27
|
+
* `registerAllTools` only ever calls the 4-argument
|
|
28
|
+
* `tool(name, description, schemaShape, handler)` overload — every
|
|
29
|
+
* `registerXTool` in `mcp/tools/*.ts` uses exactly this call shape, and
|
|
30
|
+
* `mcp/tools/__tests__/tools.test.ts`'s `createMockServer` already narrows
|
|
31
|
+
* the same way to unit-test handlers without a transport. `as unknown as
|
|
32
|
+
* McpServer` is the one pre-approved narrowing for this file (task brief,
|
|
33
|
+
* "Capture-server dispatch") — `capture` is not, and never claims to be, a
|
|
34
|
+
* real `McpServer`; it implements only the single method every register
|
|
35
|
+
* function calls.
|
|
36
|
+
*/
|
|
37
|
+
function createCaptureServer() {
|
|
38
|
+
const tools = new Map();
|
|
39
|
+
const capture = {
|
|
40
|
+
tool(name, _description, schemaShape, handler) {
|
|
41
|
+
tools.set(name, { schemaShape, handler });
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
return { server: capture, tools };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Every tool name the MCP surface registers, sorted.
|
|
48
|
+
*
|
|
49
|
+
* Registration is pure: each `registerXTool(server, ctx)` passes `ctx` into the
|
|
50
|
+
* async handler's closure and never dereferences it while registering (verified
|
|
51
|
+
* across all 11). This enumerates without a database by handing registration a
|
|
52
|
+
* proxy that throws on ANY property access — so if a future tool starts reading
|
|
53
|
+
* `ctx` at registration time, this fails loudly instead of quietly returning a
|
|
54
|
+
* short list, and the drift guard in `docs-cmd.test.ts` keeps its meaning.
|
|
55
|
+
*/
|
|
56
|
+
export function listRegisteredToolNames() {
|
|
57
|
+
const { server, tools } = createCaptureServer();
|
|
58
|
+
const forbidden = new Proxy({}, {
|
|
59
|
+
get(_t, prop) {
|
|
60
|
+
throw new Error(`tool registration read ctx.${String(prop)}; listRegisteredToolNames assumes registration is pure`);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
registerAllTools(server, forbidden);
|
|
64
|
+
return [...tools.keys()].sort();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Dispatch a single CLI query through the exact registered MCP tool handler
|
|
68
|
+
* and return its serialized response text — byte-identical to what an MCP
|
|
69
|
+
* client would receive for the same tool + args.
|
|
70
|
+
*
|
|
71
|
+
* @throws QueryError when the resolved state dir has never been indexed, the
|
|
72
|
+
* JSON argument string fails to parse, `toolName` does not match a
|
|
73
|
+
* registered tool, or the parsed args fail that tool's own zod schema. Any
|
|
74
|
+
* other error (a tool-internal failure) propagates unchanged, same as it
|
|
75
|
+
* would over the MCP transport.
|
|
76
|
+
*/
|
|
77
|
+
export async function runQuery(toolName, jsonArgs, options = {}) {
|
|
78
|
+
const config = resolveConfig({ projectRoot: options.path, stateDirOverride: options.stateDir });
|
|
79
|
+
// openDatabase() creates graph.db on first open (better-sqlite3's default
|
|
80
|
+
// behavior for a missing file), so the never-indexed case must be caught
|
|
81
|
+
// BEFORE opening it — otherwise "no index" would silently succeed against a
|
|
82
|
+
// freshly-created empty database instead of failing fast. M6 (empty-state
|
|
83
|
+
// read semantics) is explicitly out of scope; this is only a guard against
|
|
84
|
+
// openDatabase's own create-on-open side effect.
|
|
85
|
+
const graphDbPath = join(config.resolved_state_dir, 'graph.db');
|
|
86
|
+
if (!existsSync(graphDbPath)) {
|
|
87
|
+
throw new QueryError(`no index found at ${config.resolved_state_dir}; run \`mast init\` / \`mast index\` first`);
|
|
88
|
+
}
|
|
89
|
+
let parsedArgs;
|
|
90
|
+
try {
|
|
91
|
+
parsedArgs = JSON.parse(jsonArgs);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
throw new QueryError(`malformed JSON argument: ${err instanceof Error ? err.message : String(err)}`);
|
|
95
|
+
}
|
|
96
|
+
const db = openDatabase(config.resolved_state_dir);
|
|
97
|
+
try {
|
|
98
|
+
const chunkStore = new SqliteChunkStore(db);
|
|
99
|
+
// Each CLI invocation is its own metrics session — unlike `serve()`,
|
|
100
|
+
// there is no long-lived MCP connection to key session-scoped telemetry
|
|
101
|
+
// (mast_efficiency's "session" scope) to.
|
|
102
|
+
const ctx = { db, chunkStore, config, sessionId: randomUUID() };
|
|
103
|
+
const { server, tools } = createCaptureServer();
|
|
104
|
+
registerAllTools(server, ctx);
|
|
105
|
+
const tool = tools.get(toolName);
|
|
106
|
+
if (tool === undefined) {
|
|
107
|
+
const available = [...tools.keys()].sort().join(', ');
|
|
108
|
+
throw new QueryError(`unknown tool "${toolName}"; available tools: ${available}`);
|
|
109
|
+
}
|
|
110
|
+
let validatedArgs;
|
|
111
|
+
try {
|
|
112
|
+
// The CLI arg string crosses a trust boundary (project CLAUDE.md §3.2);
|
|
113
|
+
// this reuses the exact per-tool zod shape the MCP layer validates
|
|
114
|
+
// with, so a CLI call can never see a tool accept input the MCP
|
|
115
|
+
// transport would have rejected, or vice versa.
|
|
116
|
+
validatedArgs = z.object(tool.schemaShape).parse(parsedArgs);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (err instanceof ZodError) {
|
|
120
|
+
throw new QueryError(`invalid arguments for "${toolName}": ${JSON.stringify(err.issues, null, 2)}`);
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
const result = await tool.handler(validatedArgs);
|
|
125
|
+
return result.content[0].text;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
await db.destroy();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export function registerQueryCommand(program) {
|
|
132
|
+
program
|
|
133
|
+
.command('query <tool> [json] [path]')
|
|
134
|
+
.description('Invoke an MCP read tool directly from the CLI — identical output to the MCP transport (MAST_SPEC.md §8)')
|
|
135
|
+
.option('--state-dir <dir>', 'State directory')
|
|
136
|
+
.option('--json', 'Emit the exact single-line MCP response text (machine use); default pretty-prints for humans')
|
|
137
|
+
.action(async (toolName, jsonArg, path, opts) => {
|
|
138
|
+
try {
|
|
139
|
+
const text = await runQuery(toolName, jsonArg ?? '{}', { stateDir: opts.stateDir, path });
|
|
140
|
+
if (opts.json) {
|
|
141
|
+
process.stdout.write(text + '\n');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
process.stdout.write(JSON.stringify(JSON.parse(text), null, 2) + '\n');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/cli/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* `mast search <query>` — the readable front door to `mast_search`.
|
|
4
|
+
*
|
|
5
|
+
* This is a presentation layer, not a second search implementation. It builds
|
|
6
|
+
* `mast_search`'s arguments and dispatches through `runQuery`, which routes to the
|
|
7
|
+
* registered MCP handler exactly as `mast query` does (D0). Ranking, JIT refresh,
|
|
8
|
+
* staleness flags, and `_stats` therefore cannot drift between the two surfaces —
|
|
9
|
+
* re-implementing any of it here is the drift D0 exists to prevent.
|
|
10
|
+
*
|
|
11
|
+
* `mast query mast_search '{...}'` remains the exact-MCP-parity surface for scripts;
|
|
12
|
+
* this one is for a human at a terminal, and prints text rather than JSON.
|
|
13
|
+
*/
|
|
14
|
+
export interface SearchFlags {
|
|
15
|
+
readonly limit?: string;
|
|
16
|
+
readonly type?: string;
|
|
17
|
+
readonly language?: string;
|
|
18
|
+
readonly exported?: boolean;
|
|
19
|
+
readonly file?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Maps CLI flags onto `mast_search`'s schema fields. Absent flags are omitted, not nulled. */
|
|
22
|
+
export declare function buildSearchArgs(query: string, flags: SearchFlags): string;
|
|
23
|
+
/**
|
|
24
|
+
* Renders the MCP response as text.
|
|
25
|
+
*
|
|
26
|
+
* Every confidence signal the response carries is echoed. An empty result that is
|
|
27
|
+
* empty *because* the index was busy or stale reads identically to "this symbol
|
|
28
|
+
* does not exist" unless the flag is shown — the S0 confusion this package's whole
|
|
29
|
+
* severity scale is built around.
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatSearchResults(responseText: string): string;
|
|
32
|
+
export declare function registerSearchCommand(program: Command): void;
|
|
33
|
+
//# sourceMappingURL=search-cmd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-cmd.d.ts","sourceRoot":"","sources":["../../src/cli/search-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAczE;AAqBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA8EhE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoB5D"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { runQuery, QueryError } from './query.js';
|
|
2
|
+
/** Maps CLI flags onto `mast_search`'s schema fields. Absent flags are omitted, not nulled. */
|
|
3
|
+
export function buildSearchArgs(query, flags) {
|
|
4
|
+
const args = { query };
|
|
5
|
+
if (flags.limit !== undefined) {
|
|
6
|
+
// Commander hands through whatever was typed; `Number('ten')` is NaN, which
|
|
7
|
+
// would serialise as `null` and be rejected far from the mistake.
|
|
8
|
+
const limit = Number(flags.limit);
|
|
9
|
+
if (!Number.isInteger(limit))
|
|
10
|
+
throw new QueryError(`--limit must be a whole number, got "${flags.limit}"`);
|
|
11
|
+
args['limit'] = limit;
|
|
12
|
+
}
|
|
13
|
+
if (flags.type !== undefined)
|
|
14
|
+
args['chunk_type'] = flags.type;
|
|
15
|
+
if (flags.language !== undefined)
|
|
16
|
+
args['language'] = flags.language;
|
|
17
|
+
if (flags.exported === true)
|
|
18
|
+
args['only_exported'] = true;
|
|
19
|
+
if (flags.file !== undefined)
|
|
20
|
+
args['file_pattern'] = flags.file;
|
|
21
|
+
return JSON.stringify(args);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Renders the MCP response as text.
|
|
25
|
+
*
|
|
26
|
+
* Every confidence signal the response carries is echoed. An empty result that is
|
|
27
|
+
* empty *because* the index was busy or stale reads identically to "this symbol
|
|
28
|
+
* does not exist" unless the flag is shown — the S0 confusion this package's whole
|
|
29
|
+
* severity scale is built around.
|
|
30
|
+
*/
|
|
31
|
+
export function formatSearchResults(responseText) {
|
|
32
|
+
const res = JSON.parse(responseText);
|
|
33
|
+
const lines = [];
|
|
34
|
+
// Top-level flags. `mast_search` emits none of these — it reserves
|
|
35
|
+
// `file_busy_returning_stale_cache` for the other tools (mcp/tools/search.ts:33) —
|
|
36
|
+
// so this branch is kept for shape compatibility and is NOT what protects search.
|
|
37
|
+
// The three signals search really emits are handled below.
|
|
38
|
+
const signals = Object.keys(res).filter((k) => /^(file_busy|stale|index_stale|truncated|potential_truncated)/.test(k) && res[k] === true);
|
|
39
|
+
if (signals.length > 0)
|
|
40
|
+
lines.push(`! ${signals.join(', ')} — this answer may be incomplete`, '');
|
|
41
|
+
const hits = res.results ?? [];
|
|
42
|
+
const staleHits = hits.filter((h) => h.stale === true).length;
|
|
43
|
+
if (staleHits > 0) {
|
|
44
|
+
lines.push(`! ${String(staleHits)} of ${String(hits.length)} results are from files that changed since indexing —`, ` the code shown below may be out of date. Run \`mast index\` to refresh.`, '');
|
|
45
|
+
}
|
|
46
|
+
if (hits.length === 0) {
|
|
47
|
+
if (res.index_empty === true) {
|
|
48
|
+
// The M6 case (ADR 008): an answer that is empty because the system has
|
|
49
|
+
// nothing to answer FROM must not read like a searched-and-not-found.
|
|
50
|
+
lines.push('nothing is indexed at this path — this is not evidence the symbol is absent.');
|
|
51
|
+
lines.push('run `mast index` first, or check `mast status` for the path being used.');
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
lines.push('no matches (mast indexes TypeScript, JavaScript, and Markdown only —');
|
|
55
|
+
lines.push('a symbol in any other language is invisible to it, not absent from the repo)');
|
|
56
|
+
}
|
|
57
|
+
for (const g of res.suggestions ?? []) {
|
|
58
|
+
lines.push(` did you mean: ${g.symbol ?? '?'}${g.file_path !== undefined ? ` (${g.file_path})` : ''}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
for (const h of hits) {
|
|
63
|
+
const where = `${h.file_path ?? '?'}:${String(h.start_line ?? '?')}`;
|
|
64
|
+
const what = [h.symbol_name, h.chunk_type].filter(Boolean).join(' ');
|
|
65
|
+
const marks = [h.is_exported === true ? '(exported)' : '', h.stale === true ? '[STALE]' : '']
|
|
66
|
+
.filter(Boolean).join(' ');
|
|
67
|
+
lines.push(`${where} ${what}${marks === '' ? '' : ` ${marks}`}`);
|
|
68
|
+
for (const l of (h.content ?? '').split('\n'))
|
|
69
|
+
lines.push(` ${l}`);
|
|
70
|
+
lines.push('');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const s = res._stats;
|
|
74
|
+
const got = s?.tokens_returned;
|
|
75
|
+
const whole = s?.tokens_full_file_upper_bound;
|
|
76
|
+
// Only meaningful when something matched: with no results the bound is 0 and the
|
|
77
|
+
// line renders "7 tokens returned vs 0 — 0% MORE", a comparison against nothing
|
|
78
|
+
// printed as a verdict.
|
|
79
|
+
if (hits.length > 0 && got !== undefined && whole !== undefined) {
|
|
80
|
+
// `tokens_full_file_upper_bound` is a bound on reading the referenced files
|
|
81
|
+
// whole. On small files a ranked result set can exceed it, and the saving is
|
|
82
|
+
// then negative — reported as such rather than clamped, because a tool whose
|
|
83
|
+
// efficiency number can only ever look good is not an efficiency number.
|
|
84
|
+
const saved = whole - got;
|
|
85
|
+
const pct = whole > 0 ? Math.round((saved / whole) * 100) : 0;
|
|
86
|
+
lines.push(saved >= 0
|
|
87
|
+
? `${String(got)} tokens returned vs ${String(whole)} to read the files whole — ${String(pct)}% saved`
|
|
88
|
+
: `${String(got)} tokens returned vs ${String(whole)} to read the files whole — ${String(-pct)}% MORE (the matched files are small enough to read outright)`);
|
|
89
|
+
}
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|
|
92
|
+
export function registerSearchCommand(program) {
|
|
93
|
+
program
|
|
94
|
+
.command('search <query> [path]')
|
|
95
|
+
.description('Search the index and print readable results (see `mast query mast_search` for raw MCP output)')
|
|
96
|
+
.option('--state-dir <dir>', 'State directory')
|
|
97
|
+
.option('-n, --limit <n>', 'Max results (1-50, default 10)')
|
|
98
|
+
.option('-t, --type <kind>', 'Restrict to a chunk kind: function, method, class_shell, interface, type, export, block, doc')
|
|
99
|
+
.option('-l, --language <lang>', 'Restrict to typescript, javascript, or markdown')
|
|
100
|
+
.option('-e, --exported', 'Only exported symbols')
|
|
101
|
+
.option('-f, --file <glob>', 'Restrict to files matching a glob')
|
|
102
|
+
.option('--json', 'Emit the raw MCP response instead of text')
|
|
103
|
+
.action(async (query, path, opts) => {
|
|
104
|
+
try {
|
|
105
|
+
const text = await runQuery('mast_search', buildSearchArgs(query, opts), { stateDir: opts.stateDir, path });
|
|
106
|
+
process.stdout.write((opts.json === true ? text : formatSearchResults(text)) + '\n');
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=search-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-cmd.js","sourceRoot":"","sources":["../../src/cli/search-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/cli/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmB3D"}
|