@spikedpunch/mast 0.1.0 → 0.3.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/MAST_SPEC.md +196 -52
- package/README.md +86 -11
- package/assets/skill.md +49 -10
- package/dist/ast/types.d.ts +173 -1
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/cli/query.d.ts +40 -1
- package/dist/cli/query.d.ts.map +1 -1
- package/dist/cli/query.js +36 -2
- package/dist/cli/query.js.map +1 -1
- package/dist/cli/search-cmd.d.ts +6 -1
- package/dist/cli/search-cmd.d.ts.map +1 -1
- package/dist/cli/search-cmd.js +9 -2
- package/dist/cli/search-cmd.js.map +1 -1
- package/dist/cli/serve.d.ts +6 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +14 -4
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/status.d.ts +15 -0
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +32 -6
- package/dist/cli/status.js.map +1 -1
- package/dist/indexer/freshness.d.ts +10 -0
- package/dist/indexer/freshness.d.ts.map +1 -1
- package/dist/indexer/freshness.js +7 -1
- package/dist/indexer/freshness.js.map +1 -1
- package/dist/indexer/import-resolver.js +28 -16
- package/dist/indexer/import-resolver.js.map +1 -1
- package/dist/indexer/index.d.ts +38 -3
- package/dist/indexer/index.d.ts.map +1 -1
- package/dist/indexer/index.js +47 -4
- package/dist/indexer/index.js.map +1 -1
- package/dist/indexer/watcher.d.ts +16 -0
- package/dist/indexer/watcher.d.ts.map +1 -1
- package/dist/indexer/watcher.js +8 -0
- package/dist/indexer/watcher.js.map +1 -1
- package/dist/mcp/context.d.ts +16 -0
- package/dist/mcp/context.d.ts.map +1 -1
- package/dist/mcp/freshness-probe.d.ts +82 -0
- package/dist/mcp/freshness-probe.d.ts.map +1 -0
- package/dist/mcp/freshness-probe.js +111 -0
- package/dist/mcp/freshness-probe.js.map +1 -0
- package/dist/mcp/server.d.ts +46 -3
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +65 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/_helpers.d.ts +42 -0
- package/dist/mcp/tools/_helpers.d.ts.map +1 -1
- package/dist/mcp/tools/_helpers.js +29 -0
- package/dist/mcp/tools/_helpers.js.map +1 -1
- package/dist/mcp/tools/callers.d.ts.map +1 -1
- package/dist/mcp/tools/callers.js +6 -1
- package/dist/mcp/tools/callers.js.map +1 -1
- package/dist/mcp/tools/dependencies.d.ts.map +1 -1
- package/dist/mcp/tools/dependencies.js +2 -1
- package/dist/mcp/tools/dependencies.js.map +1 -1
- package/dist/mcp/tools/exports.d.ts.map +1 -1
- package/dist/mcp/tools/exports.js +11 -2
- package/dist/mcp/tools/exports.js.map +1 -1
- package/dist/mcp/tools/implementors.d.ts.map +1 -1
- package/dist/mcp/tools/implementors.js +10 -2
- package/dist/mcp/tools/implementors.js.map +1 -1
- package/dist/mcp/tools/project-skeleton.d.ts.map +1 -1
- package/dist/mcp/tools/project-skeleton.js +2 -1
- package/dist/mcp/tools/project-skeleton.js.map +1 -1
- package/dist/mcp/tools/reindex.d.ts.map +1 -1
- package/dist/mcp/tools/reindex.js +3 -0
- package/dist/mcp/tools/reindex.js.map +1 -1
- package/dist/mcp/tools/rename-impact.d.ts.map +1 -1
- package/dist/mcp/tools/rename-impact.js +4 -1
- package/dist/mcp/tools/rename-impact.js.map +1 -1
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +13 -2
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools/signature.d.ts.map +1 -1
- package/dist/mcp/tools/signature.js +15 -2
- package/dist/mcp/tools/signature.js.map +1 -1
- package/dist/mcp/tools/status.d.ts.map +1 -1
- package/dist/mcp/tools/status.js +8 -2
- package/dist/mcp/tools/status.js.map +1 -1
- package/dist/search/potential-matches.d.ts +15 -0
- package/dist/search/potential-matches.d.ts.map +1 -1
- package/dist/search/potential-matches.js +17 -2
- package/dist/search/potential-matches.js.map +1 -1
- package/package.json +4 -2
package/dist/indexer/watcher.js
CHANGED
|
@@ -136,6 +136,14 @@ export function startWatchMode(options) {
|
|
|
136
136
|
scheduler.notify(path); });
|
|
137
137
|
watcher.on('unlink', (path) => { if (shouldWatchPath(filter, resolve(path)))
|
|
138
138
|
scheduler.notify(path); });
|
|
139
|
+
// Fires after the initial scan completes; from here on, a created file
|
|
140
|
+
// produces an `add`. A construction failure (EMFILE, permissions) throws out
|
|
141
|
+
// of `chokidarWatch` above and never reaches this line, so `onReady` cannot
|
|
142
|
+
// announce a watcher that failed to start — the caller's catch handles that
|
|
143
|
+
// case. Handler registration order is NOT what provides that guarantee, and a
|
|
144
|
+
// runtime `error` after a successful scan does not un-fire `ready`: this
|
|
145
|
+
// signal means "the initial scan finished", not "the watcher is healthy".
|
|
146
|
+
watcher.on('ready', () => { options.onReady?.(); });
|
|
139
147
|
watcher.on('error', (err) => {
|
|
140
148
|
// Watcher errors (EMFILE, EPERM, …) degrade to no-watch; JIT staleness
|
|
141
149
|
// handling keeps reads correct, so serving continues.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/indexer/watcher.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/indexer/watcher.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/context.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Db } from '../graph/db.js';
|
|
2
2
|
import type { ChunkStore } from '../store/sqliteChunkStore.js';
|
|
3
3
|
import type { ResolvedConfig } from '../store/config.js';
|
|
4
|
+
import type { FreshnessProbe } from './freshness-probe.js';
|
|
4
5
|
/**
|
|
5
6
|
* Shared state injected into every MCP tool handler at registration time.
|
|
6
7
|
*
|
|
@@ -19,5 +20,20 @@ export interface AppContext {
|
|
|
19
20
|
readonly config: ResolvedConfig;
|
|
20
21
|
/** Stable identifier for this MCP server session (used in metrics). */
|
|
21
22
|
readonly sessionId: string;
|
|
23
|
+
/**
|
|
24
|
+
* Cached "is the index missing files?" signal, read by `mast_search` to warn
|
|
25
|
+
* that a result set may be incomplete.
|
|
26
|
+
*
|
|
27
|
+
* Optional, and absent is a real configuration rather than an oversight: the
|
|
28
|
+
* probe amortises one project walk across a TTL, which only pays off in a
|
|
29
|
+
* long-lived process. `mast serve` wires one (mcp/server.ts); the one-shot
|
|
30
|
+
* CLI query path (cli/query.ts) deliberately does not, because a single
|
|
31
|
+
* search there would pay the whole walk to answer a question it can answer
|
|
32
|
+
* outright with `mast search --reindex`.
|
|
33
|
+
*
|
|
34
|
+
* Absent, or reporting `null`, means *unknown* — surfaces render that as
|
|
35
|
+
* silence, never as zero.
|
|
36
|
+
*/
|
|
37
|
+
readonly freshness?: FreshnessProbe;
|
|
22
38
|
}
|
|
23
39
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +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;
|
|
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;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAM3D;;;;;;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;IAC3B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC;CACrC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cached, non-blocking answer to "is the index missing files?".
|
|
3
|
+
*
|
|
4
|
+
* `mast_search` needs this to warn a caller that a zero-hit result may mean
|
|
5
|
+
* "not indexed" rather than "not present" — the severity-zero failure this
|
|
6
|
+
* package is built around. What it cannot do is pay for it *at scale*, and the
|
|
7
|
+
* cost is worth stating with its corpus attached, because it inverts:
|
|
8
|
+
*
|
|
9
|
+
* - This repository, 213 files walked (medians, measured 2026-09-03):
|
|
10
|
+
* `fusedSearch` 15.3 ms, `measureFreshness` 7.6 ms. The walk is CHEAPER
|
|
11
|
+
* than the search. On a tree this size the probe buys nothing.
|
|
12
|
+
* - A 14k-file monorepo: `measureFreshness` ~183 ms (measured 2026-09-02).
|
|
13
|
+
*
|
|
14
|
+
* That is the whole argument, and it is about scaling, not about a ratio.
|
|
15
|
+
* `measureFreshness` is O(files in the tree) and a search is O(matches), so the
|
|
16
|
+
* walk overtakes the query as the tree grows and then keeps going. The probe
|
|
17
|
+
* exists for the trees where that has already happened.
|
|
18
|
+
*
|
|
19
|
+
* So the probe inverts it. `peekUnindexed` returns the last-known count
|
|
20
|
+
* synchronously and schedules a refresh behind the caller when the value has
|
|
21
|
+
* aged past the TTL. The first search of a session gets `null` — unknown, no
|
|
22
|
+
* warning — and every later one gets an answer that is at most `ttlMs` old.
|
|
23
|
+
* `server.ts` primes it at startup so that window is usually already closed.
|
|
24
|
+
*
|
|
25
|
+
* The cost is amortised, NOT eliminated, and it is worth being exact about
|
|
26
|
+
* which: `measureFreshness` -> `walkProject` globs asynchronously but then
|
|
27
|
+
* `statSync`s every hit in a synchronous loop, so a refresh occupies the event
|
|
28
|
+
* loop for part of its duration. A search that lands mid-refresh can be
|
|
29
|
+
* delayed by that much. One such window per `ttlMs`, rather than one walk per
|
|
30
|
+
* search, is the whole of the improvement — do not read "background" as
|
|
31
|
+
* "free".
|
|
32
|
+
*
|
|
33
|
+
* Deliberately NOT a reindex: no `structure.lock` is taken and nothing is
|
|
34
|
+
* written, so the probe cannot contend with a concurrent `runIndex` or with
|
|
35
|
+
* another reader (E7 measured 35-88.5% JIT failure under reader-vs-reader
|
|
36
|
+
* contention). It reads, it counts, it reports.
|
|
37
|
+
*/
|
|
38
|
+
import type { Db } from '../graph/db.js';
|
|
39
|
+
import type { ResolvedConfig } from '../store/config.js';
|
|
40
|
+
import { type IndexFreshness } from '../indexer/freshness.js';
|
|
41
|
+
/** How long a measurement stays authoritative before the next peek refreshes it. */
|
|
42
|
+
export declare const DEFAULT_FRESHNESS_TTL_MS = 30000;
|
|
43
|
+
export interface FreshnessProbe {
|
|
44
|
+
/**
|
|
45
|
+
* Files on disk that the index has never seen, as of the last completed
|
|
46
|
+
* measurement — or `null` when none has completed yet.
|
|
47
|
+
*
|
|
48
|
+
* Never blocks, never throws, never awaits. May schedule a background
|
|
49
|
+
* refresh as a side effect. `null` means *unknown*, which callers must
|
|
50
|
+
* render as silence rather than as zero.
|
|
51
|
+
*/
|
|
52
|
+
peekUnindexed(): number | null;
|
|
53
|
+
/**
|
|
54
|
+
* Discard the cached value and re-measure on the next peek.
|
|
55
|
+
*
|
|
56
|
+
* Called after this process reindexes: the previous count is not merely old,
|
|
57
|
+
* it is wrong in the direction that produces a false warning about files
|
|
58
|
+
* that were just indexed. A measurement already in flight is *superseded* —
|
|
59
|
+
* it is allowed to finish, and its result is discarded rather than cached,
|
|
60
|
+
* because it was reading the pre-reindex tree.
|
|
61
|
+
*/
|
|
62
|
+
invalidate(): void;
|
|
63
|
+
/** Start a measurement now if none is in flight. Used to prime at startup. */
|
|
64
|
+
refresh(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Resolves when the in-flight measurement settles — success or failure.
|
|
67
|
+
*
|
|
68
|
+
* Settling is not the same as caching a value: a measurement superseded by
|
|
69
|
+
* `invalidate()` settles with its result discarded, leaving `peekUnindexed`
|
|
70
|
+
* at `null` until the next peek starts a clean one.
|
|
71
|
+
*/
|
|
72
|
+
settled(): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
export interface FreshnessProbeOptions {
|
|
75
|
+
readonly ttlMs?: number;
|
|
76
|
+
/** Injected for tests (§4.4); production reads the real clock. */
|
|
77
|
+
readonly now?: () => number;
|
|
78
|
+
/** Injected for tests (§4.4); production walks the project. */
|
|
79
|
+
readonly measure?: (config: ResolvedConfig, db: Db) => Promise<IndexFreshness>;
|
|
80
|
+
}
|
|
81
|
+
export declare function createFreshnessProbe(config: ResolvedConfig, db: Db, options?: FreshnessProbeOptions): FreshnessProbe;
|
|
82
|
+
//# sourceMappingURL=freshness-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness-probe.d.ts","sourceRoot":"","sources":["../../src/mcp/freshness-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEhF,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,MAAM,WAAW,cAAc;IAC7B;;;;;;;OAOG;IACH,aAAa,IAAI,MAAM,GAAG,IAAI,CAAC;IAE/B;;;;;;;;OAQG;IACH,UAAU,IAAI,IAAI,CAAC;IAEnB,8EAA8E;IAC9E,OAAO,IAAI,IAAI,CAAC;IAEhB;;;;;;OAMG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CAChF;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,EACtB,EAAE,EAAE,EAAE,EACN,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAuEhB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cached, non-blocking answer to "is the index missing files?".
|
|
3
|
+
*
|
|
4
|
+
* `mast_search` needs this to warn a caller that a zero-hit result may mean
|
|
5
|
+
* "not indexed" rather than "not present" — the severity-zero failure this
|
|
6
|
+
* package is built around. What it cannot do is pay for it *at scale*, and the
|
|
7
|
+
* cost is worth stating with its corpus attached, because it inverts:
|
|
8
|
+
*
|
|
9
|
+
* - This repository, 213 files walked (medians, measured 2026-09-03):
|
|
10
|
+
* `fusedSearch` 15.3 ms, `measureFreshness` 7.6 ms. The walk is CHEAPER
|
|
11
|
+
* than the search. On a tree this size the probe buys nothing.
|
|
12
|
+
* - A 14k-file monorepo: `measureFreshness` ~183 ms (measured 2026-09-02).
|
|
13
|
+
*
|
|
14
|
+
* That is the whole argument, and it is about scaling, not about a ratio.
|
|
15
|
+
* `measureFreshness` is O(files in the tree) and a search is O(matches), so the
|
|
16
|
+
* walk overtakes the query as the tree grows and then keeps going. The probe
|
|
17
|
+
* exists for the trees where that has already happened.
|
|
18
|
+
*
|
|
19
|
+
* So the probe inverts it. `peekUnindexed` returns the last-known count
|
|
20
|
+
* synchronously and schedules a refresh behind the caller when the value has
|
|
21
|
+
* aged past the TTL. The first search of a session gets `null` — unknown, no
|
|
22
|
+
* warning — and every later one gets an answer that is at most `ttlMs` old.
|
|
23
|
+
* `server.ts` primes it at startup so that window is usually already closed.
|
|
24
|
+
*
|
|
25
|
+
* The cost is amortised, NOT eliminated, and it is worth being exact about
|
|
26
|
+
* which: `measureFreshness` -> `walkProject` globs asynchronously but then
|
|
27
|
+
* `statSync`s every hit in a synchronous loop, so a refresh occupies the event
|
|
28
|
+
* loop for part of its duration. A search that lands mid-refresh can be
|
|
29
|
+
* delayed by that much. One such window per `ttlMs`, rather than one walk per
|
|
30
|
+
* search, is the whole of the improvement — do not read "background" as
|
|
31
|
+
* "free".
|
|
32
|
+
*
|
|
33
|
+
* Deliberately NOT a reindex: no `structure.lock` is taken and nothing is
|
|
34
|
+
* written, so the probe cannot contend with a concurrent `runIndex` or with
|
|
35
|
+
* another reader (E7 measured 35-88.5% JIT failure under reader-vs-reader
|
|
36
|
+
* contention). It reads, it counts, it reports.
|
|
37
|
+
*/
|
|
38
|
+
import { measureFreshness } from '../indexer/freshness.js';
|
|
39
|
+
/** How long a measurement stays authoritative before the next peek refreshes it. */
|
|
40
|
+
export const DEFAULT_FRESHNESS_TTL_MS = 30_000;
|
|
41
|
+
export function createFreshnessProbe(config, db, options = {}) {
|
|
42
|
+
const ttlMs = options.ttlMs ?? DEFAULT_FRESHNESS_TTL_MS;
|
|
43
|
+
const now = options.now ?? Date.now;
|
|
44
|
+
const measure = options.measure ?? measureFreshness;
|
|
45
|
+
let unindexed = null;
|
|
46
|
+
/** `null` = never measured or invalidated; otherwise the clock at settlement. */
|
|
47
|
+
let settledAt = null;
|
|
48
|
+
let inFlight = null;
|
|
49
|
+
/**
|
|
50
|
+
* Bumped by `invalidate()`. A measurement that started under an older
|
|
51
|
+
* generation was reading a tree this process has since reindexed, so its
|
|
52
|
+
* count is not merely old — it is wrong in the exact direction that warns
|
|
53
|
+
* about files that now ARE indexed. Gating on the generation is what makes
|
|
54
|
+
* `invalidate()` mean "discard", rather than "discard unless a walk happens
|
|
55
|
+
* to be in flight, in which case adopt its stale answer and stamp it fresh
|
|
56
|
+
* for a full TTL".
|
|
57
|
+
*/
|
|
58
|
+
let generation = 0;
|
|
59
|
+
function refresh() {
|
|
60
|
+
// Still coalesced across generations: a superseded walk is left to finish
|
|
61
|
+
// and its result dropped, rather than disowned and replaced immediately.
|
|
62
|
+
// Disowning would let one walk per `invalidate()` run concurrently, and
|
|
63
|
+
// `measureFreshness` holds the event loop in a synchronous `statSync` loop
|
|
64
|
+
// (see the header) — under a watch-driven edit loop that is worse than the
|
|
65
|
+
// laziness. The cache is left `null` (unknown -> silence, never a wrong
|
|
66
|
+
// warning) and the next peek starts a clean measurement.
|
|
67
|
+
if (inFlight !== null)
|
|
68
|
+
return;
|
|
69
|
+
const startedAt = generation;
|
|
70
|
+
inFlight = measure(config, db)
|
|
71
|
+
.then((result) => {
|
|
72
|
+
if (startedAt !== generation)
|
|
73
|
+
return;
|
|
74
|
+
unindexed = result.unindexed;
|
|
75
|
+
})
|
|
76
|
+
.catch(() => {
|
|
77
|
+
// Swallowed by contract: a failed walk (a directory removed mid-walk, a
|
|
78
|
+
// permission error) must not reject into a search handler or replace a
|
|
79
|
+
// good answer with a wrong one. The last value stands and the stamp
|
|
80
|
+
// below backs the retry off by a full TTL rather than re-walking on
|
|
81
|
+
// every peek.
|
|
82
|
+
})
|
|
83
|
+
.finally(() => {
|
|
84
|
+
inFlight = null;
|
|
85
|
+
// A superseded walk must NOT stamp the cache fresh. Leaving `settledAt`
|
|
86
|
+
// null keeps the value expired, so the next peek re-measures instead of
|
|
87
|
+
// serving `null` for a full TTL.
|
|
88
|
+
if (startedAt !== generation)
|
|
89
|
+
return;
|
|
90
|
+
settledAt = now();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
peekUnindexed() {
|
|
95
|
+
const isExpired = settledAt === null || now() - settledAt >= ttlMs;
|
|
96
|
+
if (isExpired)
|
|
97
|
+
refresh();
|
|
98
|
+
return unindexed;
|
|
99
|
+
},
|
|
100
|
+
invalidate() {
|
|
101
|
+
unindexed = null;
|
|
102
|
+
settledAt = null;
|
|
103
|
+
generation += 1;
|
|
104
|
+
},
|
|
105
|
+
refresh,
|
|
106
|
+
async settled() {
|
|
107
|
+
await inFlight;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=freshness-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness-probe.js","sourceRoot":"","sources":["../../src/mcp/freshness-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAIH,OAAO,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAEhF,oFAAoF;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AA6C/C,MAAM,UAAU,oBAAoB,CAClC,MAAsB,EACtB,EAAM,EACN,UAAiC,EAAE;IAEnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,wBAAwB,CAAC;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IAEpD,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,iFAAiF;IACjF,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,QAAQ,GAAyB,IAAI,CAAC;IAC1C;;;;;;;;OAQG;IACH,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,SAAS,OAAO;QACd,0EAA0E;QAC1E,yEAAyE;QACzE,wEAAwE;QACxE,2EAA2E;QAC3E,2EAA2E;QAC3E,wEAAwE;QACxE,yDAAyD;QACzD,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO;QAC9B,MAAM,SAAS,GAAG,UAAU,CAAC;QAC7B,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;aAC3B,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACf,IAAI,SAAS,KAAK,UAAU;gBAAE,OAAO;YACrC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC/B,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,wEAAwE;YACxE,uEAAuE;YACvE,oEAAoE;YACpE,oEAAoE;YACpE,cAAc;QAChB,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,QAAQ,GAAG,IAAI,CAAC;YAChB,wEAAwE;YACxE,wEAAwE;YACxE,iCAAiC;YACjC,IAAI,SAAS,KAAK,UAAU;gBAAE,OAAO;YACrC,SAAS,GAAG,GAAG,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACL,aAAa;YACX,MAAM,SAAS,GAAG,SAAS,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,SAAS,IAAI,KAAK,CAAC;YACnE,IAAI,SAAS;gBAAE,OAAO,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,UAAU;YACR,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,IAAI,CAAC,CAAC;QAClB,CAAC;QAED,OAAO;QAEP,KAAK,CAAC,OAAO;YACX,MAAM,QAAQ,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResolvedConfig } from '../store/config.js';
|
|
2
|
+
import { type FreshnessProbe } from './freshness-probe.js';
|
|
2
3
|
/**
|
|
3
4
|
* Thrown by {@link assertServableIndex} when `mast serve --no-startup-reindex`
|
|
4
5
|
* targets a state directory that has never completed an index run. Extends
|
|
@@ -33,14 +34,56 @@ export declare class NeverIndexedError extends Error {
|
|
|
33
34
|
export declare function assertServableIndex(config: ResolvedConfig, options: {
|
|
34
35
|
readonly noStartupReindex?: boolean;
|
|
35
36
|
}): void;
|
|
37
|
+
/**
|
|
38
|
+
* Run an incremental/full index pass and then correct the cached freshness
|
|
39
|
+
* count, which that pass has just made wrong.
|
|
40
|
+
*
|
|
41
|
+
* Extracted from `serve` because the ordering here is the whole contract and it
|
|
42
|
+
* was previously expressed three times in three closures, one of which got it
|
|
43
|
+
* wrong (D060): the invalidate must be in a `finally`. A reindex that FAILS is
|
|
44
|
+
* the case where the cached count is most likely to be stale and most costly to
|
|
45
|
+
* keep — the watcher only fired because files on disk changed, and `runIndex`
|
|
46
|
+
* losing `structure.lock` to a concurrent writer is an ordinary outcome, not an
|
|
47
|
+
* exceptional one. Invalidating only on success leaves `mast_search` reporting
|
|
48
|
+
* a pre-change count as authoritative for a full TTL, and after
|
|
49
|
+
* `maxConsecutiveFailures` the watcher drops the batch, so nothing retries.
|
|
50
|
+
*
|
|
51
|
+
* Errors propagate deliberately. `WatchScheduler` needs the rejection to
|
|
52
|
+
* requeue the batch, and the startup path wants to warn on its own terms; a
|
|
53
|
+
* function that both corrects the cache and swallows the failure would give
|
|
54
|
+
* neither caller what it needs.
|
|
55
|
+
*
|
|
56
|
+
* @param prime `true` measures immediately (startup, so the first search of the
|
|
57
|
+
* session has a real answer instead of `null`); `false` leaves the next
|
|
58
|
+
* `peekUnindexed` to schedule it, which is right for a watch batch that may
|
|
59
|
+
* be one of many in a burst.
|
|
60
|
+
*/
|
|
61
|
+
export declare function reindexAndRemeasure(config: ResolvedConfig, freshness: FreshnessProbe, options: {
|
|
62
|
+
readonly incremental: boolean;
|
|
63
|
+
readonly prime?: boolean;
|
|
64
|
+
/** §4.4 DI seam: tests drive the failure path without standing up a writer. */
|
|
65
|
+
readonly runIndexFn?: (config: ResolvedConfig, opts: {
|
|
66
|
+
incremental: boolean;
|
|
67
|
+
}) => Promise<unknown>;
|
|
68
|
+
}): Promise<void>;
|
|
36
69
|
export interface ServeOptions {
|
|
37
70
|
readonly config: ResolvedConfig;
|
|
38
71
|
/** Skip the startup incremental reindex (not recommended). */
|
|
39
72
|
readonly noStartupReindex?: boolean;
|
|
40
73
|
/**
|
|
41
|
-
* Watch source files and incrementally reindex on change (§11.4).
|
|
42
|
-
*
|
|
43
|
-
*
|
|
74
|
+
* Watch source files and incrementally reindex on change (§11.4).
|
|
75
|
+
*
|
|
76
|
+
* **Defaults to on** (`undefined` means watch). It was opt-in until
|
|
77
|
+
* 2026-09-03, on the theory that the startup ladder plus JIT staleness kept
|
|
78
|
+
* reads correct. Measurement said otherwise: JIT only re-parses files the
|
|
79
|
+
* index already knows, so a file CREATED during a session is invisible to
|
|
80
|
+
* every read tool until something reindexes — and nothing does. A watcher is
|
|
81
|
+
* the only mechanism here that closes that window without a per-query cost.
|
|
82
|
+
* Watcher construction failures degrade to serving without it, so the
|
|
83
|
+
* default cannot make the server fail to start.
|
|
84
|
+
*
|
|
85
|
+
* Set `false` (CLI `--no-watch`) for container or batch use, where the tree
|
|
86
|
+
* does not change under the server and the fd cost is not worth paying.
|
|
44
87
|
*/
|
|
45
88
|
readonly watch?: boolean;
|
|
46
89
|
}
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQjF;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE;IACP,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACpG,GACA,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmHhE"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { SqliteChunkStore } from '../store/sqliteChunkStore.js';
|
|
|
8
8
|
import { runIndex, loadIndexMeta } from '../indexer/index.js';
|
|
9
9
|
import { startWatchMode } from '../indexer/watcher.js';
|
|
10
10
|
import { bootstrapState } from './startup.js';
|
|
11
|
+
import { createFreshnessProbe } from './freshness-probe.js';
|
|
11
12
|
import { registerAllTools } from './register-tools.js';
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// M6 Part A (eval/GITNEXUS_COMPARISON.md §13.8 item 4): refuse to serve only
|
|
@@ -85,6 +86,44 @@ export function assertServableIndex(config, options) {
|
|
|
85
86
|
'query would return {"results":[]} forever, indistinguishable from "symbol doesn\'t exist". ' +
|
|
86
87
|
'Run `mast init` or `mast index` against this state directory first, or drop --no-startup-reindex.');
|
|
87
88
|
}
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Startup ladder (§7.4)
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
/**
|
|
93
|
+
* Run an incremental/full index pass and then correct the cached freshness
|
|
94
|
+
* count, which that pass has just made wrong.
|
|
95
|
+
*
|
|
96
|
+
* Extracted from `serve` because the ordering here is the whole contract and it
|
|
97
|
+
* was previously expressed three times in three closures, one of which got it
|
|
98
|
+
* wrong (D060): the invalidate must be in a `finally`. A reindex that FAILS is
|
|
99
|
+
* the case where the cached count is most likely to be stale and most costly to
|
|
100
|
+
* keep — the watcher only fired because files on disk changed, and `runIndex`
|
|
101
|
+
* losing `structure.lock` to a concurrent writer is an ordinary outcome, not an
|
|
102
|
+
* exceptional one. Invalidating only on success leaves `mast_search` reporting
|
|
103
|
+
* a pre-change count as authoritative for a full TTL, and after
|
|
104
|
+
* `maxConsecutiveFailures` the watcher drops the batch, so nothing retries.
|
|
105
|
+
*
|
|
106
|
+
* Errors propagate deliberately. `WatchScheduler` needs the rejection to
|
|
107
|
+
* requeue the batch, and the startup path wants to warn on its own terms; a
|
|
108
|
+
* function that both corrects the cache and swallows the failure would give
|
|
109
|
+
* neither caller what it needs.
|
|
110
|
+
*
|
|
111
|
+
* @param prime `true` measures immediately (startup, so the first search of the
|
|
112
|
+
* session has a real answer instead of `null`); `false` leaves the next
|
|
113
|
+
* `peekUnindexed` to schedule it, which is right for a watch batch that may
|
|
114
|
+
* be one of many in a burst.
|
|
115
|
+
*/
|
|
116
|
+
export async function reindexAndRemeasure(config, freshness, options) {
|
|
117
|
+
const run = options.runIndexFn ?? runIndex;
|
|
118
|
+
try {
|
|
119
|
+
await run(config, { incremental: options.incremental });
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
freshness.invalidate();
|
|
123
|
+
if (options.prime === true)
|
|
124
|
+
freshness.refresh();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
88
127
|
/**
|
|
89
128
|
* Run the MAST MCP server.
|
|
90
129
|
*
|
|
@@ -112,11 +151,17 @@ export async function serve(options) {
|
|
|
112
151
|
// tool below already uses.
|
|
113
152
|
const chunkStore = new SqliteChunkStore(db);
|
|
114
153
|
// ── Step 3: open MCP transport and register tools ─────────────────────────
|
|
154
|
+
// Freshness signal for `mast_search`'s `unindexed_files` warning. Created
|
|
155
|
+
// before the tools are registered because handlers capture `ctx` at
|
|
156
|
+
// registration; primed below so the first search of a session already has a
|
|
157
|
+
// real answer rather than `null`.
|
|
158
|
+
const freshness = createFreshnessProbe(config, db);
|
|
115
159
|
const ctx = {
|
|
116
160
|
db,
|
|
117
161
|
chunkStore,
|
|
118
162
|
config,
|
|
119
163
|
sessionId: randomUUID(),
|
|
164
|
+
freshness,
|
|
120
165
|
};
|
|
121
166
|
const server = new McpServer({ name: 'mast', version: '0.1.0' });
|
|
122
167
|
registerAllTools(server, ctx);
|
|
@@ -128,7 +173,7 @@ export async function serve(options) {
|
|
|
128
173
|
// mast_reindex). JIT staleness handling (§9.0) already guarantees read
|
|
129
174
|
// correctness; watch mode only keeps the FTS/graph index fresh between reads.
|
|
130
175
|
const startWatchIfRequested = () => {
|
|
131
|
-
if (options.watch
|
|
176
|
+
if (options.watch === false)
|
|
132
177
|
return;
|
|
133
178
|
let watchHandle = null;
|
|
134
179
|
try {
|
|
@@ -136,9 +181,19 @@ export async function serve(options) {
|
|
|
136
181
|
config,
|
|
137
182
|
runBatch: async (paths) => {
|
|
138
183
|
process.stderr.write(`[mast] watch: reindexing after ${paths.length} change(s)\n`);
|
|
139
|
-
|
|
184
|
+
// Not primed: a burst of saves produces a burst of batches, and the
|
|
185
|
+
// next `peekUnindexed` will schedule one measurement for all of them.
|
|
186
|
+
await reindexAndRemeasure(config, freshness, { incremental: true });
|
|
140
187
|
},
|
|
141
188
|
onWarn: (message) => process.stderr.write(`${message}\n`),
|
|
189
|
+
// The success path now announces itself, because the failure path
|
|
190
|
+
// always did: `startWatchMode`'s EMFILE degradation writes a warning,
|
|
191
|
+
// so before this line silence meant "watching", "not watching yet" and
|
|
192
|
+
// "watcher failed to start" alike, and an operator could not tell which
|
|
193
|
+
// (D061). Emitted once, after chokidar's initial scan — files created
|
|
194
|
+
// before it are treated as pre-existing and fire no event, so this is
|
|
195
|
+
// the point from which a change is guaranteed to be seen.
|
|
196
|
+
onReady: () => process.stderr.write('[mast] watch: watching for changes\n'),
|
|
142
197
|
});
|
|
143
198
|
}
|
|
144
199
|
catch (err) {
|
|
@@ -161,12 +216,19 @@ export async function serve(options) {
|
|
|
161
216
|
};
|
|
162
217
|
// ── Step 4: background reindex ────────────────────────────────────────────
|
|
163
218
|
if (options.noStartupReindex) {
|
|
219
|
+
// No reindex is coming, so measure the drift the user has chosen to keep.
|
|
220
|
+
freshness.refresh();
|
|
164
221
|
startWatchIfRequested();
|
|
165
222
|
return;
|
|
166
223
|
}
|
|
167
224
|
void (async () => {
|
|
168
225
|
try {
|
|
169
|
-
|
|
226
|
+
// Measured AFTER the reindex, in the same background task: a probe run
|
|
227
|
+
// before it would race the indexer and cache a count that the very next
|
|
228
|
+
// moment invalidates. On the failure path the correction is the more
|
|
229
|
+
// important half — the index is behind and nothing else will say so —
|
|
230
|
+
// which is why it lives in `reindexAndRemeasure`'s `finally`.
|
|
231
|
+
await reindexAndRemeasure(config, freshness, { incremental: !needsFullReindex, prime: true });
|
|
170
232
|
}
|
|
171
233
|
catch (err) {
|
|
172
234
|
process.stderr.write(`[mast] startup indexing failed: ${String(err)}\n`);
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAmB,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAoB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,8EAA8E;AAC9E,6EAA6E;AAC7E,yEAAyE;AACzE,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAE/C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,cAAc,CAAC,MAAsB;IAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IAChE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,EAAE,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC;AACjF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAsB,EACtB,OAAgD;IAEhD,IAAI,OAAO,CAAC,gBAAgB,KAAK,IAAI;QAAE,OAAO;IAC9C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;QAAE,OAAO;IACpC,MAAM,IAAI,iBAAiB,CACzB,gCAAgC,MAAM,CAAC,kBAAkB,gCAAgC;QACzF,wFAAwF;QACxF,6FAA6F;QAC7F,mGAAmG,CACpG,CAAC;AACJ,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import type { Db } from '../../graph/db.js';
|
|
|
2
2
|
import type { ResolvedConfig } from '../../store/config.js';
|
|
3
3
|
import type { AppContext } from '../context.js';
|
|
4
4
|
import { type StalenessCheckResult } from '../staleness.js';
|
|
5
|
+
/** @see {@link DEFAULT_RESULT_LIMIT} — re-exported so every capped tool reads one constant (D043). */
|
|
6
|
+
export { DEFAULT_RESULT_LIMIT } from '../../search/potential-matches.js';
|
|
5
7
|
export { collectPotentialMatches, collectPotentialMatchCandidates, type ChunkByIdSource, type PotentialMatchCandidate, type PotentialMatchCandidatesResult, type PotentialMatchesResult, } from '../../search/potential-matches.js';
|
|
6
8
|
/**
|
|
7
9
|
* True when the `chunks` table has zero rows — i.e. nothing is indexed yet,
|
|
@@ -14,6 +16,46 @@ export { collectPotentialMatches, collectPotentialMatchCandidates, type ChunkByI
|
|
|
14
16
|
* pays nothing extra per call.
|
|
15
17
|
*/
|
|
16
18
|
export declare function isIndexEmpty(ctx: AppContext): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* How exhaustive a tool's answer claims to be. This is what decides whether a
|
|
21
|
+
* partial index is worth mentioning alongside a *non-empty* result, and the two
|
|
22
|
+
* cases are genuinely different claims rather than a style choice.
|
|
23
|
+
*
|
|
24
|
+
* - `exhaustive-set` — the answer is "these are the ones there are"
|
|
25
|
+
* (`mast_callers`, `mast_rename_impact`, `mast_implementors`, `mast_search`,
|
|
26
|
+
* `mast_project_skeleton`). A **non-empty** answer is the dangerous one here:
|
|
27
|
+
* "3 verified callers" computed over a corpus missing forty files is what
|
|
28
|
+
* drives a delete, and it reads exactly like a complete answer.
|
|
29
|
+
* - `named-lookup` — the answer is about one thing the caller named
|
|
30
|
+
* (`mast_signature`, `mast_exports`, `mast_dependencies`). If it was found,
|
|
31
|
+
* the answer is correct however much else is unindexed; only *not finding* it
|
|
32
|
+
* is ambiguous between "absent" and "never indexed".
|
|
33
|
+
*/
|
|
34
|
+
export type CompletenessClaim = 'exhaustive-set' | 'named-lookup';
|
|
35
|
+
/**
|
|
36
|
+
* The partial-index warning (D054), for every tool that returns a primary
|
|
37
|
+
* result set — not just `mast_search`, which had it first only because that is
|
|
38
|
+
* where it was written.
|
|
39
|
+
*
|
|
40
|
+
* The severity zero: a caller reads an empty or thin answer, concludes "it
|
|
41
|
+
* isn't there", and edits or deletes code that is in fact referenced from a
|
|
42
|
+
* file the index never saw. `index_empty` covers the all-or-nothing case; this
|
|
43
|
+
* covers the far commoner partial one, where the index is populated and merely
|
|
44
|
+
* behind.
|
|
45
|
+
*
|
|
46
|
+
* Free to call. `peekUnindexed` reads a TTL-cached count synchronously and
|
|
47
|
+
* never awaits a walk (see `mcp/freshness-probe.ts`), so unlike `isIndexEmpty`
|
|
48
|
+
* this costs nothing on a populated response and needs no empty-path guard for
|
|
49
|
+
* performance — only for meaning.
|
|
50
|
+
*
|
|
51
|
+
* `null` from the probe means *no measurement has landed yet*, which is
|
|
52
|
+
* unknown, not zero. Both render as an omitted field, but they are kept
|
|
53
|
+
* distinct here because the next reader to add a branch would otherwise inherit
|
|
54
|
+
* "unknown means clean" — the exact conflation this signal exists to prevent.
|
|
55
|
+
*/
|
|
56
|
+
export declare function unindexedFilesField(ctx: AppContext, claim: CompletenessClaim, isEmpty: boolean): {
|
|
57
|
+
readonly unindexed_files?: number;
|
|
58
|
+
};
|
|
17
59
|
/** Extract the first JSDoc comment block from chunk content, if present. */
|
|
18
60
|
export declare function extractDoc(content: string): string | null;
|
|
19
61
|
export { globToRegex } from '../../indexer/walker.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_helpers.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"_helpers.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,sGAAsG;AACtG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AASzE,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAC/B,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,GAC5B,MAAM,mCAAmC,CAAC;AAM3C;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpE;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,OAAO,GACf;IAAE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAIvC;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAID,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,CAAC,CAQ/B"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { checkAndRefreshIfStale } from '../staleness.js';
|
|
2
|
+
/** @see {@link DEFAULT_RESULT_LIMIT} — re-exported so every capped tool reads one constant (D043). */
|
|
3
|
+
export { DEFAULT_RESULT_LIMIT } from '../../search/potential-matches.js';
|
|
2
4
|
// collectPotentialMatches/collectPotentialMatchCandidates moved to
|
|
3
5
|
// search/potential-matches.ts (Stage 1.2) so the `mast index --checker` pass
|
|
4
6
|
// (graph/checker-resolver.ts) can share the exact same "what counts as a
|
|
@@ -23,6 +25,33 @@ export { collectPotentialMatches, collectPotentialMatchCandidates, } from '../..
|
|
|
23
25
|
export async function isIndexEmpty(ctx) {
|
|
24
26
|
return (await ctx.chunkStore.chunkCount()) === 0;
|
|
25
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* The partial-index warning (D054), for every tool that returns a primary
|
|
30
|
+
* result set — not just `mast_search`, which had it first only because that is
|
|
31
|
+
* where it was written.
|
|
32
|
+
*
|
|
33
|
+
* The severity zero: a caller reads an empty or thin answer, concludes "it
|
|
34
|
+
* isn't there", and edits or deletes code that is in fact referenced from a
|
|
35
|
+
* file the index never saw. `index_empty` covers the all-or-nothing case; this
|
|
36
|
+
* covers the far commoner partial one, where the index is populated and merely
|
|
37
|
+
* behind.
|
|
38
|
+
*
|
|
39
|
+
* Free to call. `peekUnindexed` reads a TTL-cached count synchronously and
|
|
40
|
+
* never awaits a walk (see `mcp/freshness-probe.ts`), so unlike `isIndexEmpty`
|
|
41
|
+
* this costs nothing on a populated response and needs no empty-path guard for
|
|
42
|
+
* performance — only for meaning.
|
|
43
|
+
*
|
|
44
|
+
* `null` from the probe means *no measurement has landed yet*, which is
|
|
45
|
+
* unknown, not zero. Both render as an omitted field, but they are kept
|
|
46
|
+
* distinct here because the next reader to add a branch would otherwise inherit
|
|
47
|
+
* "unknown means clean" — the exact conflation this signal exists to prevent.
|
|
48
|
+
*/
|
|
49
|
+
export function unindexedFilesField(ctx, claim, isEmpty) {
|
|
50
|
+
if (claim === 'named-lookup' && !isEmpty)
|
|
51
|
+
return {};
|
|
52
|
+
const unindexed = ctx.freshness?.peekUnindexed() ?? null;
|
|
53
|
+
return unindexed !== null && unindexed > 0 ? { unindexed_files: unindexed } : {};
|
|
54
|
+
}
|
|
26
55
|
/** Extract the first JSDoc comment block from chunk content, if present. */
|
|
27
56
|
export function extractDoc(content) {
|
|
28
57
|
const match = /\/\*\*([\s\S]*?)\*\//.exec(content);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_helpers.js","sourceRoot":"","sources":["../../../src/mcp/tools/_helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAA6B,MAAM,iBAAiB,CAAC;AAEpF,mEAAmE;AACnE,6EAA6E;AAC7E,yEAAyE;AACzE,oEAAoE;AACpE,sEAAsE;AACtE,4EAA4E;AAC5E,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,+BAA+B,GAKhC,MAAM,mCAAmC,CAAC;AAE3C,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAe;IAChD,OAAO,CAAC,MAAM,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,+EAA+E;AAC/E,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,EAAM,EACN,MAAsB,EACtB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,UAAU,CAAC,OAAO,CAAC;SACnB,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC;SAC5B,gBAAgB,EAAE,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChE,OAAO,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,8EAA8E;AAC9E,uEAAuE;AACvE,6EAA6E;AAC7E,yEAAyE;AACzE,uDAAuD"}
|
|
1
|
+
{"version":3,"file":"_helpers.js","sourceRoot":"","sources":["../../../src/mcp/tools/_helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAA6B,MAAM,iBAAiB,CAAC;AAEpF,sGAAsG;AACtG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,mEAAmE;AACnE,6EAA6E;AAC7E,yEAAyE;AACzE,oEAAoE;AACpE,sEAAsE;AACtE,4EAA4E;AAC5E,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,+BAA+B,GAKhC,MAAM,mCAAmC,CAAC;AAE3C,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAe;IAChD,OAAO,CAAC,MAAM,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC;AAmBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAe,EACf,KAAwB,EACxB,OAAgB;IAEhB,IAAI,KAAK,KAAK,cAAc,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACpD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,IAAI,CAAC;IACzD,OAAO,SAAS,KAAK,IAAI,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,+EAA+E;AAC/E,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,EAAM,EACN,MAAsB,EACtB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,UAAU,CAAC,OAAO,CAAC;SACnB,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC;SAC5B,gBAAgB,EAAE,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChE,OAAO,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,8EAA8E;AAC9E,uEAAuE;AACvE,6EAA6E;AAC7E,yEAAyE;AACzE,uDAAuD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callers.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/callers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAYhD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"callers.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/callers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAYhD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAgI5E"}
|