@omfalos/mokosh 0.5.0 → 0.5.2
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/dist/cli.js +104 -56
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +104 -56
- package/dist/cli.mjs.map +1 -1
- package/dist/duplication-worker.d.mts +1 -1
- package/dist/duplication-worker.d.ts +1 -1
- package/dist/duplication-worker.js +6 -4
- package/dist/duplication-worker.js.map +1 -1
- package/dist/duplication-worker.mjs +6 -4
- package/dist/duplication-worker.mjs.map +1 -1
- package/dist/index.d.mts +1164 -48
- package/dist/index.d.ts +1164 -48
- package/dist/index.js +48 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -37
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.js +52 -39
- package/dist/mcp.js.map +1 -1
- package/dist/mcp.mjs +52 -39
- package/dist/mcp.mjs.map +1 -1
- package/dist/parse-worker.d.mts +1 -1
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +6 -6
- package/dist/parse-worker.js.map +1 -1
- package/dist/parse-worker.mjs +6 -6
- package/dist/parse-worker.mjs.map +1 -1
- package/dist/{tokenizer-DIbb0GLe.d.mts → tokenizer-D9P-y7lH.d.mts} +1 -1
- package/dist/{tokenizer-DIbb0GLe.d.ts → tokenizer-D9P-y7lH.d.ts} +1 -1
- package/dist/{types-y90pqeDR.d.ts → types-B4jVIqMD.d.ts} +7 -0
- package/dist/{types-C153Tz5u.d.mts → types-qfwcXiZS.d.mts} +7 -0
- package/package.json +1 -1
- package/templates/skill/SKILL.md +1 -1
- package/templates/skill/mokosh.md +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { F as FileNode, C as CallEdge, I as ImportEdge, P as ParseResult, S as StructuredTag } from './types-
|
|
2
|
-
export { E as ExportedSymbol } from './types-
|
|
3
|
-
import { N as NormalizedToken } from './tokenizer-
|
|
4
|
-
import { F as FileType, N as NodeCategory } from './parse-yamNoaoL.js';
|
|
5
|
-
export { I as ImportType
|
|
1
|
+
import { F as FileNode, C as CallEdge, I as ImportEdge, P as ParseResult, S as StructuredTag } from './types-B4jVIqMD.js';
|
|
2
|
+
export { E as ExportedSymbol } from './types-B4jVIqMD.js';
|
|
3
|
+
import { N as NormalizedToken } from './tokenizer-D9P-y7lH.js';
|
|
4
|
+
import { F as FileType, N as NodeCategory, T as TagKind } from './parse-yamNoaoL.js';
|
|
5
|
+
export { I as ImportType } from './parse-yamNoaoL.js';
|
|
6
6
|
|
|
7
7
|
interface SerializedGraph {
|
|
8
8
|
nodes: FileNode[];
|
|
@@ -17,6 +17,104 @@ interface TraversalOptions {
|
|
|
17
17
|
direction?: "outgoing" | "incoming";
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/** Analyzes a dependency graph node map for unused files, export-usage hotspots, and circular import chains. */
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Edge kinds that {@link GraphAnalyzer.findCycles} treats as *not* real import cycles and skips
|
|
24
|
+
* by default: `"samePackage"` for the synthetic JVM same-package sibling clique, `"docReference"`
|
|
25
|
+
* for Markdown link / code-span file references (ADR-009). Pass one via `includeKinds` to walk it
|
|
26
|
+
* anyway (e.g. a caller that genuinely wants to see doc cross-link loops).
|
|
27
|
+
*/
|
|
28
|
+
type CycleEdgeKind = "docReference" | "samePackage";
|
|
29
|
+
/** Options for {@link GraphAnalyzer.findCycles}. */
|
|
30
|
+
interface FindCyclesOptions {
|
|
31
|
+
/** Edge kinds to include in the walk that are otherwise skipped (see {@link CycleEdgeKind}). */
|
|
32
|
+
includeKinds?: CycleEdgeKind[] | undefined;
|
|
33
|
+
/** When true, return every raw elementary cycle instead of collapsing cycles that share a
|
|
34
|
+
* strongly-connected component to one representative each. Default false. */
|
|
35
|
+
expandComponents?: boolean | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* @description Utility for analyzing the dependency graph for cycles and unused files.
|
|
39
|
+
* Operates on the raw node map rather than a `Graph` instance so it can be used
|
|
40
|
+
* without the full traversal infrastructure.
|
|
41
|
+
*/
|
|
42
|
+
declare class GraphAnalyzer {
|
|
43
|
+
private nodes;
|
|
44
|
+
/**
|
|
45
|
+
* @param {Map<string, FileNode>} nodes - The full node map of the graph to analyze, keyed by project-relative file path.
|
|
46
|
+
*/
|
|
47
|
+
constructor(nodes: Map<string, FileNode>);
|
|
48
|
+
/**
|
|
49
|
+
* @description Returns files from `allFiles` that are absent from the graph — meaning nothing
|
|
50
|
+
* imports them directly or transitively from any entry point, making them deletion candidates.
|
|
51
|
+
* @param {string[]} allFiles - Complete list of project-relative file paths to test against the graph.
|
|
52
|
+
* @returns {string[]} Subset of `allFiles` whose paths do not appear as graph nodes.
|
|
53
|
+
*/
|
|
54
|
+
findUnusedFiles(allFiles: string[]): string[];
|
|
55
|
+
/**
|
|
56
|
+
* @description Returns files whose highest single-edge export usage ratio meets or exceeds
|
|
57
|
+
* `threshold`, sorted descending by `maxExportUsage`. Useful for identifying files
|
|
58
|
+
* that consume a large fraction of one dependency's API surface.
|
|
59
|
+
* @param {number} threshold - Minimum `maxExportUsage` value (0–1) for a file to be included.
|
|
60
|
+
* @returns {Array<{ path: string; maxExportUsage: number; tightestDep: string }>} Entries sorted descending by `maxExportUsage`.
|
|
61
|
+
*/
|
|
62
|
+
findHighExportUsage(threshold: number): Array<{
|
|
63
|
+
path: string;
|
|
64
|
+
maxExportUsage: number;
|
|
65
|
+
tightestDep: string;
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* @description The structural-import targets of `nodePath` — the outgoing edges a cycle walk
|
|
69
|
+
* follows. Excludes unresolved (`!toPath`) and external edges always, and by default the two
|
|
70
|
+
* non-`import` synthetic edge kinds: the JVM same-package sibling clique (`isSamePackage`) and
|
|
71
|
+
* Markdown link / code-span file references (`isDocReference`, ADR-009). Both are opt-in via
|
|
72
|
+
* `include`. Shared by {@link findCycles}' DFS and its strongly-connected-component pass so
|
|
73
|
+
* the two agree on exactly which edges exist.
|
|
74
|
+
* @param nodePath - The file whose outgoing edges to resolve.
|
|
75
|
+
* @param include - Edge kinds to walk that are otherwise skipped.
|
|
76
|
+
* @returns Deduplicated target paths, in first-seen order.
|
|
77
|
+
*/
|
|
78
|
+
private traversableTargets;
|
|
79
|
+
/**
|
|
80
|
+
* @description Detects all circular import chains using DFS with a recursion-stack back-edge check.
|
|
81
|
+
* Each returned array is one cycle as an ordered list of file paths ending at the entry that closes the loop.
|
|
82
|
+
* Non-import edge kinds — the synthetic JVM same-package clique and Markdown doc references
|
|
83
|
+
* (ADR-009) — are skipped by default; pass `includeKinds` to walk them anyway.
|
|
84
|
+
*
|
|
85
|
+
* Cycles that share a strongly-connected component are collapsed to one representative each
|
|
86
|
+
* (the shortest, ties broken lexicographically). A single hub file cyclically bound to N
|
|
87
|
+
* siblings — e.g. `ItemList.tsx` importing four `*CellRenderer.tsx` that each import a type
|
|
88
|
+
* back from it — is one dependency knot to untangle, not N findings; the DFS otherwise emits
|
|
89
|
+
* one back-edge cycle per sibling. Pass `expandComponents` to get every raw elementary cycle
|
|
90
|
+
* instead.
|
|
91
|
+
* @param {FindCyclesOptions} [opts] - `includeKinds` opts specific edge kinds back into the
|
|
92
|
+
* walk; `expandComponents` disables the same-component collapse.
|
|
93
|
+
* @returns {string[][]} Array of cycles; each cycle is an ordered list of file paths forming a loop.
|
|
94
|
+
*/
|
|
95
|
+
findCycles(opts?: FindCyclesOptions): string[][];
|
|
96
|
+
/**
|
|
97
|
+
* @description Collapses `cycles` so that every strongly-connected component contributes at
|
|
98
|
+
* most one — its shortest cycle, ties broken by the joined path string for determinism.
|
|
99
|
+
* Every elementary cycle lies wholly within one SCC, so cycles are grouped by the SCC id of
|
|
100
|
+
* any member; a graph with no repeated-SCC cycles passes through unchanged (order preserved
|
|
101
|
+
* by first appearance).
|
|
102
|
+
* @param cycles - Raw elementary cycles from the DFS.
|
|
103
|
+
* @param include - Edge kinds in scope, so the SCC pass walks the same edges the DFS did.
|
|
104
|
+
* @returns One representative cycle per SCC that had any, in first-appearance order.
|
|
105
|
+
*/
|
|
106
|
+
private collapseByComponent;
|
|
107
|
+
/**
|
|
108
|
+
* @description Tarjan's strongly-connected-components over the cycle-walk edge set, restricted
|
|
109
|
+
* to nodes that sit in a component of size ≥ 2 (the only ones that can be on a cycle). Runs
|
|
110
|
+
* iteratively — the graphs this analyzes reach tens of thousands of nodes, past a safe
|
|
111
|
+
* recursion depth.
|
|
112
|
+
* @param include - Edge kinds to walk, matching {@link traversableTargets}.
|
|
113
|
+
* @returns Map from file path to a numeric component id, only for nodes in a non-trivial SCC.
|
|
114
|
+
*/
|
|
115
|
+
private componentIds;
|
|
116
|
+
}
|
|
117
|
+
|
|
20
118
|
/** Graph class wrapping the raw node map with DFS traversal, cycle detection, serialization, and reverse-edge helpers. */
|
|
21
119
|
|
|
22
120
|
/**
|
|
@@ -112,9 +210,12 @@ declare class Graph {
|
|
|
112
210
|
/**
|
|
113
211
|
* @description Detects all circular import chains in the graph using DFS
|
|
114
212
|
* with a back-edge check. Each returned array is one cycle as an ordered path.
|
|
213
|
+
* Synthetic JVM same-package edges and Markdown doc references are skipped by default —
|
|
214
|
+
* pass `opts.includeKinds` to walk them.
|
|
215
|
+
* @param {FindCyclesOptions} [opts] - Forwarded to {@link GraphAnalyzer.findCycles}.
|
|
115
216
|
* @returns Array of cycles; each cycle is a list of file paths forming a loop.
|
|
116
217
|
*/
|
|
117
|
-
findCycles(): string[][];
|
|
218
|
+
findCycles(opts?: FindCyclesOptions): string[][];
|
|
118
219
|
}
|
|
119
220
|
|
|
120
221
|
/** Configures whether/how `parseFile` calls are offloaded to a `piscina` worker pool. `false` always parses in-process. */
|
|
@@ -184,6 +285,16 @@ interface MokoshConfig {
|
|
|
184
285
|
*/
|
|
185
286
|
frameworkOverrides?: Record<string, TagFramework>;
|
|
186
287
|
};
|
|
288
|
+
/**
|
|
289
|
+
* Tunes which tags count as test-selection labels for `propose_tags`, `apply_tags`,
|
|
290
|
+
* `list_tags` (default view) and the `tag:` query filter. Both lists are case-insensitive.
|
|
291
|
+
*/
|
|
292
|
+
tags?: {
|
|
293
|
+
/** Tag names to treat as noise, added to the built-in curated blocklist. */
|
|
294
|
+
blocklist?: string[];
|
|
295
|
+
/** Tag names to keep even when built-in- or user-blocked (overrides `blocklist`). */
|
|
296
|
+
allowlist?: string[];
|
|
297
|
+
};
|
|
187
298
|
/** Path to the Istanbul/v8 `coverage-summary.json` file, relative to the project root. When set, `coveragePct` is populated on each node after the graph is built. */
|
|
188
299
|
coverageReportPath?: string;
|
|
189
300
|
/** Default line-coverage threshold (0–100) used by `find_uncovered`. Defaults to `80` when not specified. */
|
|
@@ -197,6 +308,39 @@ interface MokoshConfig {
|
|
|
197
308
|
* in-process, or pass `{ minFiles, maxThreads }` to raise the threshold instead.
|
|
198
309
|
*/
|
|
199
310
|
parallelParsing?: ParallelParsingOption;
|
|
311
|
+
/** Duplicate-detection (`find_duplicates` / `--find-duplicates`) tuning. */
|
|
312
|
+
duplication?: {
|
|
313
|
+
/**
|
|
314
|
+
* Extra generated / vendored file patterns to exclude from duplicate scanning, merged with
|
|
315
|
+
* the built-in list (protobuf output, `*.generated.*`, `generated/` dirs, `@generated`
|
|
316
|
+
* markers). Two shapes only — `** /name/**` (any path segment equals `name`) and `*.suffix`
|
|
317
|
+
* (basename ends with `.suffix`) — not full glob syntax.
|
|
318
|
+
*/
|
|
319
|
+
ignoreGlobs?: string[];
|
|
320
|
+
/** When true, scan generated / vendored files too (default false). Matches involving one are
|
|
321
|
+
* tagged `signals: ["generated"]`. */
|
|
322
|
+
includeGenerated?: boolean;
|
|
323
|
+
/** When true, include matches where every occurrence is in a single file (default false) —
|
|
324
|
+
* a file's own naturally repetitive structure is usually not actionable copy-paste. Matches
|
|
325
|
+
* are always tagged `signals: ["same-file"]` regardless of this setting. */
|
|
326
|
+
includeSameFile?: boolean;
|
|
327
|
+
/** When true, include matches whose occurrences are all inline SVG / SVG-shaped JSX markup
|
|
328
|
+
* (default false) — two different icons sharing a literal-normalized skeleton (block matcher)
|
|
329
|
+
* or a byte-identical `<defs>`/`<filter>` block (`defKind: "jsxElement"`), neither of which is
|
|
330
|
+
* usually an authored clone. Matches are always tagged `signals: ["svg-markup"]` regardless
|
|
331
|
+
* of this setting. */
|
|
332
|
+
includeSvgMarkup?: boolean;
|
|
333
|
+
/** When true, include matches in the `markdown` family (default false) — mirrored prose
|
|
334
|
+
* docs (README ↔ *.mdx) are not code duplication. Matches are always tagged
|
|
335
|
+
* `signals: ["docs"]` regardless. */
|
|
336
|
+
includeDocs?: boolean;
|
|
337
|
+
/** Which duplicates to surface, by test-file involvement (default `"src"`). `"src"` drops
|
|
338
|
+
* every cluster with a test-file occurrence; `"tests"` returns only substantive test
|
|
339
|
+
* clusters (shared setup/mocks/assertions, not render/snapshot skeletons); `"all"` returns
|
|
340
|
+
* everything. The `--scope` CLI flag overrides this. Matches are always tagged
|
|
341
|
+
* `signals: ["test"]` regardless of this setting. */
|
|
342
|
+
scope?: "src" | "tests" | "all";
|
|
343
|
+
};
|
|
200
344
|
}
|
|
201
345
|
/**
|
|
202
346
|
* @description Loads a mokosh config file, probing standard filenames in `rootDirOrPath` or reading an explicit path when `isExplicitPath` is true.
|
|
@@ -243,10 +387,40 @@ declare function configToGraphOptions(config: MokoshConfig | undefined): {
|
|
|
243
387
|
declare const DEFAULT_CACHE_DIR = "mokosh-cache";
|
|
244
388
|
/** Filename for the disk-persisted `find_duplicates` token cache within `DEFAULT_CACHE_DIR`. */
|
|
245
389
|
declare const DEFAULT_DUPLICATION_TOKEN_CACHE_FILE = "duplication-tokens.json";
|
|
390
|
+
/** Filename for the disk-persisted `find_duplicates` *result* cache within `DEFAULT_CACHE_DIR`
|
|
391
|
+
* (or, on a monorepo, `<pkg-slug>-duplication-result.json` per package). Holds the full
|
|
392
|
+
* `{ groups, clusters }` from the last scan, keyed by a digest of every in-scope file's
|
|
393
|
+
* mtime/size plus the output-affecting scan params — a match lets a repeat call skip the scan
|
|
394
|
+
* entirely. See `src/graph/duplication/result-cache-store.ts`. */
|
|
395
|
+
declare const DEFAULT_DUPLICATION_RESULT_CACHE_FILE = "duplication-result.json";
|
|
246
396
|
/** Filename for the disk-persisted graph cache within `DEFAULT_CACHE_DIR`. Written by the CLI
|
|
247
397
|
* (`src/cli/graph-loader.ts`) after every build; read by the MCP server (`src/mcp/cache.ts`) to
|
|
248
398
|
* seed a session's first `analyze` call so it reuses unchanged nodes instead of parsing cold. */
|
|
249
399
|
declare const DEFAULT_GRAPH_CACHE_FILE = "graph.json";
|
|
400
|
+
/** Legacy filename for the single-blob disk-persisted *workspace* (monorepo) graph cache within
|
|
401
|
+
* `DEFAULT_CACHE_DIR`. Superseded by `DEFAULT_WORKSPACE_CACHE_SUBDIR` (a manifest plus one file
|
|
402
|
+
* per package) — retained only so `src/graph/workspace/disk-cache.ts` can unlink a stale copy
|
|
403
|
+
* left by an older mokosh. A 190 MB+ single file here was `JSON.parse`d whole and OOM-killed the
|
|
404
|
+
* MCP server; see the manifest layout below. */
|
|
405
|
+
declare const DEFAULT_WORKSPACE_GRAPH_CACHE_FILE = "workspace-graph.json";
|
|
406
|
+
/** Subdirectory of `DEFAULT_CACHE_DIR` holding the workspace graph cache as a `manifest.json`
|
|
407
|
+
* ("the map file") plus one `<pkg-slug>.json` per package. Each package file is parsed on its
|
|
408
|
+
* own, so peak memory on hydrate is bounded by the largest single package rather than the whole
|
|
409
|
+
* serialized workspace. Written/read by `src/graph/workspace/disk-cache.ts`, driven from
|
|
410
|
+
* `src/mcp/cache.ts`. */
|
|
411
|
+
declare const DEFAULT_WORKSPACE_CACHE_SUBDIR = "workspace";
|
|
412
|
+
/** Filename of the workspace cache manifest within `DEFAULT_WORKSPACE_CACHE_SUBDIR`: the small
|
|
413
|
+
* index that carries the layout, the root (non-package-owned) source digest, and one entry per
|
|
414
|
+
* package (name, relative root, entry points, per-package digest, node count, cache filename). */
|
|
415
|
+
declare const WORKSPACE_MANIFEST_FILE = "manifest.json";
|
|
416
|
+
/** Schema version stamped into the workspace cache manifest. A mismatch on read discards the
|
|
417
|
+
* whole cache (full rebuild) — bump this whenever the serialized `FileNode` shape or the
|
|
418
|
+
* manifest structure changes incompatibly. */
|
|
419
|
+
declare const WORKSPACE_CACHE_VERSION = 1;
|
|
420
|
+
/** A single package whose serialized node array exceeds this is not written to the workspace
|
|
421
|
+
* cache (and never read back) — that package always rebuilds. Keeps any one per-package
|
|
422
|
+
* `JSON.parse` on hydrate within a safe memory envelope. */
|
|
423
|
+
declare const MAX_PACKAGE_CACHE_BYTES: number;
|
|
250
424
|
/** Subdirectory of `DEFAULT_CACHE_DIR` holding one JSON file per commit sha — graphs built for
|
|
251
425
|
* the "other" ref in a `compareBranches` call (`src/graph/branch-graph-cache.ts`). Keyed by sha
|
|
252
426
|
* rather than branch name so entries are immutable and never need invalidation. */
|
|
@@ -315,6 +489,57 @@ interface ApiSurface {
|
|
|
315
489
|
*/
|
|
316
490
|
testFiles: string[];
|
|
317
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* A compact, token-bounded projection of an {@link ApiSurface}, suitable as the default
|
|
494
|
+
* response of a "what's the public API?" query. Every unbounded list in `ApiSurface` is
|
|
495
|
+
* replaced by a count; `publicExports` is reduced to a capped `{ name, kind }` sample plus a
|
|
496
|
+
* `byKind` histogram over the full set. `unreachableFromEntry` — the one directly actionable
|
|
497
|
+
* signal (missed entry points / dead code) — keeps its list, capped.
|
|
498
|
+
*/
|
|
499
|
+
interface ApiSurfaceSummary {
|
|
500
|
+
/** Entry points, capped (see `entryPointsTruncated`). */
|
|
501
|
+
entryPoints: string[];
|
|
502
|
+
/** True number of entry points. */
|
|
503
|
+
entryPointCount: number;
|
|
504
|
+
/** Present and `true` when `entryPoints` was truncated. */
|
|
505
|
+
entryPointsTruncated?: true;
|
|
506
|
+
/** True number of accessible public exports. */
|
|
507
|
+
publicExportCount: number;
|
|
508
|
+
/** Capped `{ name, kind }` sample of the public exports, alphabetical. */
|
|
509
|
+
publicExports: Array<{
|
|
510
|
+
name: string;
|
|
511
|
+
kind: ExportKind;
|
|
512
|
+
}>;
|
|
513
|
+
/** Present and `true` when `publicExports` was truncated. */
|
|
514
|
+
publicExportsTruncated?: true;
|
|
515
|
+
/** Count of public exports per {@link ExportKind}, over the full (un-capped) set. */
|
|
516
|
+
byKind: Partial<Record<ExportKind, number>>;
|
|
517
|
+
/** Number of non-test implementation files reachable from an entry point. */
|
|
518
|
+
internalFileCount: number;
|
|
519
|
+
/** Number of non-test files not reachable from any entry point. */
|
|
520
|
+
unreachableFromEntryCount: number;
|
|
521
|
+
/** The unreachable non-test files, capped (see `unreachableFromEntryTruncated`). */
|
|
522
|
+
unreachableFromEntry: string[];
|
|
523
|
+
/** Present and `true` when `unreachableFromEntry` was truncated. */
|
|
524
|
+
unreachableFromEntryTruncated?: true;
|
|
525
|
+
/** Number of unreachable test files. */
|
|
526
|
+
testFileCount: number;
|
|
527
|
+
/** How to obtain the full lists this summary omits. */
|
|
528
|
+
hint: string;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Reduces a full {@link ApiSurface} to a token-bounded {@link ApiSurfaceSummary}.
|
|
532
|
+
*
|
|
533
|
+
* @param {ApiSurface} surface - The full surface report from {@link buildApiSurface}.
|
|
534
|
+
* @param {{ maxExports?: number; maxUnreachable?: number }} [opts] - `maxExports` caps the
|
|
535
|
+
* `publicExports` sample (default 30; pass `Infinity` to keep all names); `maxUnreachable`
|
|
536
|
+
* caps the `unreachableFromEntry` list (default 50).
|
|
537
|
+
* @returns {ApiSurfaceSummary} The compact projection.
|
|
538
|
+
*/
|
|
539
|
+
declare function summarizeApiSurface(surface: ApiSurface, opts?: {
|
|
540
|
+
maxExports?: number;
|
|
541
|
+
maxUnreachable?: number;
|
|
542
|
+
}): ApiSurfaceSummary;
|
|
318
543
|
/**
|
|
319
544
|
* Attempts to auto-detect the primary public entry point by reading `package.json` from `root`.
|
|
320
545
|
* Handles modern conditional-exports objects as well as plain `main`/`module` fields.
|
|
@@ -468,17 +693,27 @@ declare function loadChangeImpactCache(cachePath: string): ChangeImpactCache | n
|
|
|
468
693
|
|
|
469
694
|
/**
|
|
470
695
|
* Language-family partitioning for duplicate detection. `findDuplicates` never compares files
|
|
471
|
-
* across a family boundary — see docs/adr-013-duplicate-detection-noise-reduction.md.
|
|
472
|
-
*
|
|
696
|
+
* across a family boundary — see docs/adr-013-duplicate-detection-noise-reduction.md.
|
|
697
|
+
*
|
|
698
|
+
* The original driver was CSS: style declarations share a small, finite vocabulary (property
|
|
473
699
|
* names, common value keywords) that the shared `KEYWORDS` denylist in `tokenizer.ts` doesn't
|
|
474
|
-
* cover, so unrelated
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
700
|
+
* cover, so unrelated rules with the same declaration *shape* hashed identically to unrelated
|
|
701
|
+
* code shapes. The same argument applies, more weakly, *within* code: Java, Go, and TS/JS all use
|
|
702
|
+
* C-style `if (…) { … }` / `for (…; …; …)` blocks, so a `for` loop that pushes onto a list
|
|
703
|
+
* tokenizes almost identically in all three once identifiers collapse to `ID` — a polyglot repo
|
|
704
|
+
* then reports phantom control-flow "duplicates" across languages that share nothing. So code is
|
|
705
|
+
* split by paradigm too, not lumped into one family.
|
|
706
|
+
*
|
|
707
|
+
* Grouping (not one family per `FileType`) is deliberate: JavaScript↔TypeScript and
|
|
708
|
+
* CoffeeScript/LiveScript→JavaScript ports are real, common, cross-`FileType` duplication worth
|
|
709
|
+
* detecting, so those languages share the `"js"` family. The JVM languages interoperate and share
|
|
710
|
+
* idioms, so they share `"jvm"` — and, being their own family, never match against `"js"`, which
|
|
711
|
+
* is the case that motivated this split. Splitting by family also lets later phases tune
|
|
712
|
+
* `windowSize`/`minLines`/vocabulary per family without one family's tuning affecting another's.
|
|
478
713
|
*/
|
|
479
714
|
|
|
480
715
|
/** One partition of `findDuplicates` matching — files in different families are never compared. */
|
|
481
|
-
type DuplicateFamily = "style" | "
|
|
716
|
+
type DuplicateFamily = "style" | "js" | "jvm" | "python" | "go" | "lua" | "markdown" | "gherkin" | "other";
|
|
482
717
|
|
|
483
718
|
/**
|
|
484
719
|
* Sliding-window (shingle) hashing over a normalized token stream, plus chain-merging of
|
|
@@ -496,7 +731,32 @@ interface DuplicateOccurrence {
|
|
|
496
731
|
file: string;
|
|
497
732
|
startLine: number;
|
|
498
733
|
endLine: number;
|
|
499
|
-
|
|
734
|
+
/** The variable/type name declared at this location. Set only on `kind: "definition"` groups
|
|
735
|
+
* (`style-vars.ts`, `type-defs.ts`) — block matches have no single declared name. */
|
|
736
|
+
name?: string | undefined;
|
|
737
|
+
/** This occurrence's own normalized value. Set only on `defKind: "cssVar"` groups tagged
|
|
738
|
+
* `signals: ["value-drift"]`, where occurrences disagree and the value itself is the point. */
|
|
739
|
+
value?: string | undefined;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Advisory tags on a {@link DuplicateGroup} that help a caller (and issue 6's query layer) judge
|
|
743
|
+
* whether a match is worth acting on: `"same-file"` — every occurrence is in one file (an
|
|
744
|
+
* internally repeated block); `"generated"` — at least one occurrence is in a file only scanned
|
|
745
|
+
* because `includeGenerated: true` was passed; `"value-drift"` — a `defKind: "cssVar"` group
|
|
746
|
+
* where the same variable *name* holds different values across files (as opposed to the default
|
|
747
|
+
* consolidation case: different names, same value); `"svg-markup"` — every occurrence's source
|
|
748
|
+
* span is predominantly inline SVG / SVG-shaped JSX markup (a block match where two *different*
|
|
749
|
+
* icons share a literal-normalized skeleton, or a `defKind: "jsxElement"` match on an identical
|
|
750
|
+
* `<defs>`/`<filter>` block — see `svg-markup.ts`); `"test"` — at least one occurrence is in a
|
|
751
|
+
* test file (`__tests__/`, `__mocks__/`, `*.test.*`, `*.spec.*`, `__snapshots__/`); `"docs"` —
|
|
752
|
+
* the group is in the `markdown` family (mirrored prose docs, `README.md` ↔ `*.mdx`).
|
|
753
|
+
* `"same-file"`, `"svg-markup"` and `"docs"` groups are excluded from `findDuplicates`' results
|
|
754
|
+
* by default (opt back in with `includeSameFile` / `includeSvgMarkup` / `includeDocs`); `"test"`
|
|
755
|
+
* groups are filtered by the `scope` option (see `findDuplicates`), which separates substantive
|
|
756
|
+
* shared test logic from render/snapshot skeletons. The tag is always attached regardless of any
|
|
757
|
+
* filter. Designed to grow (issue 7 adds more).
|
|
758
|
+
*/
|
|
759
|
+
type DuplicateSignal = "same-file" | "generated" | "value-drift" | "svg-markup" | "test" | "docs";
|
|
500
760
|
interface DuplicateGroup {
|
|
501
761
|
/** Every location sharing this duplicated block (two or more) — locations that pairwise
|
|
502
762
|
* chain-match are clustered into one group instead of being reported once per pair, so a
|
|
@@ -504,14 +764,105 @@ interface DuplicateGroup {
|
|
|
504
764
|
occurrences: DuplicateOccurrence[];
|
|
505
765
|
/** Line span of the largest single pairwise match clustered into this group (each pair's own
|
|
506
766
|
* span is the shorter of its two occurrences) — the best-verified size for this block, not an
|
|
507
|
-
* average or a value shrunk by a more weakly-matching cluster member.
|
|
767
|
+
* average or a value shrunk by a more weakly-matching cluster member. For `kind: "definition"`
|
|
768
|
+
* groups this is the declaration's own line span instead (1 for a single-line `cssVar` decl). */
|
|
508
769
|
lines: number;
|
|
509
|
-
/** Token-window length backing this block, after chain-merging adjacent windows.
|
|
770
|
+
/** Token-window length backing this block, after chain-merging adjacent windows. For
|
|
771
|
+
* `kind: "definition"` groups this is the member/field count instead (1 for `cssVar`). */
|
|
510
772
|
tokens: number;
|
|
773
|
+
/** Count of *logic-bearing* tokens (keywords + operators, not `ID`/`NUM`/`STR`/punctuation —
|
|
774
|
+
* see {@link isSignificantToken}) in the verified span. `findDuplicates` ranks and (with
|
|
775
|
+
* `minScore`) filters on this so a large low-information block — a Flow `type Props`, a JSX
|
|
776
|
+
* icon wrapper, a schema object literal — no longer outranks real shared logic. Set on
|
|
777
|
+
* `kind: "block"` groups by `suffix-duplicates.ts`; absent on `kind: "definition"` groups
|
|
778
|
+
* (already content-verified) — those are ranked by `lines` instead. See
|
|
779
|
+
* docs/adr-019-logic-token-scoring.md. */
|
|
780
|
+
score?: number | undefined;
|
|
511
781
|
/** Which language family both occurrences belong to (set by `findDuplicates`, which never
|
|
512
782
|
* matches across families — see docs/adr-013-duplicate-detection-noise-reduction.md).
|
|
513
783
|
* Absent when called directly with a token stream that isn't family-scoped, e.g. in tests. */
|
|
514
784
|
family?: DuplicateFamily | undefined;
|
|
785
|
+
/** Advisory tags for filtering — see {@link DuplicateSignal}. Absent (not `[]`) when no
|
|
786
|
+
* signal applies. */
|
|
787
|
+
signals?: DuplicateSignal[] | undefined;
|
|
788
|
+
/** `"block"` (the default, and absent on every group predating this field) is a token/structural
|
|
789
|
+
* match over a span of code; `"definition"` is a declaration-level match — see `defKind`. */
|
|
790
|
+
kind?: "block" | "definition" | undefined;
|
|
791
|
+
/** What kind of declaration a `kind: "definition"` group matched on. Absent for `kind: "block"`
|
|
792
|
+
* (or absent-`kind`) groups. */
|
|
793
|
+
defKind?: "cssVar" | "interface" | "type" | "objectLiteral" | "jsxElement" | undefined;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Groups `DuplicateGroup`s that share the exact same set of files into one `DuplicateCluster`,
|
|
798
|
+
* addressing the "window-splitting" noise class `applyDominanceFilter` (suffix-duplicates.ts)
|
|
799
|
+
* deliberately doesn't touch: two files that share several genuinely non-nested matches (different
|
|
800
|
+
* start positions, no containment relationship — see
|
|
801
|
+
* docs/adr-015-suffix-array-duplicate-detection.md's addendum) still surface as one `DuplicateGroup`
|
|
802
|
+
* per match, so a single real duplication between two files can legitimately report a dozen-plus
|
|
803
|
+
* rows. A file pair matched by 14 separate groups (same two files, non-nested spans) becomes one
|
|
804
|
+
* cluster with `matchCount: 14` instead of 14 rows a caller has to manually recognize as "the same
|
|
805
|
+
* underlying duplication" — the `ContentExplorer.tsx`/`ContentPicker.js` case from the dogfooding
|
|
806
|
+
* report this responds to.
|
|
807
|
+
*
|
|
808
|
+
* **This module previously clustered by connected file-component (union-find: any two files that
|
|
809
|
+
* co-occur in some group's occurrences are connected, transitively) instead of exact file-set
|
|
810
|
+
* equality, and that was wrong — caught by a follow-up dogfood pass before anyone relied on it.**
|
|
811
|
+
* Connected-component clustering is single-linkage clustering, and single-linkage is notorious for
|
|
812
|
+
* "chaining": on a real repo, one file sharing a single incidental block each with dozens of
|
|
813
|
+
* otherwise-unrelated files is enough to transitively merge all of them into one cluster, since
|
|
814
|
+
* connectivity only ever needs one bridge edge, never a strong one. Confirmed in practice on a
|
|
815
|
+
* production monorepo: 803 files and 4,853 groups collapsed into a single "cluster" — a regression,
|
|
816
|
+
* not a summary, and strictly less useful than the flat `groups` list it was meant to compress.
|
|
817
|
+
* Exact file-set equality can't chain this way: a group between `{A, B}` and a group between
|
|
818
|
+
* `{B, C}` (B is a genuine bridge — it duplicates code with both, but that doesn't mean A and C
|
|
819
|
+
* relate to each other) land in two separate clusters, not one, because their occurrence sets
|
|
820
|
+
* differ. The trade-off this accepts: a true N-way clone family reported as several exact-N-way
|
|
821
|
+
* groups already merges correctly (same set every time); one reported as several *different-sized*
|
|
822
|
+
* subsets (a 3-file match here, a 2-file subset of it there) does not merge into one cluster. That
|
|
823
|
+
* residual case is deliberately left alone rather than resolved with more graph theory — a
|
|
824
|
+
* conservative under-merge is a far safer default than the chaining failure mode above.
|
|
825
|
+
*
|
|
826
|
+
* **Coverage.** `matchCount: 14` still reads as "14 things," not "one relationship" — the same
|
|
827
|
+
* gap SonarQube's duplication check closes by reporting a merged duplication-*density* percentage
|
|
828
|
+
* per file instead of an enumerated match list. {@link DuplicateCluster.coverage} does the same
|
|
829
|
+
* here: union each cluster's occurrence spans per file (so overlapping/adjacent sub-matches count
|
|
830
|
+
* once, not once each) and divide by that file's total line count — turning
|
|
831
|
+
* `matchCount: 14, files: ["ContentExplorer.tsx", "ContentPicker.js"]` into
|
|
832
|
+
* `"ContentExplorer.tsx: 62% (312/501 lines) duplicated with ContentPicker.js"`, which is a much
|
|
833
|
+
* more decisive triage signal than the match count alone: high coverage on both sides says "these
|
|
834
|
+
* are near-duplicate files," low coverage says "one shared helper embedded in two otherwise-
|
|
835
|
+
* unrelated files."
|
|
836
|
+
*/
|
|
837
|
+
|
|
838
|
+
interface DuplicateClusterFileCoverage {
|
|
839
|
+
file: string;
|
|
840
|
+
/** Total lines this cluster's occurrences cover in `file`, after merging overlapping/adjacent
|
|
841
|
+
* spans across every group in the cluster — never double-counts a line two groups both touch. */
|
|
842
|
+
coveredLines: number;
|
|
843
|
+
/** `file`'s total line count, when known (the caller passed `fileLineCounts` and had an entry
|
|
844
|
+
* for it) — `undefined` otherwise, e.g. a cache-hit file scanned without re-reading its source. */
|
|
845
|
+
totalLines: number | undefined;
|
|
846
|
+
/** `coveredLines / totalLines * 100`, rounded to one decimal place — `undefined` when
|
|
847
|
+
* `totalLines` isn't known or is 0. */
|
|
848
|
+
coveragePct: number | undefined;
|
|
849
|
+
}
|
|
850
|
+
interface DuplicateCluster {
|
|
851
|
+
/** The exact set of files every group in this cluster shares occurrences in, sorted. */
|
|
852
|
+
files: string[];
|
|
853
|
+
/** This cluster's member groups, largest-`lines`-first — the same `DuplicateGroup` objects
|
|
854
|
+
* `findDuplicates` would otherwise report standalone, unmodified. */
|
|
855
|
+
groups: DuplicateGroup[];
|
|
856
|
+
/** `groups.length` — how many separate matches this cluster bundles. A cluster with
|
|
857
|
+
* `matchCount: 1` is just a single group reported through the same shape for consistency. */
|
|
858
|
+
matchCount: number;
|
|
859
|
+
/** The largest `lines` value across this cluster's groups — the single best-verified size for
|
|
860
|
+
* "how big is this duplication," independent of how many rows it fragmented into. */
|
|
861
|
+
longestMatch: number;
|
|
862
|
+
/** Per-file duplication coverage, same order as `files` — see this module's top-of-file
|
|
863
|
+
* comment. Every entry has `coveredLines`; `totalLines`/`coveragePct` are only present when
|
|
864
|
+
* `buildDuplicateClusters` was given a line count for that file. */
|
|
865
|
+
coverage: DuplicateClusterFileCoverage[];
|
|
515
866
|
}
|
|
516
867
|
|
|
517
868
|
/** One file's cached tokenize result, fingerprinted by `mtime`/`size`/`ignoreLiterals` — any
|
|
@@ -522,6 +873,10 @@ interface CachedFileTokens {
|
|
|
522
873
|
mtime: number;
|
|
523
874
|
size: number;
|
|
524
875
|
ignoreLiterals: boolean;
|
|
876
|
+
/** Whether this file is generated / vendored (path heuristic or first-bytes marker) — cached
|
|
877
|
+
* so a `includeGenerated: false` scan that gets a cache hit can still exclude it, and a
|
|
878
|
+
* `includeGenerated: true` scan can still tag matches, without re-reading the file. */
|
|
879
|
+
generated: boolean;
|
|
525
880
|
tokens: NormalizedToken[];
|
|
526
881
|
}
|
|
527
882
|
/** Caller-owned cache, keyed by project-relative path, reused across repeated `findDuplicates`
|
|
@@ -550,6 +905,174 @@ declare function loadTokenCacheFromDisk(cachePath: string): DuplicationTokenCach
|
|
|
550
905
|
*/
|
|
551
906
|
declare function saveTokenCacheToDisk(cache: DuplicationTokenCache, cachePath: string): void;
|
|
552
907
|
|
|
908
|
+
/**
|
|
909
|
+
* @description Whether a file's path alone marks it as generated — a known codegen basename
|
|
910
|
+
* suffix, a generated-output directory segment, or a caller-supplied `ignoreGlobs` match.
|
|
911
|
+
* Cheap and synchronous, so it runs before the file is read.
|
|
912
|
+
* @param relPath - Project-relative file path.
|
|
913
|
+
* @param ignoreGlobs - Extra patterns from `MokoshConfig.duplication.ignoreGlobs`.
|
|
914
|
+
* @returns Whether `relPath` should be treated as generated.
|
|
915
|
+
*/
|
|
916
|
+
declare function isGeneratedPath(relPath: string, ignoreGlobs?: readonly string[]): boolean;
|
|
917
|
+
/**
|
|
918
|
+
* @description Whether the start of a file's source carries a generated-by marker
|
|
919
|
+
* (`@generated`, `DO NOT EDIT`, `Code generated by …`, etc.). Only the first
|
|
920
|
+
* {@link MARKER_SCAN_BYTES} characters are checked — codegen tools put the banner at the top.
|
|
921
|
+
* @param source - Full file source.
|
|
922
|
+
* @returns Whether a generated marker is present near the top of `source`.
|
|
923
|
+
*/
|
|
924
|
+
declare function hasGeneratedMarker(source: string): boolean;
|
|
925
|
+
|
|
926
|
+
/** The output-affecting `findDuplicates` knobs — everything that changes which groups/clusters
|
|
927
|
+
* the scan produces. Folded into the cache key via {@link duplicationResultCacheKey}; a
|
|
928
|
+
* mismatch is a cache miss. `filter` is deliberately absent — the cache only ever holds an
|
|
929
|
+
* unfiltered scan. Any new output-affecting option wired into `findDuplicates` (e.g. a future
|
|
930
|
+
* `minScore`) must be added here too, or a run that changes it will read a stale result. */
|
|
931
|
+
interface DuplicationResultParams {
|
|
932
|
+
minLines: number;
|
|
933
|
+
ignoreLiterals: boolean;
|
|
934
|
+
maxPunctuationRatio: number;
|
|
935
|
+
/** The per-scan cap passed to `findDuplicates` — the stored result holds at most this many
|
|
936
|
+
* groups per package, so a later run wanting more must re-scan. */
|
|
937
|
+
limit: number;
|
|
938
|
+
scope: string | undefined;
|
|
939
|
+
includeGenerated: boolean;
|
|
940
|
+
includeSameFile: boolean;
|
|
941
|
+
includeSvgMarkup: boolean;
|
|
942
|
+
includeDocs: boolean;
|
|
943
|
+
ignoreDirs: string[];
|
|
944
|
+
ignoreGlobs: string[];
|
|
945
|
+
}
|
|
946
|
+
interface CachedDuplicationResult {
|
|
947
|
+
/** {@link duplicationDigest} of the in-scope files at write time. */
|
|
948
|
+
digest: string;
|
|
949
|
+
/** {@link duplicationResultCacheKey} of the scan params at write time. */
|
|
950
|
+
paramsKey: string;
|
|
951
|
+
groups: DuplicateGroup[];
|
|
952
|
+
clusters: DuplicateCluster[];
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* @description One `path\0mtime\0size` line per node, sha256'd over the sorted set — the same
|
|
956
|
+
* unit `computeWorkspaceSourceDigest` (`src/index.ts`) hashes, but sourced from the in-memory
|
|
957
|
+
* graph (every `FileNode` already carries `mtime`/`size`) so no `fs.stat` walk is needed.
|
|
958
|
+
* Passing the whole node set (a superset of what `findDuplicates` actually scans) is
|
|
959
|
+
* intentional: an out-of-scope change over-invalidates, which is safe.
|
|
960
|
+
* @param {Iterable<{ path: string; mtime: number; size: number }>} nodes - Graph file nodes.
|
|
961
|
+
* @returns {string} Hex sha-256 digest.
|
|
962
|
+
*/
|
|
963
|
+
declare function duplicationDigest(nodes: Iterable<{
|
|
964
|
+
path: string;
|
|
965
|
+
mtime: number;
|
|
966
|
+
size: number;
|
|
967
|
+
}>): string;
|
|
968
|
+
/**
|
|
969
|
+
* @description Stable string form of the scan params for the cache key — keys emitted in a fixed
|
|
970
|
+
* order, arrays sorted — so equivalent params always produce the same string regardless of how
|
|
971
|
+
* the caller assembled them.
|
|
972
|
+
* @param {DuplicationResultParams} params - The output-affecting scan knobs.
|
|
973
|
+
* @returns {string} A canonical JSON string to compare byte-for-byte.
|
|
974
|
+
*/
|
|
975
|
+
declare function duplicationResultCacheKey(params: DuplicationResultParams): string;
|
|
976
|
+
/**
|
|
977
|
+
* @description Reads a `CachedDuplicationResult` written by {@link saveDuplicationResult} and
|
|
978
|
+
* returns its `{ groups, clusters }` only when the stored `digest` and `paramsKey` both still
|
|
979
|
+
* match — i.e. no in-scope file changed and the scan params are identical. Any other outcome
|
|
980
|
+
* (missing file, corrupt JSON, wrong shape, stale digest, different params) returns `null`,
|
|
981
|
+
* which the caller treats as "run the scan". Never throws.
|
|
982
|
+
* @param {string} cachePath - Path to the JSON file written by `saveDuplicationResult`.
|
|
983
|
+
* @param {string} digest - The current {@link duplicationDigest} to validate against.
|
|
984
|
+
* @param {string} paramsKey - The current {@link duplicationResultCacheKey} to validate against.
|
|
985
|
+
* @returns {{ groups: DuplicateGroup[]; clusters: DuplicateCluster[] } | null} The cached result,
|
|
986
|
+
* or `null` on any miss.
|
|
987
|
+
*/
|
|
988
|
+
declare function loadDuplicationResult(cachePath: string, digest: string, paramsKey: string): {
|
|
989
|
+
groups: DuplicateGroup[];
|
|
990
|
+
clusters: DuplicateCluster[];
|
|
991
|
+
} | null;
|
|
992
|
+
/**
|
|
993
|
+
* @description Serializes the full scan result to `cachePath` (creating parent dirs), stamped
|
|
994
|
+
* with the `digest` / `paramsKey` it is valid for. A write failure is logged to stderr and
|
|
995
|
+
* otherwise swallowed — the result cache is pure acceleration and must never fail the call
|
|
996
|
+
* that produced it.
|
|
997
|
+
* @param {string} cachePath - Destination path; parent directories are created automatically.
|
|
998
|
+
* @param {string} digest - The {@link duplicationDigest} this result was computed against.
|
|
999
|
+
* @param {string} paramsKey - The {@link duplicationResultCacheKey} this result was computed
|
|
1000
|
+
* against.
|
|
1001
|
+
* @param {readonly DuplicateGroup[]} groups - The full pre-`limit` group list from the scan.
|
|
1002
|
+
* @param {readonly DuplicateCluster[]} clusters - The full cluster list from the scan.
|
|
1003
|
+
* @returns {void}
|
|
1004
|
+
*/
|
|
1005
|
+
declare function saveDuplicationResult(cachePath: string, digest: string, paramsKey: string, groups: readonly DuplicateGroup[], clusters: readonly DuplicateCluster[]): void;
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Response-shaping helpers shared by the `find_duplicates` MCP handler and the `--find-duplicates`
|
|
1009
|
+
* CLI command: a triage-first `summary` block and the compact ("slim") group/cluster projections.
|
|
1010
|
+
* Kept separate from `index.ts` (the scan) and `dup-filter.ts` (the predicate) so both consumers
|
|
1011
|
+
* present duplicate results identically.
|
|
1012
|
+
*/
|
|
1013
|
+
|
|
1014
|
+
/** First path segment of a project-relative path (`"packages/app/x.ts"` -> `"packages"`), or
|
|
1015
|
+
* `"."` when the file sits at the repo root — the bucket key for `summary.byTopDir`. */
|
|
1016
|
+
declare function topDir(relPath: string): string;
|
|
1017
|
+
/** The triage-first `summary` block. */
|
|
1018
|
+
interface DuplicatesSummary {
|
|
1019
|
+
/** How many groups matched (post-`filter`, before any response `limit`). A lower bound when a
|
|
1020
|
+
* per-package scan cap clipped on a very large monorepo; exact otherwise. */
|
|
1021
|
+
matched: number;
|
|
1022
|
+
/** Group count per language `family`. */
|
|
1023
|
+
byFamily: Record<string, number>;
|
|
1024
|
+
/** Group count per top-level directory — a group touching two dirs counts in both. */
|
|
1025
|
+
byTopDir: Record<string, number>;
|
|
1026
|
+
/** Occurrence-signal frequency across the matched groups. */
|
|
1027
|
+
bySignal: Record<string, number>;
|
|
1028
|
+
/** Largest `lines` value among the matched groups. */
|
|
1029
|
+
largestLines: number;
|
|
1030
|
+
/** Only set on a `view: "full"` response: how many groups were dropped from the returned
|
|
1031
|
+
* `groups` list because a returned multi-member cluster already represents them (see
|
|
1032
|
+
* {@link dedupeGroupsAgainstClusters}). */
|
|
1033
|
+
clusteredGroups?: number;
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* @description Drops from `groups` every group that is a member of a multi-member cluster in
|
|
1037
|
+
* `clusters` — that cluster already represents it (with a better signal: per-file `coverage`),
|
|
1038
|
+
* so returning both is redundant. Groups whose file pair matched only once (no multi-member
|
|
1039
|
+
* cluster) are kept. Used only for the `view: "full"` response, the one path that returns both
|
|
1040
|
+
* lists.
|
|
1041
|
+
* @param {readonly DuplicateGroup[]} groups - The ordered, pre-`limit` group list.
|
|
1042
|
+
* @param {readonly DuplicateCluster[]} clusters - The clusters that will be returned alongside.
|
|
1043
|
+
* @returns {DuplicateGroup[]} `groups` minus the ones folded into a returned multi-member cluster.
|
|
1044
|
+
*/
|
|
1045
|
+
declare function dedupeGroupsAgainstClusters(groups: readonly DuplicateGroup[], clusters: readonly DuplicateCluster[]): DuplicateGroup[];
|
|
1046
|
+
/**
|
|
1047
|
+
* @description Builds the {@link DuplicatesSummary} for a `find_duplicates` response — the counts
|
|
1048
|
+
* a caller reads once to decide which targeted `filter` call to make next, without pulling the
|
|
1049
|
+
* full group list.
|
|
1050
|
+
* @param {readonly DuplicateGroup[]} groups - Every group that survived the `filter` predicate,
|
|
1051
|
+
* before the response `limit` truncates the list.
|
|
1052
|
+
* @returns {DuplicatesSummary} The aggregate counts.
|
|
1053
|
+
*/
|
|
1054
|
+
declare function summarizeDuplicates(groups: readonly DuplicateGroup[]): DuplicatesSummary;
|
|
1055
|
+
/** Compact `["path:startLine-endLine", …]` rendering of a group's occurrences. */
|
|
1056
|
+
declare function slimOccurrences(group: DuplicateGroup): string[];
|
|
1057
|
+
/**
|
|
1058
|
+
* @description Slim per-group projection: size / score / classification / signals plus
|
|
1059
|
+
* `"path:start-end"` occurrence strings — no duplicated source text, no per-occurrence
|
|
1060
|
+
* metadata objects. Preserves a `package` field when `findDuplicates`' caller tagged one.
|
|
1061
|
+
* @param {DuplicateGroup} group - A finalized duplicate group.
|
|
1062
|
+
* @returns {Record<string, unknown>} The compact object.
|
|
1063
|
+
*/
|
|
1064
|
+
declare function slimDupGroup(group: DuplicateGroup): Record<string, unknown>;
|
|
1065
|
+
/**
|
|
1066
|
+
* @description Slim per-cluster projection: the file set, match count, best-verified size,
|
|
1067
|
+
* per-file coverage, and the `"path:start-end"` span of the cluster's longest match — without
|
|
1068
|
+
* the nested member-group bodies. `longestMatchAt` is what makes a slim cluster actionable on
|
|
1069
|
+
* its own: it answers "where do I look" without the caller also pulling the `groups` list.
|
|
1070
|
+
* @param {DuplicateCluster} cluster - A built cluster. `cluster.groups` is largest-`lines`-first
|
|
1071
|
+
* (see `buildDuplicateClusters`), so `groups[0]` is the longest match.
|
|
1072
|
+
* @returns {Record<string, unknown>} The compact object.
|
|
1073
|
+
*/
|
|
1074
|
+
declare function slimDupCluster(cluster: DuplicateCluster): Record<string, unknown>;
|
|
1075
|
+
|
|
553
1076
|
/** Configures whether/how tokenizing is offloaded to a `piscina` worker pool. `false` always
|
|
554
1077
|
* tokenizes in-process. */
|
|
555
1078
|
type ParallelTokenizingOption = boolean | {
|
|
@@ -577,6 +1100,62 @@ interface FindDuplicatesOptions {
|
|
|
577
1100
|
/** Directory names to exclude, matched against any path segment (default `DEFAULT_IGNORE_DIRS`
|
|
578
1101
|
* — `node_modules`, `dist`, `.git`, `mokosh-cache`, `coverage`, etc.). Pass `[]` to disable. */
|
|
579
1102
|
ignoreDirs?: readonly string[] | undefined;
|
|
1103
|
+
/** When false (default), skip generated / vendored files — protobuf output, `*.generated.*`,
|
|
1104
|
+
* codegen basenames, files under a `generated/` segment, and files whose first ~500 bytes
|
|
1105
|
+
* carry a `@generated` / `DO NOT EDIT` / `Code generated by` marker. Their repetition is not
|
|
1106
|
+
* actionable copy-paste. Set true to scan them anyway; matches involving one are tagged
|
|
1107
|
+
* `signals: ["generated"]`. See docs/adr-013-duplicate-detection-noise-reduction.md. */
|
|
1108
|
+
includeGenerated?: boolean | undefined;
|
|
1109
|
+
/** When false (default), skip matches where every occurrence is in a single file — a file's
|
|
1110
|
+
* own naturally repetitive structure (a long class with many similar methods, a Markdown doc
|
|
1111
|
+
* table, a big object literal) shows up as "duplicating itself" far more often than it
|
|
1112
|
+
* represents an actionable copy-paste bug. Dogfooding found this was the single largest noise
|
|
1113
|
+
* class in `groups` (32% in one measurement). Set true to see them anyway; they're always
|
|
1114
|
+
* tagged `signals: ["same-file"]` regardless of this flag, so a caller already relying on that
|
|
1115
|
+
* tag to filter manually is unaffected either way. Mirrors `includeGenerated`'s
|
|
1116
|
+
* default-off/opt-in-back-in shape. */
|
|
1117
|
+
includeSameFile?: boolean | undefined;
|
|
1118
|
+
/** When false (default), skip matches where every occurrence's source span is predominantly
|
|
1119
|
+
* inline SVG / SVG-shaped JSX markup. Two mechanisms produce these: the token-shingle block
|
|
1120
|
+
* matcher (under the default `ignoreLiterals: true` the `d=` path string and filter constants
|
|
1121
|
+
* that actually tell two icons apart normalize to a placeholder, so *different* icons
|
|
1122
|
+
* token-match on their shared skeleton), and the `defKind: "jsxElement"` detector (two icons
|
|
1123
|
+
* sharing a byte-identical `<defs>`/`<filter>` block — boilerplate, not an authored clone).
|
|
1124
|
+
* Both are excluded here. Set true to see them anyway; they're always tagged
|
|
1125
|
+
* `signals: ["svg-markup"]` regardless. Mirrors `includeSameFile`'s default-off/opt-in shape.
|
|
1126
|
+
* See `svg-markup.ts`. */
|
|
1127
|
+
includeSvgMarkup?: boolean | undefined;
|
|
1128
|
+
/** When false (default), skip matches in the `markdown` family — prose docs (`README.md` ↔
|
|
1129
|
+
* `*.mdx`, `*.md` ↔ `*.md`) that mirror each other are not code duplication, and their
|
|
1130
|
+
* fenced code blocks tokenize densely enough to top the score-ranked list. Set true to see
|
|
1131
|
+
* them anyway; they're always tagged `signals: ["docs"]` regardless. Mirrors
|
|
1132
|
+
* `includeSameFile`'s default-off/opt-in shape. */
|
|
1133
|
+
includeDocs?: boolean | undefined;
|
|
1134
|
+
/** Minimum logic-token score (keywords + operators in the verified span — see
|
|
1135
|
+
* {@link significantTokenCount}) for a `kind: "block"` match to be reported. Default `0`
|
|
1136
|
+
* (off): every match is still returned, but `groups` and `clusters` are now *ranked* by score
|
|
1137
|
+
* regardless. A positive value drops boilerplate-shaped blocks — a Flow `type Props`, a JSX
|
|
1138
|
+
* icon-component wrapper, a schema object literal all score in the single digits; a real
|
|
1139
|
+
* 30-line function scores 25–40. Never filters `kind: "definition"` groups (already
|
|
1140
|
+
* content-verified). See docs/adr-019-logic-token-scoring.md. */
|
|
1141
|
+
minScore?: number | undefined;
|
|
1142
|
+
/** Which duplicates to surface, by whether test files are involved (default `"src"`). A group
|
|
1143
|
+
* with any occurrence in a test file (`__tests__/`, `__mocks__/`, `*.test.*`, `*.spec.*`,
|
|
1144
|
+
* `__snapshots__/`) is always tagged `signals: ["test"]`; this option decides which of those
|
|
1145
|
+
* reach `groups`/`clusters`:
|
|
1146
|
+
* - `"src"` (default) — drop every cluster that has a test-file occurrence. Auditing product
|
|
1147
|
+
* code shouldn't be buried under `render(<Icon/>); expect(…).toMatchSnapshot()` repeated
|
|
1148
|
+
* across hundreds of icon tests.
|
|
1149
|
+
* - `"tests"` — surface *only* test clusters, and only the substantive ones: a cluster with
|
|
1150
|
+
* ≥ {@link TEST_SUBSTANTIVE_BLOCKS} distinct shared blocks across ≤ {@link
|
|
1151
|
+
* TEST_SUBSTANTIVE_MAX_FILES} files, or one block ≥ {@link TEST_SUBSTANTIVE_TOKENS} tokens —
|
|
1152
|
+
* i.e. shared setup / mocks / assertions worth a helper, not a one-block render skeleton.
|
|
1153
|
+
* - `"all"` — every cluster, test or not, subject only to the other filters. */
|
|
1154
|
+
scope?: "src" | "tests" | "all" | undefined;
|
|
1155
|
+
/** Extra generated-file patterns merged with the built-in list (from
|
|
1156
|
+
* `MokoshConfig.duplication.ignoreGlobs`). Two shapes only — `**/name/**` (path segment) and
|
|
1157
|
+
* `*.suffix` (basename) — not full glob syntax. */
|
|
1158
|
+
ignoreGlobs?: readonly string[] | undefined;
|
|
580
1159
|
/** Controls worker-pool offloading of per-file tokenizing (default `true`): offloads once the
|
|
581
1160
|
* candidate file count reaches `minFiles` (default 20, matching `GraphBuilder`'s parse pool);
|
|
582
1161
|
* `false` always tokenizes in-process; an object overrides `minFiles`/`maxThreads`. See
|
|
@@ -587,10 +1166,33 @@ interface FindDuplicatesOptions {
|
|
|
587
1166
|
* call's) skip tokenizing entirely. Mutated in place; omit for one-shot callers (e.g. the CLI).
|
|
588
1167
|
* See {@link DuplicationTokenCache} and docs/adr-014-duplicate-detection-scale.md. */
|
|
589
1168
|
tokenCache?: DuplicationTokenCache | undefined;
|
|
1169
|
+
/** Optional `key:value` result filter (see `src/query/dup-parser.ts` / `DuplicateQuery`).
|
|
1170
|
+
* Applied as a per-group predicate *before* clustering, so `clusters` stay consistent with the
|
|
1171
|
+
* narrowed `groups`. Only the predicate keys act here — the DSL's `sort`/`limit` are the
|
|
1172
|
+
* caller's to apply (via `sortLimitDupGroups`), since on a monorepo `findDuplicates` runs
|
|
1173
|
+
* once per package and a global order/cap can only be decided after the per-package results
|
|
1174
|
+
* merge. A parse error in the string is thrown, not swallowed. */
|
|
1175
|
+
filter?: string | undefined;
|
|
590
1176
|
}
|
|
591
1177
|
interface FindDuplicatesResult {
|
|
592
1178
|
/** Duplicate blocks, largest-first, capped at `limit`. */
|
|
593
1179
|
groups: DuplicateGroup[];
|
|
1180
|
+
/** `groups` (before `limit` truncation) bucketed by *exact* occurrence file set — every group
|
|
1181
|
+
* is merged into one cluster with every other group whose occurrences touch the identical set
|
|
1182
|
+
* of files, so N separate non-nested matches between the same two files (the "window-splitting"
|
|
1183
|
+
* case, see docs/known_issues/09-duplicate-clone-family-noise.md) read as one `matchCount: N`
|
|
1184
|
+
* entry instead of N rows a caller has to manually recognize as the same underlying
|
|
1185
|
+
* duplication. Deliberately *not* transitive across partially-overlapping file sets (a group
|
|
1186
|
+
* over `{A, B}` and one over `{B, C}` land in separate clusters) — an earlier connected-
|
|
1187
|
+
* component version was chaining unrelated files through shared bridge files into one
|
|
1188
|
+
* incomprehensible supercluster on real repos; see `src/graph/duplication/clusters.ts`'s
|
|
1189
|
+
* top-of-file comment. Largest-`longestMatch`-first, capped at `limit` clusters (each cluster's
|
|
1190
|
+
* own `groups` are never truncated). No group is dropped or altered to build this — every group
|
|
1191
|
+
* in `groups` (subject to `limit`) also appears inside exactly one cluster here. Each cluster
|
|
1192
|
+
* also carries per-file duplication `coverage` — merged occurrence spans divided by that
|
|
1193
|
+
* file's total line count — so `matchCount: 14` reads as "62% of this file" rather than just
|
|
1194
|
+
* a row count; see `src/graph/duplication/clusters.ts`'s `DuplicateClusterFileCoverage`. */
|
|
1195
|
+
clusters: DuplicateCluster[];
|
|
594
1196
|
}
|
|
595
1197
|
/**
|
|
596
1198
|
* @description Scans every file already present in `graph` for cross-file (and within-file)
|
|
@@ -602,8 +1204,11 @@ interface FindDuplicatesResult {
|
|
|
602
1204
|
* identifiers (and, by default, literals) are normalized to placeholders so renamed-variable
|
|
603
1205
|
* copies still match, then a sliding token window is hashed and chain-merged into contiguous
|
|
604
1206
|
* blocks. Token-shingled files are additionally partitioned into language families
|
|
605
|
-
* ({@link getDuplicateFamily} — `"
|
|
606
|
-
*
|
|
1207
|
+
* ({@link getDuplicateFamily} — `"js"` for JS/TS/Coffee/LS, `"jvm"` for Java/Kotlin/Scala/
|
|
1208
|
+
* Groovy, `"style"` for Stylus, and one family each for Python, Go, Lua, Markdown, Gherkin) so
|
|
1209
|
+
* matching never crosses a family boundary — a `for` loop tokenizes almost identically in Java,
|
|
1210
|
+
* Go and TS, so a single `"code"` family produced phantom cross-language matches on polyglot
|
|
1211
|
+
* repos. See docs/adr-013-duplicate-detection-noise-reduction.md.
|
|
607
1212
|
* @param graph - The graph to scan; its node paths (already ignore-rule-filtered) are the file
|
|
608
1213
|
* list, re-read from disk since duplication data isn't cached on `FileNode`.
|
|
609
1214
|
* @param rootDir - Absolute project root that graph paths are relative to.
|
|
@@ -612,13 +1217,24 @@ interface FindDuplicatesResult {
|
|
|
612
1217
|
* token-shingle path only (CSS/Less/SCSS always match on literal declaration content);
|
|
613
1218
|
* `maxPunctuationRatio` gates out token-shingle blocks that are mostly object/array-literal
|
|
614
1219
|
* structural punctuation (e.g. schema/object-literal boilerplate) rather than substantive
|
|
615
|
-
* shared logic; `ignoreDirs` excludes files under matching directory names; `
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
1220
|
+
* shared logic; `ignoreDirs` excludes files under matching directory names; `includeSameFile`
|
|
1221
|
+
* controls whether same-file-only matches are returned (excluded by default — see its doc
|
|
1222
|
+
* comment); `includeSvgMarkup` likewise controls whether inline-SVG-markup matches are returned
|
|
1223
|
+
* (excluded by default — two different icons share a literal-normalized skeleton); `scope`
|
|
1224
|
+
* filters whole clusters by test-file involvement — `"src"` (default) drops every test cluster,
|
|
1225
|
+
* `"tests"` returns only the substantive ones, `"all"` returns everything; `filter` is an
|
|
1226
|
+
* optional `key:value` DSL string (see `DuplicateQuery`) applied as a per-group predicate
|
|
1227
|
+
* before clustering; `limit` caps results; `parallelTokenizing` offloads per-file tokenizing
|
|
1228
|
+
* to a worker pool once the candidate file count is large enough to be worth it. Lock files and
|
|
1229
|
+
* `type: "unknown"` nodes (non-code assets like `.svg`/`.json` pulled in via an explicit
|
|
1230
|
+
* `import`) are always excluded, independent of `ignoreDirs`.
|
|
619
1231
|
* @returns `groups` — duplicate blocks (each tagged with its `family`), two or more occurrences
|
|
620
1232
|
* per block, every block that pairwise chain-matches another clustered into one group instead
|
|
621
|
-
* of one per pair, sorted largest-first across all families
|
|
1233
|
+
* of one per pair, sorted largest-first across all families (same-file-only and svg-markup
|
|
1234
|
+
* matches excluded unless `includeSameFile` / `includeSvgMarkup`; test matches filtered per
|
|
1235
|
+
* `scope`). `clusters` — the same groups
|
|
1236
|
+
* bucketed by exact file set, with
|
|
1237
|
+
* per-file duplication coverage, see {@link FindDuplicatesResult.clusters}.
|
|
622
1238
|
*/
|
|
623
1239
|
declare function findDuplicates(graph: Graph, rootDir: string, options?: FindDuplicatesOptions): Promise<FindDuplicatesResult>;
|
|
624
1240
|
|
|
@@ -920,19 +1536,48 @@ interface MonorepoDetector {
|
|
|
920
1536
|
*/
|
|
921
1537
|
declare function registerMonorepoDetector(detector: MonorepoDetector): void;
|
|
922
1538
|
|
|
923
|
-
/**
|
|
924
|
-
* @description Runs all registered monorepo detectors against `rootDir` and merges
|
|
925
|
-
* their results into a single `MonorepoLayout`. All matching detectors contribute
|
|
926
|
-
* their `type` string and packages — so a Turborepo + pnpm repo will have
|
|
927
|
-
* `types: ["turborepo", "pnpm"]` and packages from the pnpm detector.
|
|
928
|
-
*
|
|
929
|
-
* Packages are deduplicated by name: the first detector to emit a package name wins.
|
|
930
|
-
* Returns `type: "none"` when no detector fires.
|
|
931
|
-
*/
|
|
932
|
-
declare function detectMonorepo(rootDir: string, detectors?: readonly MonorepoDetector[]): MonorepoLayout;
|
|
933
|
-
|
|
934
1539
|
/** WorkspaceGraph holds one per-package Graph for a monorepo and exposes cross-package blast-radius queries. */
|
|
935
1540
|
|
|
1541
|
+
/**
|
|
1542
|
+
* @description Whether `relPath` falls under `pkg`'s own `relativeRoot` — i.e. the package owns
|
|
1543
|
+
* that file, as opposed to merely importing it across a package boundary. Each per-package
|
|
1544
|
+
* `Graph` also carries "borrowed" nodes for cross-package files it imports (so outward
|
|
1545
|
+
* traversal reaches them); this predicate is how callers that iterate every package's graph
|
|
1546
|
+
* avoid double-counting a file that two packages both import.
|
|
1547
|
+
* @param {string} relPath - A monorepo-root-relative file path.
|
|
1548
|
+
* @param {Pick<WorkspacePackage, "relativeRoot">} pkg - The package to test ownership against.
|
|
1549
|
+
* @returns {boolean} `true` if `relPath` is `pkg.relativeRoot` or sits beneath it.
|
|
1550
|
+
*/
|
|
1551
|
+
declare function packageOwnsFile(relPath: string, pkg: Pick<WorkspacePackage, "relativeRoot">): boolean;
|
|
1552
|
+
/** @description A whole-workspace `Graph` (one namespace, all packages' own nodes, cross-package
|
|
1553
|
+
* edges intact) paired with a path → owning-package-name lookup. Produced by
|
|
1554
|
+
* {@link WorkspaceGraph.flatten}. */
|
|
1555
|
+
interface FlatWorkspaceGraph {
|
|
1556
|
+
graph: Graph;
|
|
1557
|
+
packageOf: Map<string, string>;
|
|
1558
|
+
}
|
|
1559
|
+
/** @description Compact, capped cross-package blast radius — the response shape of
|
|
1560
|
+
* {@link WorkspaceGraph.summarizeAffectedAcrossPackages}. */
|
|
1561
|
+
interface WorkspaceAffectedSummary {
|
|
1562
|
+
/** The changed file the blast radius was computed for. */
|
|
1563
|
+
file: string;
|
|
1564
|
+
/** Total affected files across every package (the real count, never capped). */
|
|
1565
|
+
totalAffected: number;
|
|
1566
|
+
/** Number of distinct packages with at least one affected file. */
|
|
1567
|
+
packageCount: number;
|
|
1568
|
+
/** Per-package breakdown, sorted by `count` descending. */
|
|
1569
|
+
byPackage: Array<{
|
|
1570
|
+
package: string;
|
|
1571
|
+
/** Real affected-file count for this package. */
|
|
1572
|
+
count: number;
|
|
1573
|
+
/** Up to `maxFilesPerPackage` example paths. */
|
|
1574
|
+
sample: string[];
|
|
1575
|
+
/** `count - sample.length` — files omitted from `sample`. */
|
|
1576
|
+
more: number;
|
|
1577
|
+
}>;
|
|
1578
|
+
/** `true` if any package's `sample` was capped (`more > 0` somewhere). */
|
|
1579
|
+
truncated: boolean;
|
|
1580
|
+
}
|
|
936
1581
|
/** @description JSON-safe snapshot of a `WorkspaceGraph`, suitable for writing to disk and restoring via `WorkspaceGraph.deserialize`. */
|
|
937
1582
|
interface SerializedWorkspaceGraph {
|
|
938
1583
|
monorepoRoot: string;
|
|
@@ -955,6 +1600,9 @@ declare class WorkspaceGraph {
|
|
|
955
1600
|
graph: Graph;
|
|
956
1601
|
pkg: WorkspacePackage;
|
|
957
1602
|
}>;
|
|
1603
|
+
/** Lazily-built merged view; see {@link flatten}. Never invalidated — a rebuild replaces the
|
|
1604
|
+
* whole `WorkspaceGraph` instance rather than mutating this one. */
|
|
1605
|
+
private flattened?;
|
|
958
1606
|
/**
|
|
959
1607
|
* @param {string} monorepoRoot - Absolute path to the monorepo root directory.
|
|
960
1608
|
* @param {string} type - Primary detected monorepo tool (e.g. `"turborepo"`, `"pnpm"`), or `"none"`.
|
|
@@ -977,6 +1625,19 @@ declare class WorkspaceGraph {
|
|
|
977
1625
|
* Call once after all packages are registered.
|
|
978
1626
|
*/
|
|
979
1627
|
annotateCrossPackageEdges(): void;
|
|
1628
|
+
/**
|
|
1629
|
+
* @description Merges every package's own nodes into a single `Graph` sharing one path
|
|
1630
|
+
* namespace, plus a `path → package name` lookup. "Borrowed" cross-package nodes are
|
|
1631
|
+
* dropped (each is contributed by its owning package instead), so no file appears twice.
|
|
1632
|
+
* Cross-package import edges already carry real monorepo-root-relative `toPath`s, so
|
|
1633
|
+
* `Graph.traverse` and `Graph.findCycles` span package boundaries on the returned graph
|
|
1634
|
+
* with no special-casing — this is the basis for whole-workspace blast radius, call
|
|
1635
|
+
* graphs, symbol search and queries. The graph is read-only: `FileNode`s are shared by
|
|
1636
|
+
* reference with the per-package graphs, never cloned.
|
|
1637
|
+
* Result is memoized for the life of this `WorkspaceGraph`.
|
|
1638
|
+
* @returns {FlatWorkspaceGraph} The merged graph and its package lookup.
|
|
1639
|
+
*/
|
|
1640
|
+
flatten(): FlatWorkspaceGraph;
|
|
980
1641
|
/**
|
|
981
1642
|
* @description Returns the workspace package whose `relativeRoot` is a path prefix of `relPath`.
|
|
982
1643
|
* @param {string} relPath - A monorepo-root-relative file path to look up.
|
|
@@ -991,9 +1652,10 @@ declare class WorkspaceGraph {
|
|
|
991
1652
|
getPackageDependencies(): Map<string, string[]>;
|
|
992
1653
|
/**
|
|
993
1654
|
* @description Cross-package blast-radius analysis. Returns every file (with its package name)
|
|
994
|
-
*
|
|
995
|
-
*
|
|
996
|
-
*
|
|
1655
|
+
* transitively affected if the given monorepo-root-relative path changes — a full incoming
|
|
1656
|
+
* traversal over the flattened whole-workspace graph, so it follows real import edges across
|
|
1657
|
+
* package boundaries. The full, unbounded list: on a large monorepo this can be most of the
|
|
1658
|
+
* repo, so prefer {@link summarizeAffectedAcrossPackages} for an agent-facing response.
|
|
997
1659
|
* @param {string} relPath - Monorepo-root-relative path of the changed file.
|
|
998
1660
|
* @returns {Array<{ file: string; package: string }>} Each affected file paired with its package name.
|
|
999
1661
|
*/
|
|
@@ -1001,6 +1663,19 @@ declare class WorkspaceGraph {
|
|
|
1001
1663
|
file: string;
|
|
1002
1664
|
package: string;
|
|
1003
1665
|
}>;
|
|
1666
|
+
/**
|
|
1667
|
+
* @description Agent-friendly form of {@link getAffectedAcrossPackages}: the blast radius
|
|
1668
|
+
* grouped by owning package, with each package's file list capped so the response stays
|
|
1669
|
+
* small even when thousands of files are affected. Packages are sorted by affected count
|
|
1670
|
+
* (descending).
|
|
1671
|
+
* @param {string} relPath - Monorepo-root-relative path of the changed file.
|
|
1672
|
+
* @param {{ maxFilesPerPackage?: number }} [opts] - `maxFilesPerPackage` caps each package's
|
|
1673
|
+
* `sample` list (default 10; `0` = counts only, no file lists).
|
|
1674
|
+
* @returns {WorkspaceAffectedSummary} Totals plus a capped per-package breakdown.
|
|
1675
|
+
*/
|
|
1676
|
+
summarizeAffectedAcrossPackages(relPath: string, opts?: {
|
|
1677
|
+
maxFilesPerPackage?: number;
|
|
1678
|
+
}): WorkspaceAffectedSummary;
|
|
1004
1679
|
/**
|
|
1005
1680
|
* @description Serializes the workspace graph to a plain JSON-safe object.
|
|
1006
1681
|
* `root` is omitted from package entries as it is not needed after build time.
|
|
@@ -1016,6 +1691,47 @@ declare class WorkspaceGraph {
|
|
|
1016
1691
|
static deserialize(data: SerializedWorkspaceGraph): WorkspaceGraph;
|
|
1017
1692
|
}
|
|
1018
1693
|
|
|
1694
|
+
interface WorkspaceLayoutPackageSummary {
|
|
1695
|
+
name: string;
|
|
1696
|
+
relativeRoot: string;
|
|
1697
|
+
/** Sibling workspace packages this one depends on. Exact when `dependsOnResolved`, best-effort otherwise. */
|
|
1698
|
+
dependsOn: string[];
|
|
1699
|
+
/** Present only when a built workspace graph was available. */
|
|
1700
|
+
nodeCount?: number;
|
|
1701
|
+
}
|
|
1702
|
+
interface WorkspaceLayoutSummary {
|
|
1703
|
+
monorepoType: string;
|
|
1704
|
+
monorepoTypes: string[];
|
|
1705
|
+
packageCount: number;
|
|
1706
|
+
packages: WorkspaceLayoutPackageSummary[];
|
|
1707
|
+
/** `true` when `dependsOn` came from a built graph or every package exposed a manifest. */
|
|
1708
|
+
dependsOnResolved: boolean;
|
|
1709
|
+
/** `true` when per-package `nodeCount` is populated (a built graph was available). */
|
|
1710
|
+
nodeCountsResolved: boolean;
|
|
1711
|
+
note?: string;
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* @description Summarizes a monorepo from its detected layout alone — package names, relative
|
|
1715
|
+
* roots, and best-effort `dependsOn` from `package.json` manifests — without building any
|
|
1716
|
+
* dependency graph. When a built `WorkspaceGraph` is supplied, its exact per-package node
|
|
1717
|
+
* counts and cross-package edges are used instead.
|
|
1718
|
+
* @param layout - The result of `detectMonorepo`. Must not be `type: "none"`.
|
|
1719
|
+
* @param builtGraph - An already-built workspace graph for `layout.root`, if one is cached.
|
|
1720
|
+
* @returns A layout summary suitable for the `get_workspace_packages` response.
|
|
1721
|
+
*/
|
|
1722
|
+
declare function summarizeWorkspaceLayout(layout: MonorepoLayout, builtGraph?: WorkspaceGraph): WorkspaceLayoutSummary;
|
|
1723
|
+
|
|
1724
|
+
/**
|
|
1725
|
+
* @description Runs all registered monorepo detectors against `rootDir` and merges
|
|
1726
|
+
* their results into a single `MonorepoLayout`. All matching detectors contribute
|
|
1727
|
+
* their `type` string and packages — so a Turborepo + pnpm repo will have
|
|
1728
|
+
* `types: ["turborepo", "pnpm"]` and packages from the pnpm detector.
|
|
1729
|
+
*
|
|
1730
|
+
* Packages are deduplicated by name: the first detector to emit a package name wins.
|
|
1731
|
+
* Returns `type: "none"` when no detector fires.
|
|
1732
|
+
*/
|
|
1733
|
+
declare function detectMonorepo(rootDir: string, detectors?: readonly MonorepoDetector[]): MonorepoLayout;
|
|
1734
|
+
|
|
1019
1735
|
/**
|
|
1020
1736
|
* @description Contract for graph serializers. Implement this to add a new export format
|
|
1021
1737
|
* (e.g. Graphviz DOT, JSON, SVG) without touching the core graph model.
|
|
@@ -1136,6 +1852,7 @@ interface SlimNode {
|
|
|
1136
1852
|
exports: string[];
|
|
1137
1853
|
tags: string[];
|
|
1138
1854
|
importsFiles: string[];
|
|
1855
|
+
package?: string;
|
|
1139
1856
|
description?: string;
|
|
1140
1857
|
testedBy?: string[];
|
|
1141
1858
|
coveragePct?: number;
|
|
@@ -1182,6 +1899,14 @@ declare function hasCoverageData(graph: Graph): boolean;
|
|
|
1182
1899
|
* @returns Whether any node has a defined `commitCount90d`.
|
|
1183
1900
|
*/
|
|
1184
1901
|
declare function hasChurnData(graph: Graph): boolean;
|
|
1902
|
+
/**
|
|
1903
|
+
* @description Returns `true` if at least one node has a last-commit timestamp — i.e. `analyze`
|
|
1904
|
+
* ran with `gitStats` enabled. `check_doc_drift` needs this: without it `enrichDocDrift`
|
|
1905
|
+
* populates no `staleFor`, so an empty result would be indistinguishable from "no drift".
|
|
1906
|
+
* @param graph - The graph to check.
|
|
1907
|
+
* @returns Whether any node has a defined `lastCommitAt`.
|
|
1908
|
+
*/
|
|
1909
|
+
declare function hasGitTimestampData(graph: Graph): boolean;
|
|
1185
1910
|
interface RiskHotspotEntry {
|
|
1186
1911
|
file: string;
|
|
1187
1912
|
name: string;
|
|
@@ -1228,6 +1953,13 @@ interface BuildGraphAtRefOptions {
|
|
|
1228
1953
|
pathAliases?: Record<string, string[]> | undefined;
|
|
1229
1954
|
/** Extra directory names to skip during test/doc discovery, on top of the built-in list. Sourced from `MokoshConfig.ignoreDirs`. */
|
|
1230
1955
|
additionalIgnoreDirs?: string[] | undefined;
|
|
1956
|
+
/** Monorepo roots only. When set, the base-ref graph is built by calling this with the
|
|
1957
|
+
* worktree directory — the caller builds a whole `WorkspaceGraph` there and returns its
|
|
1958
|
+
* flattened `Graph`, so cross-package edges survive. Bypasses the entry-point-seeded
|
|
1959
|
+
* `GraphBuilder` path (which has no `workspaceMap` and would miss `@org/*` imports).
|
|
1960
|
+
* Injected rather than imported to keep `compare.ts` free of the `createWorkspaceGraph`
|
|
1961
|
+
* dependency cycle. */
|
|
1962
|
+
workspaceBuilder?: ((worktreeDir: string) => Promise<Graph>) | undefined;
|
|
1231
1963
|
}
|
|
1232
1964
|
/**
|
|
1233
1965
|
* @description Builds the dependency graph as it existed at `ref`, rather than the current
|
|
@@ -1412,12 +2144,89 @@ declare const EXPORT_TRACKING_TYPES: ReadonlySet<FileType>;
|
|
|
1412
2144
|
declare const IMPORT_SYMBOL_TYPES: ReadonlySet<FileType>;
|
|
1413
2145
|
/** File types whose parser records function-level call edges (`FileNode.callEdges`). */
|
|
1414
2146
|
declare const CALL_EDGE_TYPES: ReadonlySet<FileType>;
|
|
2147
|
+
/** File types whose parser populates the per-function complexity breakdown (`FileNode.functions`)
|
|
2148
|
+
* plus file-level `complexity` / `cognitiveComplexity`. */
|
|
2149
|
+
declare const FUNCTION_COMPLEXITY_TYPES: ReadonlySet<FileType>;
|
|
2150
|
+
/** File types with a dedicated test-tag strategy (`src/tags/strategies/`) — a framework-aware
|
|
2151
|
+
* applier, not the generic path-glob fallback. */
|
|
2152
|
+
declare const TEST_TAG_STRATEGY_TYPES: ReadonlySet<FileType>;
|
|
2153
|
+
/** File types the type graph (`buildTypeGraph`) can extract interfaces/classes/enums/aliases from. */
|
|
2154
|
+
declare const TYPE_GRAPH_TYPES: ReadonlySet<FileType>;
|
|
2155
|
+
/** A graph-analysis capability that only some languages' parsers feed. */
|
|
2156
|
+
type LanguageFeature = "callEdges" | "functionComplexity" | "typeGraph";
|
|
2157
|
+
/**
|
|
2158
|
+
* @description Returns an explanatory note when none of the graphs contain a language whose
|
|
2159
|
+
* parser feeds `feature` — so an empty tool result (`count: 0`) reads as "language not
|
|
2160
|
+
* supported" rather than "nothing found". Returns `undefined` when at least one supported-
|
|
2161
|
+
* language file is present (the empty result is then genuine).
|
|
2162
|
+
* @param graphs - One graph, or the per-package graphs of a workspace.
|
|
2163
|
+
* @param feature - The capability the calling tool depends on.
|
|
2164
|
+
* @returns A one-sentence note, or `undefined`.
|
|
2165
|
+
*/
|
|
2166
|
+
declare function languageSupportNote(graphs: Graph | Graph[], feature: LanguageFeature): string | undefined;
|
|
2167
|
+
/** How completely a single analysis axis is implemented for one language:
|
|
2168
|
+
* - `"full"` — implemented with language-aware handling; results are call/symbol-level accurate.
|
|
2169
|
+
* - `"partial"` — implemented but known-lossy (index-based resolution, module-level exports,
|
|
2170
|
+
* constructor-only call edges, heuristic categories, the generic token duplicate pipeline).
|
|
2171
|
+
* - `"none"` — not implemented, or not applicable to this language. */
|
|
2172
|
+
type FidelityLevel = "full" | "partial" | "none";
|
|
2173
|
+
/** Per-language fidelity across every precision-relevant analysis axis. The four axes backed by
|
|
2174
|
+
* a `*_TYPES` set above are kept in exact sync with it by a test; the rest are a maintained
|
|
2175
|
+
* judgement, cross-checked against `docs/language-support.md`. */
|
|
2176
|
+
interface LanguageFidelity {
|
|
2177
|
+
/** Raw import/require specifiers → resolved graph edges. */
|
|
2178
|
+
importResolution: FidelityLevel;
|
|
2179
|
+
/** `FileNode.exports` populated with named symbols. Backed by {@link EXPORT_TRACKING_TYPES}. */
|
|
2180
|
+
exportSymbols: FidelityLevel;
|
|
2181
|
+
/** `ImportEdge.symbols` — which names each import pulls in. Backed by {@link IMPORT_SYMBOL_TYPES}. */
|
|
2182
|
+
importSymbols: FidelityLevel;
|
|
2183
|
+
/** Function-level call edges (`FileNode.callEdges`). Backed by {@link CALL_EDGE_TYPES}. */
|
|
2184
|
+
callEdges: FidelityLevel;
|
|
2185
|
+
/** Per-function + file-level complexity. Backed by {@link FUNCTION_COMPLEXITY_TYPES}. */
|
|
2186
|
+
complexity: FidelityLevel;
|
|
2187
|
+
/** Accuracy of the `logic`/`ui`/`test`/`config`/… category classification. */
|
|
2188
|
+
category: FidelityLevel;
|
|
2189
|
+
/** Duplicate detection: `"full"` = language-aware structural comparator (CSS family),
|
|
2190
|
+
* `"partial"` = the generic cross-language token pipeline (the norm — works, no language
|
|
2191
|
+
* semantics), `"none"` = not scanned. */
|
|
2192
|
+
duplication: FidelityLevel;
|
|
2193
|
+
/** A framework-aware test-tag strategy. Backed by {@link TEST_TAG_STRATEGY_TYPES}. */
|
|
2194
|
+
testTags: FidelityLevel;
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Authoritative per-language fidelity matrix — see `docs/language-support.md` for the same table
|
|
2198
|
+
* with per-language known-limitations prose and ADR links, and `docs/known_issues/08-cross-language-reliability.md`
|
|
2199
|
+
* for the plan this closes the first slice of. Every {@link FileType} has an entry.
|
|
2200
|
+
*/
|
|
2201
|
+
declare const LANGUAGE_FIDELITY: Record<FileType, LanguageFidelity>;
|
|
2202
|
+
/**
|
|
2203
|
+
* @description Returns one advisory sentence per language present in `graphs` whose `axis`
|
|
2204
|
+
* fidelity is `"partial"` or `"none"` — so a tool whose result is real but *lossy* for the
|
|
2205
|
+
* languages in play (e.g. `get_call_graph` on a Java repo: constructors only) can say so
|
|
2206
|
+
* instead of returning a confident-looking result. Complements {@link languageSupportNote},
|
|
2207
|
+
* which only fires when the result is fully empty *and* no supported-language file is present.
|
|
2208
|
+
* @param graphs - One graph, or the per-package graphs of a workspace.
|
|
2209
|
+
* @param axis - The fidelity axis the calling tool depends on.
|
|
2210
|
+
* @returns Sorted, de-duplicated caveat sentences; empty when every present language is `"full"`
|
|
2211
|
+
* for `axis` (or the only files are markdown/unknown).
|
|
2212
|
+
*/
|
|
2213
|
+
declare function languageCaveats(graphs: Graph | Graph[], axis: keyof LanguageFidelity): string[];
|
|
2214
|
+
/**
|
|
2215
|
+
* @description Aggregates {@link languageCaveats} across the precision-relevant axes
|
|
2216
|
+
* ({@link SUMMARY_AXES}) for an `analyze` response, so a caller sees upfront — from the first
|
|
2217
|
+
* call — where results for this repo's languages will be lossy.
|
|
2218
|
+
* @param graphs - One graph, or the per-package graphs of a workspace.
|
|
2219
|
+
* @returns Sorted, de-duplicated caveat sentences; empty for an all-`full` (e.g. all-TS) repo.
|
|
2220
|
+
*/
|
|
2221
|
+
declare function languageCaveatsSummary(graphs: Graph | Graph[]): string[];
|
|
1415
2222
|
interface LanguageCoverage {
|
|
1416
2223
|
type: FileType;
|
|
1417
2224
|
fileCount: number;
|
|
1418
2225
|
exportsTracked: boolean;
|
|
1419
2226
|
importSymbolsTracked: boolean;
|
|
1420
2227
|
callEdgesTracked: boolean;
|
|
2228
|
+
/** The full per-axis fidelity for this language — see {@link LANGUAGE_FIDELITY}. */
|
|
2229
|
+
fidelity: LanguageFidelity;
|
|
1421
2230
|
}
|
|
1422
2231
|
/**
|
|
1423
2232
|
* @description Reports which precision-relevant data mokosh actually tracks for each language
|
|
@@ -1464,6 +2273,90 @@ interface SymbolMatch {
|
|
|
1464
2273
|
*/
|
|
1465
2274
|
declare function findSymbol(graph: Graph, name: string): SymbolMatch[];
|
|
1466
2275
|
|
|
2276
|
+
/** Tag kinds worth surfacing for `tag:<name>` querying by default — the shared
|
|
2277
|
+
* {@link SELECTION_TAG_KINDS} (`comment-marker` + `import`). The other kinds (`function` /
|
|
2278
|
+
* `variable` / `library`) are declaration/dependency names, not test-selection labels. */
|
|
2279
|
+
declare const SUMMARY_TAG_KINDS: readonly TagKind[];
|
|
2280
|
+
/** Default `minCount` floor — drops the single-occurrence long tail. */
|
|
2281
|
+
declare const DEFAULT_TAG_MIN_COUNT = 2;
|
|
2282
|
+
/** Default `limit` when the caller gives none. 50 count-ranked tags ≈ ~1K tokens and covers
|
|
2283
|
+
* the discovery need; the tail past it is uniformly low-count. */
|
|
2284
|
+
declare const DEFAULT_TAG_LIMIT = 50;
|
|
2285
|
+
/** Absolute ceiling on the returned `tags` array. `limit` is clamped to this; no argument
|
|
2286
|
+
* combination can exceed it. ~250 `{name,count,kinds}` entries ≈ ~1.5K tokens. */
|
|
2287
|
+
declare const TAG_RESPONSE_HARD_CAP = 250;
|
|
2288
|
+
/** One distinct tag name, aggregated across every kind and node it appears on. */
|
|
2289
|
+
interface TagAggregateEntry {
|
|
2290
|
+
name: string;
|
|
2291
|
+
/** Total node occurrences, summed across every kind this name appears with. */
|
|
2292
|
+
count: number;
|
|
2293
|
+
/** Distinct kinds seen for this name, in `TagKind` declaration order. */
|
|
2294
|
+
kinds: TagKind[];
|
|
2295
|
+
}
|
|
2296
|
+
/** The full tag inventory for one or more graphs, before any filtering. */
|
|
2297
|
+
interface TagInventory {
|
|
2298
|
+
/** name → aggregate, first-seen insertion order. */
|
|
2299
|
+
entries: Map<string, TagAggregateEntry>;
|
|
2300
|
+
/** Distinct tag *names* per kind (a name carrying two kinds counts once under each). */
|
|
2301
|
+
byKind: Partial<Record<TagKind, number>>;
|
|
2302
|
+
/** `entries.size` — distinct tag names across all kinds. */
|
|
2303
|
+
totalDistinct: number;
|
|
2304
|
+
}
|
|
2305
|
+
/** Knobs for {@link summarizeTagInventory}. Each may be `undefined` (unset) so callers can
|
|
2306
|
+
* forward optional args straight through under `exactOptionalPropertyTypes`. */
|
|
2307
|
+
interface TagSummaryOptions {
|
|
2308
|
+
/** Restrict to one kind, or `"all"` for every kind. Default: {@link SUMMARY_TAG_KINDS}. */
|
|
2309
|
+
kind?: TagKind | "all" | undefined;
|
|
2310
|
+
/** Case-insensitive substring match on the tag name. */
|
|
2311
|
+
prefix?: string | undefined;
|
|
2312
|
+
/** Minimum node count for a tag to appear. Default: {@link DEFAULT_TAG_MIN_COUNT}. */
|
|
2313
|
+
minCount?: number | undefined;
|
|
2314
|
+
/** Max tags returned. Default: {@link DEFAULT_TAG_LIMIT}; hard-capped at
|
|
2315
|
+
* {@link TAG_RESPONSE_HARD_CAP}. */
|
|
2316
|
+
limit?: number | undefined;
|
|
2317
|
+
}
|
|
2318
|
+
/** The bounded `list_tags` response. */
|
|
2319
|
+
interface TagSummary {
|
|
2320
|
+
/** Filtered, sorted (count desc, then name asc), capped. */
|
|
2321
|
+
tags: Array<{
|
|
2322
|
+
name: string;
|
|
2323
|
+
count: number;
|
|
2324
|
+
kinds: TagKind[];
|
|
2325
|
+
}>;
|
|
2326
|
+
/** `tags.length` after the cap. */
|
|
2327
|
+
count: number;
|
|
2328
|
+
/** Names passing the `kind` + `prefix` + `minCount` filter, before the cap. */
|
|
2329
|
+
matched: number;
|
|
2330
|
+
/** Distinct tag names across ALL kinds, before any filter. */
|
|
2331
|
+
totalDistinct: number;
|
|
2332
|
+
/** Distinct names per kind over the FULL inventory (always every kind). */
|
|
2333
|
+
byKind: Partial<Record<TagKind, number>>;
|
|
2334
|
+
/** Set when `matched > count` (the cap or `limit` clipped the list). */
|
|
2335
|
+
truncated?: true;
|
|
2336
|
+
/** How to narrow further. */
|
|
2337
|
+
hint: string;
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* @description Aggregates every tag on every node of the given graphs into one inventory:
|
|
2341
|
+
* per-name occurrence count, the set of kinds each name carries, and a `byKind` histogram
|
|
2342
|
+
* of distinct names per kind. Replaces the ad-hoc count loop the MCP handler and CLI command
|
|
2343
|
+
* each used to run.
|
|
2344
|
+
* @param {readonly Graph[]} graphs - Graphs to scan (one for a single package, many for a
|
|
2345
|
+
* workspace fan-out).
|
|
2346
|
+
* @returns {TagInventory} The full, unfiltered inventory.
|
|
2347
|
+
*/
|
|
2348
|
+
declare function buildTagInventory(graphs: readonly Graph[]): TagInventory;
|
|
2349
|
+
/**
|
|
2350
|
+
* @description Filters, sorts and caps a {@link TagInventory} into the bounded `list_tags`
|
|
2351
|
+
* response. The returned `tags` array never exceeds {@link TAG_RESPONSE_HARD_CAP}, whatever
|
|
2352
|
+
* the options; `byKind` and `totalDistinct` always describe the full inventory so a caller
|
|
2353
|
+
* can see what the cap hid.
|
|
2354
|
+
* @param {TagInventory} inventory - Output of {@link buildTagInventory}.
|
|
2355
|
+
* @param {TagSummaryOptions} [opts] - `kind` / `prefix` / `minCount` / `limit` knobs.
|
|
2356
|
+
* @returns {TagSummary} The bounded response object.
|
|
2357
|
+
*/
|
|
2358
|
+
declare function summarizeTagInventory(inventory: TagInventory, opts?: TagSummaryOptions): TagSummary;
|
|
2359
|
+
|
|
1467
2360
|
/** A config matcher: substring, regex, or predicate tested against the lowercase basename. */
|
|
1468
2361
|
type ConfigMatcher = string | RegExp | ((baseName: string) => boolean);
|
|
1469
2362
|
/**
|
|
@@ -1535,6 +2428,10 @@ interface NodeQuery {
|
|
|
1535
2428
|
/** AND match — all entries must be present (use `tag:a+b` syntax in query strings). */
|
|
1536
2429
|
allTags?: string[];
|
|
1537
2430
|
path?: string;
|
|
2431
|
+
/** Exact match on the node's owning workspace package name. Prefix with `"!"` to negate.
|
|
2432
|
+
* Only meaningful when a `packageOf` lookup is supplied to `filterGraph`/`matchNode`
|
|
2433
|
+
* (i.e. querying a flattened workspace graph); ignored otherwise. */
|
|
2434
|
+
package?: string;
|
|
1538
2435
|
isExternal?: boolean;
|
|
1539
2436
|
/** Substring match on `imp.toPath` — node must import a file whose path contains this string. */
|
|
1540
2437
|
importsFile?: string;
|
|
@@ -1578,23 +2475,202 @@ interface NodeQuery {
|
|
|
1578
2475
|
/** OR-group: node matches if it satisfies ANY sub-query in this array, ANDed with all other top-level fields on this NodeQuery. Populated by `any(key:val|key:val)` syntax in query strings. */
|
|
1579
2476
|
any?: NodeQuery[];
|
|
1580
2477
|
}
|
|
2478
|
+
/** Sortable axes for `find_duplicates` results (`DuplicateQuery.sort`). */
|
|
2479
|
+
type DupSortField = "lines" | "score" | "occurrences";
|
|
2480
|
+
/**
|
|
2481
|
+
* Criteria for filtering `find_duplicates` results — the duplicate-group/cluster analogue of
|
|
2482
|
+
* {@link NodeQuery}. Parsed from a `"key:value,key:value"` string by `parseDupQuery`
|
|
2483
|
+
* (AND across keys); applied per group by `matchDupGroup`. String fields support a `"!"` prefix
|
|
2484
|
+
* for negation. A group's occurrences never span more than one language family or `FileType`
|
|
2485
|
+
* (matching never crosses a family boundary), so `family`/`type` are effectively group-level.
|
|
2486
|
+
*/
|
|
2487
|
+
interface DuplicateQuery {
|
|
2488
|
+
/** Substring match on occurrence paths — matches when *at least one* occurrence's path
|
|
2489
|
+
* contains this. `"!substr"` matches when *no* occurrence's path contains it. */
|
|
2490
|
+
path?: string;
|
|
2491
|
+
/** Substring match — matches only when *every* occurrence's path contains this (an
|
|
2492
|
+
* entirely-within-one-module duplicate). `"!substr"` negates (no occurrence contains it). */
|
|
2493
|
+
allPaths?: string;
|
|
2494
|
+
/** Exact match on the group's `family` (`js`, `jvm`, `style`, `python`, `go`, `lua`,
|
|
2495
|
+
* `markdown`, `gherkin`, `other`). `"!"` to negate. */
|
|
2496
|
+
family?: string;
|
|
2497
|
+
/** Exact match on the `FileType` shared by every occurrence (derived from each occurrence's
|
|
2498
|
+
* path). `"!lang"` matches when no occurrence is of that type. */
|
|
2499
|
+
type?: string;
|
|
2500
|
+
/** `"block"` (a span match) or `"definition"` (a declaration-level match). */
|
|
2501
|
+
kind?: string;
|
|
2502
|
+
/** Exact match on a definition group's `defKind` (`cssVar` | `interface` | `type` |
|
|
2503
|
+
* `objectLiteral` | `jsxElement`). `"!"` to negate. */
|
|
2504
|
+
defKind?: string;
|
|
2505
|
+
/** Minimum / maximum duplicated block size in lines (`DuplicateGroup.lines`). */
|
|
2506
|
+
minLines?: number;
|
|
2507
|
+
maxLines?: number;
|
|
2508
|
+
/** Minimum / maximum logic-token score (`DuplicateGroup.score`, falling back to `lines` for
|
|
2509
|
+
* definition groups, which carry no score). */
|
|
2510
|
+
minScore?: number;
|
|
2511
|
+
maxScore?: number;
|
|
2512
|
+
/** Minimum number of occurrences (`DuplicateGroup.occurrences.length`). */
|
|
2513
|
+
minOccurrences?: number;
|
|
2514
|
+
/** `true` = occurrences span ≥2 distinct files; `false` = every occurrence is in one file. */
|
|
2515
|
+
crossFile?: boolean;
|
|
2516
|
+
/** OR match across positive entries (`DuplicateSignal` names); `"!name"` entries are
|
|
2517
|
+
* mandatory exclusions, evaluated independently. */
|
|
2518
|
+
signals?: string[];
|
|
2519
|
+
/** Result ordering for the flat `groups` list. */
|
|
2520
|
+
sort?: DupSortField;
|
|
2521
|
+
/** Direction for `sort`. Defaults to `"desc"`. */
|
|
2522
|
+
sortDir?: "asc" | "desc";
|
|
2523
|
+
/** Cap on the number of results. */
|
|
2524
|
+
limit?: number;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
/** Applies DuplicateQuery predicates to `find_duplicates` groups: path, family, type, size, score,
|
|
2528
|
+
* occurrence count, cross-file, and signal filters, plus DSL-driven sort/limit. */
|
|
2529
|
+
|
|
2530
|
+
/**
|
|
2531
|
+
* @description Whether a single {@link DuplicateGroup} satisfies every clause in `query` (AND
|
|
2532
|
+
* across keys). An empty query matches every group.
|
|
2533
|
+
* @param {DuplicateGroup} group - The group to test.
|
|
2534
|
+
* @param {DuplicateQuery} query - Parsed filter criteria.
|
|
2535
|
+
* @returns {boolean} `true` when the group passes all set criteria.
|
|
2536
|
+
*/
|
|
2537
|
+
declare function matchDupGroup(group: DuplicateGroup, query: DuplicateQuery): boolean;
|
|
2538
|
+
/**
|
|
2539
|
+
* @description Applies the DSL's `sort` / `sortDir` / `limit` to an already-filtered group list.
|
|
2540
|
+
* When no `sort` is given the input order is preserved (callers pass groups already ranked by
|
|
2541
|
+
* `findDuplicates`). Pure — returns a new array. Split from {@link applyDupQuery} so consumers
|
|
2542
|
+
* that received predicate-filtered groups from `findDuplicates` can re-order/cap them without a
|
|
2543
|
+
* redundant second predicate pass.
|
|
2544
|
+
* @param {DuplicateGroup[]} groups - Groups to order and cap.
|
|
2545
|
+
* @param {DuplicateQuery} query - Parsed criteria; only `sort`/`sortDir`/`limit` are read.
|
|
2546
|
+
* @returns {DuplicateGroup[]} The groups, ordered and capped per the query.
|
|
2547
|
+
*/
|
|
2548
|
+
declare function sortLimitDupGroups(groups: DuplicateGroup[], query: DuplicateQuery): DuplicateGroup[];
|
|
2549
|
+
/**
|
|
2550
|
+
* @description Filters `groups` by `query`, then orders and caps them via
|
|
2551
|
+
* {@link sortLimitDupGroups}. Pure — returns a new array.
|
|
2552
|
+
* @param {DuplicateGroup[]} groups - Groups to filter and shape.
|
|
2553
|
+
* @param {DuplicateQuery} query - Parsed filter criteria.
|
|
2554
|
+
* @returns {DuplicateGroup[]} The matching groups, ordered and capped per the query.
|
|
2555
|
+
*/
|
|
2556
|
+
declare function applyDupQuery(groups: DuplicateGroup[], query: DuplicateQuery): DuplicateGroup[];
|
|
2557
|
+
|
|
2558
|
+
/** Parses a `key:value` filter string for `find_duplicates` results into a structured DuplicateQuery. */
|
|
2559
|
+
|
|
2560
|
+
/**
|
|
2561
|
+
* @description Parses a `"key:value,key:value"` filter string (AND across keys) into a
|
|
2562
|
+
* structured {@link DuplicateQuery} for use with `matchDupGroup` / `applyDupQuery`. An empty
|
|
2563
|
+
* or whitespace-only string yields an empty query (matches everything).
|
|
2564
|
+
* @param {string} queryString - Comma-separated `key:value` pairs, e.g.
|
|
2565
|
+
* `"crossFile:true,type:typescript,path:!test"`.
|
|
2566
|
+
* @returns {DuplicateQuery} The structured query object.
|
|
2567
|
+
* @throws {Error} On an unknown key, a malformed clause, a non-numeric numeric value, or an
|
|
2568
|
+
* invalid `sort` value — surfaced to the caller rather than silently ignored, so a typo'd
|
|
2569
|
+
* filter fails loudly instead of returning zero groups.
|
|
2570
|
+
*/
|
|
2571
|
+
declare function parseDupQuery(queryString: string): DuplicateQuery;
|
|
1581
2572
|
|
|
1582
2573
|
/** Filters a graph by applying NodeQuery predicates: category, type, tag, path, imports, coverage, and more. */
|
|
1583
2574
|
|
|
2575
|
+
/** @description Optional extras for {@link filterGraph}. */
|
|
2576
|
+
interface FilterGraphOptions {
|
|
2577
|
+
/** Path → owning-package-name lookup, from a flattened `WorkspaceGraph`. When supplied,
|
|
2578
|
+
* enables the `package:` query key and stamps each result node with its `package`. */
|
|
2579
|
+
packageOf?: Map<string, string> | undefined;
|
|
2580
|
+
}
|
|
1584
2581
|
/**
|
|
1585
2582
|
* @description Filters a serialized graph to only nodes matching all criteria in `query`,
|
|
1586
2583
|
* then trims each node's import list to edges whose target is also in the result set.
|
|
1587
2584
|
* Optionally sorts the result and applies a `limit`.
|
|
1588
2585
|
* @param {SerializedGraph} graph - The serialized graph to filter.
|
|
1589
2586
|
* @param {NodeQuery} query - Filter criteria; omitted fields are treated as wildcards.
|
|
2587
|
+
* @param {FilterGraphOptions} [options] - Optional `packageOf` lookup for workspace-scoped queries.
|
|
1590
2588
|
* @returns {SerializedGraph} A new `SerializedGraph` containing only the matching subgraph.
|
|
1591
2589
|
*/
|
|
1592
|
-
declare function filterGraph(graph: SerializedGraph, query: NodeQuery): SerializedGraph;
|
|
2590
|
+
declare function filterGraph(graph: SerializedGraph, query: NodeQuery, options?: FilterGraphOptions): SerializedGraph;
|
|
1593
2591
|
|
|
1594
2592
|
/** Parses a key:value query string into a structured NodeQuery for use with filterGraph. */
|
|
1595
2593
|
|
|
1596
2594
|
declare function parseQuery(queryString: string): NodeQuery;
|
|
1597
2595
|
|
|
2596
|
+
/**
|
|
2597
|
+
* Shared definition of a **test-selection tag** — a tag name that is a plausible
|
|
2598
|
+
* `vitest --grep` / native-framework-tag term for "given this source change, which tests run?".
|
|
2599
|
+
*
|
|
2600
|
+
* Graph tags are produced generously (every top-level declaration name, every imported symbol,
|
|
2601
|
+
* every `@word` in a test title, every third-party package). Most of those are noise for test
|
|
2602
|
+
* selection: a `library` tag like `vitest` matches every test under grep, a `function` tag is
|
|
2603
|
+
* the name of a helper in the test file itself, `test`/`barrel` are on every node of their
|
|
2604
|
+
* kind. This module is the one place that decides which tags survive, reused by
|
|
2605
|
+
* `propose_tags` (`src/tags/proposer.ts`), `apply_tags` (`src/tags/applier.ts`),
|
|
2606
|
+
* `list_tags` (`src/graph/tag-inventory.ts`) and the `tag:` query filter
|
|
2607
|
+
* (`src/query/matchers.ts`).
|
|
2608
|
+
*
|
|
2609
|
+
* The blocklist is configured from `mokosh.config` via {@link configureTagQuality}, called by
|
|
2610
|
+
* `applyConfig` — the same reset-then-apply lifecycle as the classify registries.
|
|
2611
|
+
*/
|
|
2612
|
+
|
|
2613
|
+
/** Tag kinds that can denote a test-selection label. `function` / `variable` are declaration
|
|
2614
|
+
* names (a helper in the test file, or a symbol on a source file — neither is a grep term);
|
|
2615
|
+
* `library` is a third-party package name (present on nearly every test). `class` / `type`
|
|
2616
|
+
* are never produced. */
|
|
2617
|
+
declare const SELECTION_TAG_KINDS: readonly TagKind[];
|
|
2618
|
+
/** Tag names that are just a file's `category` echoed as a tag — present on every node of that
|
|
2619
|
+
* category, so they have zero selectivity. */
|
|
2620
|
+
declare const CATEGORY_MARKER_TAGS: ReadonlySet<string>;
|
|
2621
|
+
/** A tag name must be a bare identifier: no `@`, `:`, `/`, spaces. (Moved here from
|
|
2622
|
+
* `src/tags/applier.ts`.) */
|
|
2623
|
+
declare const VALID_TAG_NAME_RE: RegExp;
|
|
2624
|
+
/**
|
|
2625
|
+
* Curated generic names that appear in almost every codebase and carry no domain signal, so
|
|
2626
|
+
* they never make a useful test-selection tag. Deliberately **conservative** — real subsystem
|
|
2627
|
+
* names (`graph`, `parser`, `resolver`, `cache`, …) are left queryable; a project narrows
|
|
2628
|
+
* further with `mokosh.config` `tags.blocklist`. Mirrors the `rename-singles` denylist
|
|
2629
|
+
* philosophy: small, evidence-driven, extended only when a name is shown to be noise.
|
|
2630
|
+
*
|
|
2631
|
+
* Seeded from `applier.ts`'s former `GENERIC_TAG_BLOCKLIST` plus the generic offenders seen
|
|
2632
|
+
* dominating `list_tags` on real repos (structural filenames + common code words).
|
|
2633
|
+
*/
|
|
2634
|
+
declare const DEFAULT_TAG_BLOCKLIST: ReadonlySet<string>;
|
|
2635
|
+
/** User overrides from `mokosh.config` `tags`. */
|
|
2636
|
+
interface TagQualityConfig {
|
|
2637
|
+
/** Names added to {@link DEFAULT_TAG_BLOCKLIST} (case-insensitive). */
|
|
2638
|
+
blocklist?: string[];
|
|
2639
|
+
/** Names removed from the effective blocklist — kept even if built-in or user-blocked. */
|
|
2640
|
+
allowlist?: string[];
|
|
2641
|
+
}
|
|
2642
|
+
/**
|
|
2643
|
+
* @description Recomputes the process-wide effective tag blocklist:
|
|
2644
|
+
* `(DEFAULT_TAG_BLOCKLIST ∪ cfg.blocklist) \ cfg.allowlist`, all lower-cased. Called by
|
|
2645
|
+
* `applyConfig` after `resetTagQuality`, so each analyzed root fully replaces the previous
|
|
2646
|
+
* config rather than accumulating.
|
|
2647
|
+
* @param {TagQualityConfig} [cfg] - The `tags` section of the loaded `mokosh.config`.
|
|
2648
|
+
*/
|
|
2649
|
+
declare function configureTagQuality(cfg?: TagQualityConfig): void;
|
|
2650
|
+
/** @description Restores the built-in blocklist, dropping any prior {@link configureTagQuality}. */
|
|
2651
|
+
declare function resetTagQuality(): void;
|
|
2652
|
+
/**
|
|
2653
|
+
* @description The kind-independent half of {@link isSelectionTag}: a bare identifier, not a
|
|
2654
|
+
* `category` echo (`test`/`barrel`), and not blocklisted. Use when you already have just a
|
|
2655
|
+
* tag name (e.g. the `list_tags` inventory aggregates names across kinds).
|
|
2656
|
+
* @param {string} name - The tag name to test.
|
|
2657
|
+
* @returns {boolean} `true` when the name is not noise.
|
|
2658
|
+
*/
|
|
2659
|
+
declare function isSelectionTagName(name: string): boolean;
|
|
2660
|
+
/**
|
|
2661
|
+
* @description Whether a tag is usable as a test-selection label: a selection kind, plus
|
|
2662
|
+
* {@link isSelectionTagName}.
|
|
2663
|
+
* @param {StructuredTag} tag - The structured tag to test.
|
|
2664
|
+
* @returns {boolean} `true` when the tag should be surfaced for test selection.
|
|
2665
|
+
*/
|
|
2666
|
+
declare function isSelectionTag(tag: StructuredTag): boolean;
|
|
2667
|
+
/**
|
|
2668
|
+
* @description Distinct names of the selection-quality tags on a node, in first-seen order.
|
|
2669
|
+
* @param {readonly StructuredTag[]} tags - A node's `tags` array.
|
|
2670
|
+
* @returns {string[]} Deduplicated selection tag names.
|
|
2671
|
+
*/
|
|
2672
|
+
declare function selectionTagNames(tags: readonly StructuredTag[]): string[];
|
|
2673
|
+
|
|
1598
2674
|
/**
|
|
1599
2675
|
* @description Result for a single file processed by {@link applyTagsToFile}.
|
|
1600
2676
|
*/
|
|
@@ -1620,10 +2696,12 @@ interface ApplyTagsResult {
|
|
|
1620
2696
|
files: ApplyTagsFileResult[];
|
|
1621
2697
|
}
|
|
1622
2698
|
/**
|
|
1623
|
-
* @description Iterates every test node in the graph,
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
2699
|
+
* @description Iterates every test node in the graph, keeps its selection-quality tags
|
|
2700
|
+
* (see {@link isSelectionTag} — `import` filename/symbol tags and deliberate `comment-marker`
|
|
2701
|
+
* markers, minus declaration/library names, `test`/`barrel`, and blocklisted generics), then
|
|
2702
|
+
* delegates writing to the strategy selected by `mokosh.config.*` (`tagApplier.framework`,
|
|
2703
|
+
* default `"vitest"`). Non-test nodes are skipped. Assumes `applyConfig` has already run for
|
|
2704
|
+
* this root so any `tags.blocklist` / `tags.allowlist` is active.
|
|
1627
2705
|
* @param {Graph} graph - The fully-enriched dependency graph.
|
|
1628
2706
|
* @param {string} rootDir - Absolute path to the project root.
|
|
1629
2707
|
* @param {{ dryRun: boolean }} options - Pass `dryRun: true` to preview changes without disk writes.
|
|
@@ -1661,9 +2739,10 @@ interface ProposeTagsOptions {
|
|
|
1661
2739
|
* @description Proposes Vitest tags to run based on which files changed.
|
|
1662
2740
|
*
|
|
1663
2741
|
* Traverses the incoming dependency graph from each changed file. Test nodes
|
|
1664
|
-
* that can reach the changed file contribute their tags
|
|
1665
|
-
*
|
|
1666
|
-
*
|
|
2742
|
+
* that can reach the changed file contribute their **selection-quality** tags
|
|
2743
|
+
* (see {@link selectionTagNames} — drops declaration/library names, `test`/`barrel`,
|
|
2744
|
+
* and blocklisted generics). Feature hubs act as boundaries: the hub's tag is
|
|
2745
|
+
* emitted and traversal stops there, preventing combinatorial blowup in large graphs.
|
|
1667
2746
|
* @param {Graph} graph - The full project dependency graph.
|
|
1668
2747
|
* @param {string[]} changedFiles - Relative paths of files that were modified (e.g. from git diff).
|
|
1669
2748
|
* @param {ProposeTagsOptions} [options] - Optional: custom test identifier and feature-detection settings.
|
|
@@ -1705,20 +2784,57 @@ declare function createImportMap(rootDir: string, entryPoints: string[], previou
|
|
|
1705
2784
|
parallelParsing?: ParallelParsingOption | undefined;
|
|
1706
2785
|
pathAliases?: Record<string, string[]> | undefined;
|
|
1707
2786
|
additionalIgnoreDirs?: string[] | undefined;
|
|
2787
|
+
docFiles?: string[] | null | undefined;
|
|
1708
2788
|
}): Promise<Graph>;
|
|
2789
|
+
/**
|
|
2790
|
+
* @description Computes a digest of every source file under `rootDir` (path + mtime + size),
|
|
2791
|
+
* used to decide whether a disk-persisted workspace graph is still valid. Changing, adding, or
|
|
2792
|
+
* removing any file changes the digest.
|
|
2793
|
+
* @param rootDir - Absolute monorepo root.
|
|
2794
|
+
* @returns A hex sha-256 digest, and the relative file list it was computed from (reused by the
|
|
2795
|
+
* caller so the directory tree is walked only once).
|
|
2796
|
+
*/
|
|
2797
|
+
declare function computeWorkspaceSourceDigest(rootDir: string): {
|
|
2798
|
+
digest: string;
|
|
2799
|
+
files: string[];
|
|
2800
|
+
};
|
|
2801
|
+
/**
|
|
2802
|
+
* @description Buckets the monorepo's source files by owning package and digests each bucket
|
|
2803
|
+
* independently — plus a `rootDigest` over files owned by no package (top-level configs,
|
|
2804
|
+
* lockfiles, root docs). Lets the workspace disk cache (`src/graph/workspace/disk-cache.ts`)
|
|
2805
|
+
* tell which individual packages are still current, instead of one whole-tree digest that any
|
|
2806
|
+
* single edit invalidates.
|
|
2807
|
+
* @param rootDir - Absolute monorepo root.
|
|
2808
|
+
* @param packages - Packages to bucket into; each needs `name` and `relativeRoot`.
|
|
2809
|
+
* @param files - A pre-computed `getAllProjectFiles(rootDir)` result (e.g. from
|
|
2810
|
+
* `computeWorkspaceSourceDigest`), reused so the tree is walked only once.
|
|
2811
|
+
* @returns `rootDigest` and a `packageDigests` map keyed by package name (every package in
|
|
2812
|
+
* `packages` gets an entry, even if it owns no files).
|
|
2813
|
+
*/
|
|
2814
|
+
declare function computeWorkspacePackageDigests(rootDir: string, packages: ReadonlyArray<Pick<WorkspacePackage, "name" | "relativeRoot">>, files: string[]): {
|
|
2815
|
+
rootDigest: string;
|
|
2816
|
+
packageDigests: Map<string, string>;
|
|
2817
|
+
};
|
|
1709
2818
|
/**
|
|
1710
2819
|
* @description Auto-detects the monorepo layout under `rootDir` and builds a per-package
|
|
1711
2820
|
* dependency graph, stitching them together into a single WorkspaceGraph.
|
|
1712
2821
|
* @param rootDir - Absolute path to the monorepo root.
|
|
1713
|
-
* @param options - `packages` filters to a named subset of packages; `silent` suppresses progress; `gitStats` attaches git churn data per file; `parallelParsing` controls worker-pool offloading of file parsing per package (see {@link ParallelParsingOption}); `pathAliases` overrides/extends tsconfig path-alias resolution for every package (see `MokoshConfig.pathAliases`).
|
|
2822
|
+
* @param options - `packages` filters to a named subset of packages; `silent` suppresses progress; `gitStats` attaches git churn data per file; `parallelParsing` controls worker-pool offloading of file parsing per package (see {@link ParallelParsingOption}); `pathAliases` overrides/extends tsconfig path-alias resolution for every package (see `MokoshConfig.pathAliases`); `layout` supplies a pre-computed `detectMonorepo` result so detection is not repeated by callers that already ran it.
|
|
1714
2823
|
* @returns A WorkspaceGraph where each package has its own Graph and cross-package edges are resolved.
|
|
1715
2824
|
*/
|
|
1716
2825
|
declare function createWorkspaceGraph(rootDir: string, options?: {
|
|
1717
|
-
packages?: string[];
|
|
2826
|
+
packages?: string[] | undefined;
|
|
1718
2827
|
silent?: boolean;
|
|
1719
2828
|
gitStats?: boolean;
|
|
1720
2829
|
parallelParsing?: ParallelParsingOption | undefined;
|
|
1721
2830
|
pathAliases?: Record<string, string[]> | undefined;
|
|
2831
|
+
additionalIgnoreDirs?: string[] | undefined;
|
|
2832
|
+
layout?: MonorepoLayout | undefined;
|
|
2833
|
+
/** Pre-computed `getAllProjectFiles(rootDir)` result, so the caller's digest walk isn't repeated. */
|
|
2834
|
+
projectFiles?: string[] | undefined;
|
|
2835
|
+
/** A previously built workspace graph; each package reuses its prior graph as an incremental
|
|
2836
|
+
* base so unchanged files are not re-parsed (mtime+size match). */
|
|
2837
|
+
previousWorkspace?: WorkspaceGraph | undefined;
|
|
1722
2838
|
}): Promise<WorkspaceGraph>;
|
|
1723
2839
|
/**
|
|
1724
2840
|
* @description Recursively walks `rootDir` and returns paths of every file whose extension
|
|
@@ -1729,4 +2845,4 @@ declare function createWorkspaceGraph(rootDir: string, options?: {
|
|
|
1729
2845
|
*/
|
|
1730
2846
|
declare function getAllProjectFiles(rootDir: string, options?: ScanOptions): string[];
|
|
1731
2847
|
|
|
1732
|
-
export { type ApiSurface, type ApplyTagsFileResult, type ApplyTagsResult, type BranchComparison, type BranchComparisonSummary, type BuildGraphAtRefOptions, CALL_EDGE_TYPES, type CachedFileTokens, CallEdge, type CalleeEntry, type CallerEntry$1 as CallerEntry, type ChangeImpactCache, type CompareBranchesOptions, type ComplexFunctionEntry, type ComplexityDelta, type CoverageDelta, DEFAULT_BRANCH_GRAPH_CACHE_DIR, DEFAULT_CACHE_DIR, DEFAULT_DUPLICATION_TOKEN_CACHE_FILE, DEFAULT_EXTENSIONS, DEFAULT_GRAPH_CACHE_FILE, DEFAULT_IGNORE_DIRS, type DependencyGraph, type DocDriftDelta, type DuplicateFamily, type DuplicateGroup, type DuplicateOccurrence, type DuplicationDelta, type DuplicationTokenCache, EXPORT_TRACKING_TYPES, type ExportKind, type FeatureDetectionOptions, type FeatureDomain, type FeatureGraph, type FeatureGraphOptions, type FeatureInfo, type FileDiff, FileNode, FileType, type FindComplexFunctionsOptions, type FindDuplicatesOptions, type FindDuplicatesResult, type FindRiskHotspotsOptions, type FunctionCallInfo, type GetAffectedOptions, type GetCallersOptions, Graph, type CallerEntry as GraphCallerEntry, type GraphExporter, IMPORT_SYMBOL_TYPES, ImportEdge, type LanguageCoverage, MermaidExporter, type ModuleResponsibility, type ModuleRole, type MokoshConfig, type MonorepoDetector, type MonorepoLayout, NodeCategory, type NodeMeta, type NodeQuery, type ParallelParsingOption, type PathWithSymbols, type ProposeTagsOptions, type PublicExport, type ResponsibilityGraph, type RiskHotspotEntry, type RiskHotspotsResult, type ScanOptions, type SerializedGraph, type SerializedWorkspaceGraph, type SlimNode, type SlimSerializedGraph, type StaleReference, StructuredTag, type SummarizeOptions, type SymbolCaller, type SymbolImporter, type SymbolMatch, type SymbolPrecision, SymbolTraversalContext, type TestNodeIdentifier, type TraversalOptions, type TraversalVisitor, type TypeEdge, type TypeGraph, type TypeKind, type TypeNode, type TypeQueryResult, WorkspaceGraph, type WorkspacePackage, type WorkspacePackageSummary, type WorkspacePackagesSummary, applyConfig, applyTags, buildApiSurface, buildChangeImpactCache, buildFeatureGraph, buildGraphAtRef, buildResponsibilityGraph, buildTypeGraph, compareBranches, computeGraphHash, configToGraphOptions, createImportMap, createWorkspaceGraph, detectAllEntryPoints, detectEntryPoint, detectFeatures, detectMonorepo, filterGraph, findComplexFunctions, findDuplicates, findRiskHotspots, findSymbol, getAffected, getAllProjectFiles, getCallers, getDependencies, getDependents, getLanguageCoverage, getNodeMeta, hasChurnData, hasCoverageData, isChangeImpactCacheValid, loadChangeImpactCache, loadCoverageMap, loadMokoshConfig, loadTokenCacheFromDisk, parseQuery, proposeAffectedTests, proposeTags, queryCallGraph, queryChangeImpact, queryTypeGraph, registerConfigMatcher, registerMonorepoDetector, registerParser, registerTestLibrary, registerTestPattern, resetClassifyRegistries, saveChangeImpactCache, saveTokenCacheToDisk, slimSerialize, summarizeBranchComparison, summarizeWorkspacePackages, toMermaid };
|
|
2848
|
+
export { type ApiSurface, type ApiSurfaceSummary, type ApplyTagsFileResult, type ApplyTagsResult, type BranchComparison, type BranchComparisonSummary, type BuildGraphAtRefOptions, CALL_EDGE_TYPES, CATEGORY_MARKER_TAGS, type CachedDuplicationResult, type CachedFileTokens, CallEdge, type CalleeEntry, type CallerEntry$1 as CallerEntry, type ChangeImpactCache, type CompareBranchesOptions, type ComplexFunctionEntry, type ComplexityDelta, type CoverageDelta, type CycleEdgeKind, DEFAULT_BRANCH_GRAPH_CACHE_DIR, DEFAULT_CACHE_DIR, DEFAULT_DUPLICATION_RESULT_CACHE_FILE, DEFAULT_DUPLICATION_TOKEN_CACHE_FILE, DEFAULT_EXTENSIONS, DEFAULT_GRAPH_CACHE_FILE, DEFAULT_IGNORE_DIRS, DEFAULT_TAG_BLOCKLIST, DEFAULT_TAG_LIMIT, DEFAULT_TAG_MIN_COUNT, DEFAULT_WORKSPACE_CACHE_SUBDIR, DEFAULT_WORKSPACE_GRAPH_CACHE_FILE, type DependencyGraph, type DocDriftDelta, type DuplicateCluster, type DuplicateClusterFileCoverage, type DuplicateFamily, type DuplicateGroup, type DuplicateOccurrence, type DuplicateQuery, type DuplicateSignal, type DuplicatesSummary, type DuplicationDelta, type DuplicationResultParams, type DuplicationTokenCache, EXPORT_TRACKING_TYPES, type ExportKind, FUNCTION_COMPLEXITY_TYPES, type FeatureDetectionOptions, type FeatureDomain, type FeatureGraph, type FeatureGraphOptions, type FeatureInfo, type FidelityLevel, type FileDiff, FileNode, FileType, type FindComplexFunctionsOptions, type FindCyclesOptions, type FindDuplicatesOptions, type FindDuplicatesResult, type FindRiskHotspotsOptions, type FlatWorkspaceGraph, type FunctionCallInfo, type GetAffectedOptions, type GetCallersOptions, Graph, GraphAnalyzer, type CallerEntry as GraphCallerEntry, type GraphExporter, IMPORT_SYMBOL_TYPES, ImportEdge, LANGUAGE_FIDELITY, type LanguageCoverage, type LanguageFeature, type LanguageFidelity, MAX_PACKAGE_CACHE_BYTES, MermaidExporter, type ModuleResponsibility, type ModuleRole, type MokoshConfig, type MonorepoDetector, type MonorepoLayout, NodeCategory, type NodeMeta, type NodeQuery, type ParallelParsingOption, type PathWithSymbols, type ProposeTagsOptions, type PublicExport, type ResponsibilityGraph, type RiskHotspotEntry, type RiskHotspotsResult, SELECTION_TAG_KINDS, SUMMARY_TAG_KINDS, type ScanOptions, type SerializedGraph, type SerializedWorkspaceGraph, type SlimNode, type SlimSerializedGraph, type StaleReference, StructuredTag, type SummarizeOptions, type SymbolCaller, type SymbolImporter, type SymbolMatch, type SymbolPrecision, SymbolTraversalContext, TAG_RESPONSE_HARD_CAP, TEST_TAG_STRATEGY_TYPES, TYPE_GRAPH_TYPES, type TagAggregateEntry, type TagInventory, TagKind, type TagQualityConfig, type TagSummary, type TagSummaryOptions, type TestNodeIdentifier, type TraversalOptions, type TraversalVisitor, type TypeEdge, type TypeGraph, type TypeKind, type TypeNode, type TypeQueryResult, VALID_TAG_NAME_RE, WORKSPACE_CACHE_VERSION, WORKSPACE_MANIFEST_FILE, type WorkspaceAffectedSummary, WorkspaceGraph, type WorkspaceLayoutPackageSummary, type WorkspaceLayoutSummary, type WorkspacePackage, type WorkspacePackageSummary, type WorkspacePackagesSummary, applyConfig, applyDupQuery, applyTags, buildApiSurface, buildChangeImpactCache, buildFeatureGraph, buildGraphAtRef, buildResponsibilityGraph, buildTagInventory, buildTypeGraph, compareBranches, computeGraphHash, computeWorkspacePackageDigests, computeWorkspaceSourceDigest, configToGraphOptions, configureTagQuality, createImportMap, createWorkspaceGraph, dedupeGroupsAgainstClusters, detectAllEntryPoints, detectEntryPoint, detectFeatures, detectMonorepo, duplicationDigest, duplicationResultCacheKey, filterGraph, findComplexFunctions, findDuplicates, findRiskHotspots, findSymbol, getAffected, getAllProjectFiles, getCallers, getDependencies, getDependents, getLanguageCoverage, getNodeMeta, hasChurnData, hasCoverageData, hasGeneratedMarker, hasGitTimestampData, isChangeImpactCacheValid, isGeneratedPath, isSelectionTag, isSelectionTagName, languageCaveats, languageCaveatsSummary, languageSupportNote, loadChangeImpactCache, loadCoverageMap, loadDuplicationResult, loadMokoshConfig, loadTokenCacheFromDisk, matchDupGroup, packageOwnsFile, parseDupQuery, parseQuery, proposeAffectedTests, proposeTags, queryCallGraph, queryChangeImpact, queryTypeGraph, registerConfigMatcher, registerMonorepoDetector, registerParser, registerTestLibrary, registerTestPattern, resetClassifyRegistries, resetTagQuality, saveChangeImpactCache, saveDuplicationResult, saveTokenCacheToDisk, selectionTagNames, slimDupCluster, slimDupGroup, slimOccurrences, slimSerialize, sortLimitDupGroups, summarizeApiSurface, summarizeBranchComparison, summarizeDuplicates, summarizeTagInventory, summarizeWorkspaceLayout, summarizeWorkspacePackages, toMermaid, topDir };
|