@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,128 @@
|
|
|
1
|
+
import fg from 'fast-glob';
|
|
2
|
+
import { statSync } from 'node:fs';
|
|
3
|
+
/**
|
|
4
|
+
* Convert a glob pattern to a RegExp.
|
|
5
|
+
* `**\/` — matches zero or more leading directories
|
|
6
|
+
* `**` — matches any sequence including `/`
|
|
7
|
+
* `*` — matches any sequence not containing `/`
|
|
8
|
+
* `?` — matches a single non-`/` character
|
|
9
|
+
*
|
|
10
|
+
* Lives in the walker (file-discovery domain) because both watch mode and the
|
|
11
|
+
* MCP tools' `file_pattern` filters need the same glob semantics — and because
|
|
12
|
+
* `walkProject` below hands the same patterns to fast-glob, so this function's
|
|
13
|
+
* output has to agree with fast-glob's for the same pattern. It is checked
|
|
14
|
+
* against fast-glob directly by `__tests__/glob-to-regex.test.ts`.
|
|
15
|
+
*
|
|
16
|
+
* Written as a single left-to-right scan rather than a chain of `.replace`
|
|
17
|
+
* calls. The chain was re-entrant: each rule rewrote the output of the ones
|
|
18
|
+
* before it, so `**` → `.*` left a `*` for the `*` rule to turn into
|
|
19
|
+
* `.[^/]*`, and `**\/` → `(.+/)?` left a `?` for the `?` rule to turn into
|
|
20
|
+
* `(.+/)[^/]`. Every shipped default in `exclude_patterns` (`store/config.ts`)
|
|
21
|
+
* contains one of those two sequences, so every one of them compiled to a
|
|
22
|
+
* regex that matched neither the directory it named nor its contents.
|
|
23
|
+
*/
|
|
24
|
+
export function globToRegex(pattern) {
|
|
25
|
+
let rx = '';
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < pattern.length) {
|
|
28
|
+
if (pattern.startsWith('**/', i)) {
|
|
29
|
+
// Optional, so `**\/x` matches a bare `x` at the root — fast-glob's
|
|
30
|
+
// reading of the same pattern.
|
|
31
|
+
rx += '(?:.+/)?';
|
|
32
|
+
i += 3;
|
|
33
|
+
}
|
|
34
|
+
else if (pattern.startsWith('**', i)) {
|
|
35
|
+
rx += '.*';
|
|
36
|
+
i += 2;
|
|
37
|
+
}
|
|
38
|
+
else if (pattern.startsWith('*', i)) {
|
|
39
|
+
rx += '[^/]*';
|
|
40
|
+
i += 1;
|
|
41
|
+
}
|
|
42
|
+
else if (pattern.startsWith('?', i)) {
|
|
43
|
+
rx += '[^/]';
|
|
44
|
+
i += 1;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
rx += pattern.charAt(i).replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
48
|
+
i += 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return new RegExp(`^${rx}$`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Walk the project and return all indexable files.
|
|
55
|
+
*
|
|
56
|
+
* Applies `file_extensions` allowlist and `exclude_patterns` denylist from
|
|
57
|
+
* config. Results are sorted lexicographically by `relativePath` (D1,
|
|
58
|
+
* IMPLEMENTATION_PLAN.md Stage 4): fast-glob returns filesystem order, which
|
|
59
|
+
* varies between identical runs — and because edge insertion order feeds the
|
|
60
|
+
* bare-name fallback in `insertEdges`' name resolution, two identical index
|
|
61
|
+
* runs produced edge sets differing by ±4/3,940 (§15.5). Sorting here makes
|
|
62
|
+
* every downstream consumer (index order, manifest, edge insertion)
|
|
63
|
+
* deterministic at the source. Callers still must not attach SEMANTIC meaning
|
|
64
|
+
* to the order — the guarantee is reproducibility, not priority.
|
|
65
|
+
*/
|
|
66
|
+
export async function walkProject(config) {
|
|
67
|
+
const patterns = config.file_extensions.map((ext) => `**/*${ext}`);
|
|
68
|
+
const paths = await fg(patterns, {
|
|
69
|
+
cwd: config.project_root,
|
|
70
|
+
ignore: config.exclude_patterns,
|
|
71
|
+
absolute: true,
|
|
72
|
+
onlyFiles: true,
|
|
73
|
+
followSymbolicLinks: false,
|
|
74
|
+
});
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const absPath of paths) {
|
|
77
|
+
try {
|
|
78
|
+
const stat = statSync(absPath);
|
|
79
|
+
const relativePath = absPath
|
|
80
|
+
.slice(config.project_root.length)
|
|
81
|
+
.replace(/^\//, '');
|
|
82
|
+
entries.push({
|
|
83
|
+
path: absPath,
|
|
84
|
+
relativePath,
|
|
85
|
+
mtime: stat.mtimeMs / 1_000,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// File disappeared between glob and stat — skip silently.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// D1: localeCompare is locale-sensitive and would make "deterministic"
|
|
93
|
+
// depend on the host locale — plain code-unit comparison does not.
|
|
94
|
+
entries.sort((a, b) => (a.relativePath < b.relativePath ? -1 : a.relativePath > b.relativePath ? 1 : 0));
|
|
95
|
+
return entries;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build a `{ relativePath → mtime }` snapshot map from `walkProject` results.
|
|
99
|
+
* Stored as `file_manifest.json` at the end of each index run.
|
|
100
|
+
*/
|
|
101
|
+
export function buildManifest(entries) {
|
|
102
|
+
const manifest = {};
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
manifest[entry.relativePath] = entry.mtime;
|
|
105
|
+
}
|
|
106
|
+
return manifest;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Compare the current filesystem scan against the previous manifest.
|
|
110
|
+
* Returns sets of paths for changed, new, and deleted files.
|
|
111
|
+
*/
|
|
112
|
+
export function diffManifest(current, previousManifest) {
|
|
113
|
+
const currentPaths = new Set(current.map((e) => e.relativePath));
|
|
114
|
+
const stale = [];
|
|
115
|
+
const added = [];
|
|
116
|
+
for (const entry of current) {
|
|
117
|
+
const prevMtime = previousManifest[entry.relativePath];
|
|
118
|
+
if (prevMtime === undefined) {
|
|
119
|
+
added.push(entry);
|
|
120
|
+
}
|
|
121
|
+
else if (entry.mtime > prevMtime) {
|
|
122
|
+
stale.push(entry);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const deleted = Object.keys(previousManifest).filter((p) => !currentPaths.has(p));
|
|
126
|
+
return { stale, added, deleted };
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=walker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walker.js","sourceRoot":"","sources":["../../src/indexer/walker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ResolvedConfig } from '../store/config.js';
|
|
2
|
+
export interface WatchSchedulerOptions {
|
|
3
|
+
/** Quiet period after the last event before a batch runs (~500ms in prod). */
|
|
4
|
+
readonly debounceMs: number;
|
|
5
|
+
/**
|
|
6
|
+
* Runs one reindex batch. `paths` is the coalesced set of changed files —
|
|
7
|
+
* informational only, since the incremental index run rescans the manifest.
|
|
8
|
+
* A rejection requeues the batch (see `maxConsecutiveFailures`).
|
|
9
|
+
*/
|
|
10
|
+
readonly onBatch: (paths: readonly string[]) => Promise<void>;
|
|
11
|
+
/** Warning sink — watcher problems must never crash the MCP server. */
|
|
12
|
+
readonly onWarn: (message: string) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Consecutive failed runs before the batch is dropped (with a warning, never
|
|
15
|
+
* silently). Bounded so a persistently held lock or a broken index cannot
|
|
16
|
+
* retry forever. Default 3, per the repo's three-attempts rule.
|
|
17
|
+
*/
|
|
18
|
+
readonly maxConsecutiveFailures?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Debounce + coalesce + single-flight orchestration for watch mode.
|
|
22
|
+
*
|
|
23
|
+
* - Events within the debounce window collapse into one pending set (a Set,
|
|
24
|
+
* so rapid saves of the same file are one entry).
|
|
25
|
+
* - Only one `onBatch` runs at a time; events arriving mid-run accumulate and
|
|
26
|
+
* trigger a follow-up run after the current one settles.
|
|
27
|
+
* - A failed run requeues its paths and warns; after `maxConsecutiveFailures`
|
|
28
|
+
* the batch is dropped with a warning. JIT staleness handling (§9.0) still
|
|
29
|
+
* guarantees read correctness, so dropping only delays ranking freshness.
|
|
30
|
+
*/
|
|
31
|
+
export declare class WatchScheduler {
|
|
32
|
+
private readonly options;
|
|
33
|
+
private readonly pending;
|
|
34
|
+
private timer;
|
|
35
|
+
private running;
|
|
36
|
+
private closed;
|
|
37
|
+
private consecutiveFailures;
|
|
38
|
+
private readonly maxConsecutiveFailures;
|
|
39
|
+
constructor(options: WatchSchedulerOptions);
|
|
40
|
+
/** Record a changed path and (re)arm the debounce timer. */
|
|
41
|
+
notify(path: string): void;
|
|
42
|
+
/** Cancel any pending run; the scheduler accepts no further notifications. */
|
|
43
|
+
close(): void;
|
|
44
|
+
private armTimer;
|
|
45
|
+
private run;
|
|
46
|
+
}
|
|
47
|
+
export interface WatchPathFilter {
|
|
48
|
+
/** Absolute project root. */
|
|
49
|
+
readonly projectRoot: string;
|
|
50
|
+
/** Absolute state directory — never watched (self-triggering loop hazard). */
|
|
51
|
+
readonly stateDir: string;
|
|
52
|
+
/** Watched file extensions (e.g. ['.ts', '.md']). */
|
|
53
|
+
readonly extensions: readonly string[];
|
|
54
|
+
/** Compiled exclude_patterns, matched against project-relative paths. */
|
|
55
|
+
readonly excludeRegexes: readonly RegExp[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* True when a filesystem event for `absPath` should feed the scheduler.
|
|
59
|
+
* Mirrors the walker's allowlist/denylist so watch mode indexes exactly the
|
|
60
|
+
* set of files a manual `mast index` run would.
|
|
61
|
+
*/
|
|
62
|
+
export declare function shouldWatchPath(filter: WatchPathFilter, absPath: string): boolean;
|
|
63
|
+
export interface WatchHandle {
|
|
64
|
+
close(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface StartWatchModeOptions {
|
|
67
|
+
readonly config: ResolvedConfig;
|
|
68
|
+
/** One incremental Phase 1 + embed run; supplied by the serve lifecycle. */
|
|
69
|
+
readonly runBatch: (paths: readonly string[]) => Promise<void>;
|
|
70
|
+
readonly onWarn: (message: string) => void;
|
|
71
|
+
/** Override for tests; production default 500ms. */
|
|
72
|
+
readonly debounceMs?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Start `--watch` mode: a chokidar watcher over the project feeding the
|
|
76
|
+
* debounced scheduler. May throw on construction (e.g. EMFILE) — callers must
|
|
77
|
+
* catch and degrade to serving without watch, never crash the server.
|
|
78
|
+
*/
|
|
79
|
+
export declare function startWatchMode(options: StartWatchModeOptions): WatchHandle;
|
|
80
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/indexer/watcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,MAAM,WAAW,qBAAqB;IACpC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IAQb,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAEnB,OAAO,EAAE,qBAAqB;IAI3D,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM1B,8EAA8E;IAC9E,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,QAAQ;YAQF,GAAG;CA4BlB;AAMD,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAMjF;AAMD,MAAM,WAAW,WAAW;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,oDAAoD;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,WAAW,CAgD1E"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { extname, relative, resolve } from 'node:path';
|
|
2
|
+
import { watch as chokidarWatch } from 'chokidar';
|
|
3
|
+
import { globToRegex } from './walker.js';
|
|
4
|
+
/**
|
|
5
|
+
* Debounce + coalesce + single-flight orchestration for watch mode.
|
|
6
|
+
*
|
|
7
|
+
* - Events within the debounce window collapse into one pending set (a Set,
|
|
8
|
+
* so rapid saves of the same file are one entry).
|
|
9
|
+
* - Only one `onBatch` runs at a time; events arriving mid-run accumulate and
|
|
10
|
+
* trigger a follow-up run after the current one settles.
|
|
11
|
+
* - A failed run requeues its paths and warns; after `maxConsecutiveFailures`
|
|
12
|
+
* the batch is dropped with a warning. JIT staleness handling (§9.0) still
|
|
13
|
+
* guarantees read correctness, so dropping only delays ranking freshness.
|
|
14
|
+
*/
|
|
15
|
+
export class WatchScheduler {
|
|
16
|
+
options;
|
|
17
|
+
pending = new Set();
|
|
18
|
+
timer = null;
|
|
19
|
+
running = false;
|
|
20
|
+
closed = false;
|
|
21
|
+
consecutiveFailures = 0;
|
|
22
|
+
maxConsecutiveFailures;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.options = options;
|
|
25
|
+
this.maxConsecutiveFailures = options.maxConsecutiveFailures ?? 3;
|
|
26
|
+
}
|
|
27
|
+
/** Record a changed path and (re)arm the debounce timer. */
|
|
28
|
+
notify(path) {
|
|
29
|
+
if (this.closed)
|
|
30
|
+
return;
|
|
31
|
+
this.pending.add(path);
|
|
32
|
+
this.armTimer();
|
|
33
|
+
}
|
|
34
|
+
/** Cancel any pending run; the scheduler accepts no further notifications. */
|
|
35
|
+
close() {
|
|
36
|
+
this.closed = true;
|
|
37
|
+
if (this.timer !== null) {
|
|
38
|
+
clearTimeout(this.timer);
|
|
39
|
+
this.timer = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
armTimer() {
|
|
43
|
+
if (this.timer !== null)
|
|
44
|
+
clearTimeout(this.timer);
|
|
45
|
+
this.timer = setTimeout(() => {
|
|
46
|
+
this.timer = null;
|
|
47
|
+
void this.run();
|
|
48
|
+
}, this.options.debounceMs);
|
|
49
|
+
}
|
|
50
|
+
async run() {
|
|
51
|
+
// `running` guard = single-flight: a timer firing mid-run is a no-op; the
|
|
52
|
+
// in-flight run re-arms in `finally` when anything is still pending.
|
|
53
|
+
if (this.running || this.closed || this.pending.size === 0)
|
|
54
|
+
return;
|
|
55
|
+
this.running = true;
|
|
56
|
+
const batch = [...this.pending];
|
|
57
|
+
this.pending.clear();
|
|
58
|
+
try {
|
|
59
|
+
await this.options.onBatch(batch);
|
|
60
|
+
this.consecutiveFailures = 0;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
this.consecutiveFailures++;
|
|
64
|
+
if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
|
|
65
|
+
this.options.onWarn(`[mast] watch: dropping batch of ${batch.length} path(s) after ${this.consecutiveFailures} consecutive failures: ${String(err)}`);
|
|
66
|
+
this.consecutiveFailures = 0;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.options.onWarn(`[mast] watch: batch failed, requeueing: ${String(err)}`);
|
|
70
|
+
for (const p of batch)
|
|
71
|
+
this.pending.add(p);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
this.running = false;
|
|
76
|
+
if (this.pending.size > 0 && !this.closed)
|
|
77
|
+
this.armTimer();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* True when a filesystem event for `absPath` should feed the scheduler.
|
|
83
|
+
* Mirrors the walker's allowlist/denylist so watch mode indexes exactly the
|
|
84
|
+
* set of files a manual `mast index` run would.
|
|
85
|
+
*/
|
|
86
|
+
export function shouldWatchPath(filter, absPath) {
|
|
87
|
+
if (absPath === filter.stateDir || absPath.startsWith(`${filter.stateDir}/`))
|
|
88
|
+
return false;
|
|
89
|
+
if (!filter.extensions.includes(extname(absPath)))
|
|
90
|
+
return false;
|
|
91
|
+
const rel = relative(filter.projectRoot, absPath);
|
|
92
|
+
if (rel.startsWith('..'))
|
|
93
|
+
return false;
|
|
94
|
+
return !filter.excludeRegexes.some((rx) => rx.test(rel));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Start `--watch` mode: a chokidar watcher over the project feeding the
|
|
98
|
+
* debounced scheduler. May throw on construction (e.g. EMFILE) — callers must
|
|
99
|
+
* catch and degrade to serving without watch, never crash the server.
|
|
100
|
+
*/
|
|
101
|
+
export function startWatchMode(options) {
|
|
102
|
+
const { config } = options;
|
|
103
|
+
const filter = {
|
|
104
|
+
projectRoot: config.resolved_project_root,
|
|
105
|
+
stateDir: config.resolved_state_dir,
|
|
106
|
+
extensions: config.file_extensions,
|
|
107
|
+
excludeRegexes: config.exclude_patterns.map(globToRegex),
|
|
108
|
+
};
|
|
109
|
+
const scheduler = new WatchScheduler({
|
|
110
|
+
debounceMs: options.debounceMs ?? 500,
|
|
111
|
+
onBatch: options.runBatch,
|
|
112
|
+
onWarn: options.onWarn,
|
|
113
|
+
});
|
|
114
|
+
const watcher = chokidarWatch(config.resolved_project_root, {
|
|
115
|
+
// The startup ladder already reindexed — only future changes matter.
|
|
116
|
+
ignoreInitial: true,
|
|
117
|
+
// Prune ignored subtrees at the directory level so chokidar never descends
|
|
118
|
+
// into node_modules/ or the state dir. Files are re-checked (with the
|
|
119
|
+
// extension allowlist) in shouldWatchPath; `rel + '/'` lets patterns like
|
|
120
|
+
// `**/node_modules/**` match the directory itself, not just its contents.
|
|
121
|
+
ignored: (path) => {
|
|
122
|
+
const abs = resolve(path);
|
|
123
|
+
if (abs === filter.stateDir || abs.startsWith(`${filter.stateDir}/`))
|
|
124
|
+
return true;
|
|
125
|
+
const rel = relative(filter.projectRoot, abs);
|
|
126
|
+
if (rel === '' || rel.startsWith('..'))
|
|
127
|
+
return false;
|
|
128
|
+
return filter.excludeRegexes.some((rx) => rx.test(rel) || rx.test(`${rel}/`));
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
// `unlink` feeds the same incremental run: deleted files are cleaned up by
|
|
132
|
+
// the index run's manifest diff (removeDeletedFiles), not reimplemented here.
|
|
133
|
+
watcher.on('add', (path) => { if (shouldWatchPath(filter, resolve(path)))
|
|
134
|
+
scheduler.notify(path); });
|
|
135
|
+
watcher.on('change', (path) => { if (shouldWatchPath(filter, resolve(path)))
|
|
136
|
+
scheduler.notify(path); });
|
|
137
|
+
watcher.on('unlink', (path) => { if (shouldWatchPath(filter, resolve(path)))
|
|
138
|
+
scheduler.notify(path); });
|
|
139
|
+
watcher.on('error', (err) => {
|
|
140
|
+
// Watcher errors (EMFILE, EPERM, …) degrade to no-watch; JIT staleness
|
|
141
|
+
// handling keeps reads correct, so serving continues.
|
|
142
|
+
options.onWarn(`[mast] watch: watcher error (continuing without event): ${String(err)}`);
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
close: async () => {
|
|
146
|
+
scheduler.close();
|
|
147
|
+
await watcher.close();
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/indexer/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Db } from '../graph/db.js';
|
|
2
|
+
import type { ChunkStore } from '../store/sqliteChunkStore.js';
|
|
3
|
+
import type { ResolvedConfig } from '../store/config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared state injected into every MCP tool handler at registration time.
|
|
6
|
+
*
|
|
7
|
+
* Stage 7.2 (IMPLEMENTATION_PLAN.md "Stage 7: Vector-store deletion") removed
|
|
8
|
+
* `searchMode` — its only consumer was `mast_status`'s search-mode status
|
|
9
|
+
* field, itself removed (decision 2). Nothing else ever read it.
|
|
10
|
+
*/
|
|
11
|
+
export interface AppContext {
|
|
12
|
+
readonly db: Db;
|
|
13
|
+
/**
|
|
14
|
+
* Chunk read surface (M1, eval/GITNEXUS_COMPARISON.md §15.1). Backed by
|
|
15
|
+
* `SqliteChunkStore` wrapping the same `db` connection in production
|
|
16
|
+
* (mcp/server.ts); tests may inject a fake.
|
|
17
|
+
*/
|
|
18
|
+
readonly chunkStore: ChunkStore;
|
|
19
|
+
readonly config: ResolvedConfig;
|
|
20
|
+
/** Stable identifier for this MCP server session (used in metrics). */
|
|
21
|
+
readonly sessionId: string;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/mcp/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAMzD;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/mcp/context.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { AppContext } from './context.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register every MAST MCP tool against `server`.
|
|
5
|
+
*
|
|
6
|
+
* D0 (IMPLEMENTATION_PLAN.md "Stage 4: Determinism, hygiene, and the
|
|
7
|
+
* measurement harness", "### D0 — CLI query surface"): this list previously
|
|
8
|
+
* existed only inlined in `mcp/server.ts` and duplicated (by hand) in
|
|
9
|
+
* `mcp/tools/__tests__/tools.test.ts`'s `createMockServer` setup. A third copy
|
|
10
|
+
* in the CLI query dispatcher (`cli/query.ts`) would let a new tool ship
|
|
11
|
+
* reachable over one transport but not the other — extracting one function
|
|
12
|
+
* both `serve()` and the CLI import closes that gap by construction.
|
|
13
|
+
*
|
|
14
|
+
* Registration order is preserved from the original `server.ts` inline list
|
|
15
|
+
* (search, project-skeleton, exports, signature, callers, dependencies,
|
|
16
|
+
* implementors, reindex, status, efficiency, rename-impact) — nothing here
|
|
17
|
+
* depends on order today, but changing it silently would be an unannounced
|
|
18
|
+
* behavior change during what is meant to be a behavior-preserving refactor.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerAllTools(server: McpServer, ctx: AppContext): void;
|
|
21
|
+
//# sourceMappingURL=register-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/register-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAa/C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAYzE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { registerSearchTool } from './tools/search.js';
|
|
2
|
+
import { registerProjectSkeletonTool } from './tools/project-skeleton.js';
|
|
3
|
+
import { registerExportsTool } from './tools/exports.js';
|
|
4
|
+
import { registerSignatureTool } from './tools/signature.js';
|
|
5
|
+
import { registerCallersTool } from './tools/callers.js';
|
|
6
|
+
import { registerDependenciesTool } from './tools/dependencies.js';
|
|
7
|
+
import { registerImplementorsTool } from './tools/implementors.js';
|
|
8
|
+
import { registerReindexTool } from './tools/reindex.js';
|
|
9
|
+
import { registerStatusTool } from './tools/status.js';
|
|
10
|
+
import { registerEfficiencyTool } from './tools/efficiency.js';
|
|
11
|
+
import { registerRenameImpactTool } from './tools/rename-impact.js';
|
|
12
|
+
/**
|
|
13
|
+
* Register every MAST MCP tool against `server`.
|
|
14
|
+
*
|
|
15
|
+
* D0 (IMPLEMENTATION_PLAN.md "Stage 4: Determinism, hygiene, and the
|
|
16
|
+
* measurement harness", "### D0 — CLI query surface"): this list previously
|
|
17
|
+
* existed only inlined in `mcp/server.ts` and duplicated (by hand) in
|
|
18
|
+
* `mcp/tools/__tests__/tools.test.ts`'s `createMockServer` setup. A third copy
|
|
19
|
+
* in the CLI query dispatcher (`cli/query.ts`) would let a new tool ship
|
|
20
|
+
* reachable over one transport but not the other — extracting one function
|
|
21
|
+
* both `serve()` and the CLI import closes that gap by construction.
|
|
22
|
+
*
|
|
23
|
+
* Registration order is preserved from the original `server.ts` inline list
|
|
24
|
+
* (search, project-skeleton, exports, signature, callers, dependencies,
|
|
25
|
+
* implementors, reindex, status, efficiency, rename-impact) — nothing here
|
|
26
|
+
* depends on order today, but changing it silently would be an unannounced
|
|
27
|
+
* behavior change during what is meant to be a behavior-preserving refactor.
|
|
28
|
+
*/
|
|
29
|
+
export function registerAllTools(server, ctx) {
|
|
30
|
+
registerSearchTool(server, ctx);
|
|
31
|
+
registerProjectSkeletonTool(server, ctx);
|
|
32
|
+
registerExportsTool(server, ctx);
|
|
33
|
+
registerSignatureTool(server, ctx);
|
|
34
|
+
registerCallersTool(server, ctx);
|
|
35
|
+
registerDependenciesTool(server, ctx);
|
|
36
|
+
registerImplementorsTool(server, ctx);
|
|
37
|
+
registerReindexTool(server, ctx);
|
|
38
|
+
registerStatusTool(server, ctx);
|
|
39
|
+
registerEfficiencyTool(server, ctx);
|
|
40
|
+
registerRenameImpactTool(server, ctx);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=register-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-tools.js","sourceRoot":"","sources":["../../src/mcp/register-tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAgB,mBAAmB,CAAC;AACjE,OAAO,EAAE,2BAA2B,EAAE,MAAO,6BAA6B,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAe,oBAAoB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAa,sBAAsB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAe,oBAAoB,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAU,yBAAyB,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAU,yBAAyB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAe,oBAAoB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAgB,mBAAmB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAY,uBAAuB,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAU,0BAA0B,CAAC;AAExE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,GAAe;IACjE,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,2BAA2B,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzC,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,sBAAsB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ResolvedConfig } from '../store/config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by {@link assertServableIndex} when `mast serve --no-startup-reindex`
|
|
4
|
+
* targets a state directory that has never completed an index run. Extends
|
|
5
|
+
* `Error` per project convention (never throw a plain string/object) —
|
|
6
|
+
* mirrors `cli/query.ts`'s `QueryError` precedent for a typed, CLI-surfaced
|
|
7
|
+
* failure mode.
|
|
8
|
+
*/
|
|
9
|
+
export declare class NeverIndexedError extends Error {
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Refuse to start serving when nothing can ever fill the index (M6 Part A).
|
|
13
|
+
*
|
|
14
|
+
* A blanket "refuse whenever the state dir is empty" is wrong: §7.4's
|
|
15
|
+
* startup ladder LEGITIMATELY serves from an empty state dir while Step 4's
|
|
16
|
+
* background reindex fills it in — that window is the designed SDD
|
|
17
|
+
* container flow and must keep working untouched. This function is a no-op
|
|
18
|
+
* whenever the startup reindex is enabled (the default) — see the early
|
|
19
|
+
* return below — so it can never gate or delay that path.
|
|
20
|
+
*
|
|
21
|
+
* The refusal fires only for the one combination where an empty index can
|
|
22
|
+
* NEVER be filled by this server process: `--no-startup-reindex` disables
|
|
23
|
+
* the one mechanism that would have filled it, permanently pinning every
|
|
24
|
+
* query at `{"results":[]}` — indistinguishable from "symbol doesn't exist",
|
|
25
|
+
* the exact ambiguity M6 (`eval/GITNEXUS_COMPARISON.md` §13.8 item 4) names.
|
|
26
|
+
* Part B (`mcp/tools/_helpers.ts`'s `isIndexEmpty`, consumed by every read
|
|
27
|
+
* tool) covers the legitimate empty window this function deliberately
|
|
28
|
+
* leaves alone.
|
|
29
|
+
*
|
|
30
|
+
* @throws NeverIndexedError naming the state dir and suggesting `mast init`
|
|
31
|
+
* / `mast index`, or dropping `--no-startup-reindex`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertServableIndex(config: ResolvedConfig, options: {
|
|
34
|
+
readonly noStartupReindex?: boolean;
|
|
35
|
+
}): void;
|
|
36
|
+
export interface ServeOptions {
|
|
37
|
+
readonly config: ResolvedConfig;
|
|
38
|
+
/** Skip the startup incremental reindex (not recommended). */
|
|
39
|
+
readonly noStartupReindex?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Watch source files and incrementally reindex on change (§11.4). Opt-in,
|
|
42
|
+
* for interactive (non-container) use — the SDD container relies on the
|
|
43
|
+
* startup ladder instead.
|
|
44
|
+
*/
|
|
45
|
+
readonly watch?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Run the MAST MCP server.
|
|
49
|
+
*
|
|
50
|
+
* Implements the startup ladder from §7.4 (Step 4's embed half was excised in
|
|
51
|
+
* Stage 7.1, IMPLEMENTATION_PLAN.md "Stage 7: Vector-store deletion"):
|
|
52
|
+
* Step 1 — bootstrap state directory (< 1s)
|
|
53
|
+
* Step 2 — schema version check + open DB (< 2s)
|
|
54
|
+
* Step 3 — open MCP transport, register tools, accept connections (< 1s)
|
|
55
|
+
* Step 4 — background incremental reindex (async)
|
|
56
|
+
*/
|
|
57
|
+
export declare function serve(options: ServeOptions): Promise<void>;
|
|
58
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAczD;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAiC/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,IAAI,CASN;AAMD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2FhE"}
|