@dreamtree-org/graphify 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-K322XB4U.js → chunk-7LTO76UD.js} +2 -1
- package/dist/chunk-7LTO76UD.js.map +1 -0
- package/dist/{chunk-UBXYMMXJ.js → chunk-EHMBINRV.js} +115 -102
- package/dist/chunk-EHMBINRV.js.map +1 -0
- package/dist/cli/index.cjs +19 -7
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/index.cjs +564 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +229 -17
- package/dist/index.d.ts +229 -17
- package/dist/index.js +442 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.cjs.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-K322XB4U.js.map +0 -1
- package/dist/chunk-UBXYMMXJ.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -2,12 +2,18 @@ import Graph from 'graphology';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
type Confidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
|
|
5
|
-
type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports';
|
|
5
|
+
type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports' | 'follows';
|
|
6
6
|
interface GraphNode {
|
|
7
7
|
id: string;
|
|
8
8
|
label: string;
|
|
9
9
|
sourceFile: string;
|
|
10
10
|
sourceLocation: string;
|
|
11
|
+
/**
|
|
12
|
+
* Node kind for document graphs (document|section|chunk|entity — see
|
|
13
|
+
* docs/KB-PROVIDER.md §5). Optional and schema-additive: classic code
|
|
14
|
+
* nodes encode kind inside the label ("class Foo") and omit this.
|
|
15
|
+
*/
|
|
16
|
+
kind?: string;
|
|
11
17
|
}
|
|
12
18
|
interface GraphEdge {
|
|
13
19
|
source: string;
|
|
@@ -89,28 +95,111 @@ declare function extract(filePath: string, displayPath?: string): Promise<Extrac
|
|
|
89
95
|
* same module) are kept as separate edges.
|
|
90
96
|
*/
|
|
91
97
|
declare function buildGraph(extractions: ExtractionResult[]): Graph;
|
|
98
|
+
/**
|
|
99
|
+
* The merge loop shared by buildGraph() (fresh graph) and
|
|
100
|
+
* updateCorpusGraph() (incremental, src/ingest/corpus.ts). Callers validate
|
|
101
|
+
* first — this assumes well-formed extractions. A node that already exists
|
|
102
|
+
* only as an auto-vivified placeholder (sourceFile '<unknown>') is upgraded
|
|
103
|
+
* in place when an extraction supplies the real node.
|
|
104
|
+
*/
|
|
105
|
+
declare function mergeExtractionsInto(graph: Graph, validated: ExtractionResult[]): void;
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Provider-agnostic interface for the semantic (LLM-driven) extraction
|
|
109
|
+
* pass. The first-class, tested path is Anthropic (see anthropic.ts) —
|
|
110
|
+
* this interface exists so an OpenAI-compatible endpoint can be swapped in
|
|
111
|
+
* later without redesigning callers. Do not build out a large provider
|
|
112
|
+
* matrix for v1 (see master prompt §7 v2 ideas).
|
|
113
|
+
*/
|
|
114
|
+
interface SemanticExtractor {
|
|
115
|
+
extractSemantic(path: string, content: string): Promise<ExtractionResult>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Smart document ingestion (docs/KB-PROVIDER.md §5): replaces the flat
|
|
120
|
+
* `text -> string[]` chunker contract with `document -> { chunks + graph
|
|
121
|
+
* fragment }`. One document per call — providers ingest from queue workers
|
|
122
|
+
* a page at a time; updateCorpusGraph() (corpus.ts) folds each fragment
|
|
123
|
+
* into the corpus graph. Structural pass only: sections, chunks,
|
|
124
|
+
* contains/follows edges, and explicit relative markdown links — fully
|
|
125
|
+
* offline and deterministic. Entities in prose need the optional
|
|
126
|
+
* caller-injected SemanticExtractor.
|
|
127
|
+
*/
|
|
128
|
+
interface DocumentInput {
|
|
94
129
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* 'zustand/middleware'`) rather than a relative path — without this,
|
|
98
|
-
* those land on an opaque `module:` node and the test never connects to
|
|
99
|
-
* the source it exercises (so `graphify tests` under-selects).
|
|
130
|
+
* Plain text or markdown. Binary parsing (PDF/DOCX/...) is the caller's
|
|
131
|
+
* job — this API takes extracted text.
|
|
100
132
|
*/
|
|
101
|
-
|
|
133
|
+
text: string;
|
|
134
|
+
/**
|
|
135
|
+
* Stable caller-side identity — becomes GraphNode.sourceFile and the id
|
|
136
|
+
* prefix for every node from this document. Re-ingesting the same
|
|
137
|
+
* sourceRef through updateCorpusGraph() replaces that document's subgraph.
|
|
138
|
+
*/
|
|
139
|
+
sourceRef: string;
|
|
140
|
+
/** Routes the structural pass. Markdown headings are honored unless this says text/plain. */
|
|
141
|
+
mimeType?: string;
|
|
142
|
+
title?: string;
|
|
143
|
+
/** Echoed onto every chunk, never interpreted. */
|
|
144
|
+
metadata?: Record<string, string>;
|
|
145
|
+
}
|
|
146
|
+
interface ChunkOptions {
|
|
147
|
+
/** Soft target per chunk, in estimated tokens (~4 chars each). Default 400. */
|
|
148
|
+
targetTokens?: number;
|
|
149
|
+
/** Hard cap — over-long paragraphs are split to fit. Default 512. */
|
|
150
|
+
maxTokens?: number;
|
|
151
|
+
/**
|
|
152
|
+
* Overlap is OFF by default: `follows` edges + expandRetrieval() replace
|
|
153
|
+
* what sliding-window overlap approximates. Callers migrating from
|
|
154
|
+
* window-chunkers can turn it back on.
|
|
155
|
+
*/
|
|
156
|
+
overlapTokens?: number;
|
|
102
157
|
}
|
|
103
|
-
interface
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
158
|
+
interface IngestOptions {
|
|
159
|
+
chunking?: ChunkOptions;
|
|
160
|
+
/**
|
|
161
|
+
* Optional semantic pass for entities/relationships in prose. Injected,
|
|
162
|
+
* never built-in — same stance as embeddings (docs/KB-PROVIDER.md §6).
|
|
163
|
+
* Omitted = structural-only, no LLM, no API key.
|
|
164
|
+
*/
|
|
165
|
+
semanticExtractor?: SemanticExtractor;
|
|
166
|
+
}
|
|
167
|
+
interface IngestedChunk {
|
|
168
|
+
/**
|
|
169
|
+
* Graph node id of this chunk — store it next to the chunk row: it is
|
|
170
|
+
* what makes expandRetrieval() hits O(1) instead of lexical.
|
|
171
|
+
*/
|
|
172
|
+
nodeId: string;
|
|
173
|
+
/** What the caller embeds and stores. */
|
|
174
|
+
content: string;
|
|
175
|
+
tokenEstimate: number;
|
|
176
|
+
/** Reading order within the document. */
|
|
177
|
+
index: number;
|
|
178
|
+
/** Heading trail, e.g. ["Q3 Results", "Revenue"]. Empty for plain text. */
|
|
179
|
+
sectionPath: string[];
|
|
180
|
+
/** sha256 of normalized content — caller-side dedup across sources. */
|
|
181
|
+
contentHash: string;
|
|
182
|
+
metadata: Record<string, string>;
|
|
183
|
+
}
|
|
184
|
+
interface IngestResult {
|
|
185
|
+
chunks: IngestedChunk[];
|
|
186
|
+
/** Graph fragment for this document — feed to updateCorpusGraph() or runPipeline's extraExtractions. */
|
|
187
|
+
extraction: ExtractionResult;
|
|
188
|
+
stats: {
|
|
189
|
+
sections: number;
|
|
190
|
+
entities: number;
|
|
191
|
+
tokenTotal: number;
|
|
192
|
+
};
|
|
108
193
|
}
|
|
109
194
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
195
|
+
* Turn one document into structure-aware chunks plus a typed graph
|
|
196
|
+
* fragment: a `document` node, `section` nodes nested via `contains`,
|
|
197
|
+
* `chunk` nodes under their section (`contains`), chained in reading order
|
|
198
|
+
* (`follows`), with relative markdown links as `references` edges. The
|
|
199
|
+
* chunks are graph nodes — the caller stores each chunk's nodeId, which is
|
|
200
|
+
* what fuses their vector search with expandRetrieval() at query time.
|
|
112
201
|
*/
|
|
113
|
-
declare function
|
|
202
|
+
declare function ingestDocument(input: DocumentInput, options?: IngestOptions): Promise<IngestResult>;
|
|
114
203
|
|
|
115
204
|
type ClusterAlgorithm = 'louvain' | 'leiden';
|
|
116
205
|
interface ClusterOptions {
|
|
@@ -144,6 +233,60 @@ interface ClusterOptions {
|
|
|
144
233
|
*/
|
|
145
234
|
declare function cluster(graph: Graph, options?: ClusterOptions): Graph;
|
|
146
235
|
|
|
236
|
+
interface UpdateCorpusOptions {
|
|
237
|
+
algorithm?: ClusterAlgorithm;
|
|
238
|
+
/**
|
|
239
|
+
* Clustering strategy per update. `'auto'` (default) keeps bulk loading
|
|
240
|
+
* safe without the caller thinking about it: new nodes adopt the majority
|
|
241
|
+
* community of their neighbors (cheap, local, deterministic), and a full
|
|
242
|
+
* global re-cluster runs only when accumulated churn since the last full
|
|
243
|
+
* pass exceeds `reclusterRatio` of the graph — or on a never-clustered
|
|
244
|
+
* graph. `true` forces a full re-cluster now (e.g. once after a batch);
|
|
245
|
+
* `false` skips community maintenance entirely for this call (churn is
|
|
246
|
+
* still tracked, so a later `'auto'` call sees the backlog).
|
|
247
|
+
*/
|
|
248
|
+
recluster?: boolean | 'auto';
|
|
249
|
+
/**
|
|
250
|
+
* Auto mode only: fraction of the graph's nodes that must have churned
|
|
251
|
+
* since the last full clustering before one is triggered. Default 0.1.
|
|
252
|
+
*/
|
|
253
|
+
reclusterRatio?: number;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Fold one document's extraction into an existing corpus graph — the
|
|
257
|
+
* streaming-ingestion companion to ingestDocument() (docs/KB-PROVIDER.md
|
|
258
|
+
* §5). Pass null for the store.load() no-graph-yet case. Re-ingesting a
|
|
259
|
+
* document is replace-not-duplicate: every node whose sourceFile matches
|
|
260
|
+
* one of the extraction's sourceFiles is dropped (with its edges) before
|
|
261
|
+
* the fragment is merged. Placeholder nodes that earlier documents' link
|
|
262
|
+
* edges auto-vivified are upgraded in place when this document is the real
|
|
263
|
+
* thing. Community upkeep is incremental by default and self-tunes to bulk
|
|
264
|
+
* loads — see UpdateCorpusOptions.recluster. Mutates and returns the graph.
|
|
265
|
+
*/
|
|
266
|
+
declare function updateCorpusGraph(graph: Graph | null, extraction: ExtractionResult, options?: UpdateCorpusOptions): Graph;
|
|
267
|
+
|
|
268
|
+
interface ResolveOptions {
|
|
269
|
+
/**
|
|
270
|
+
* The scanned package's own name(s) (from package.json). Tests very
|
|
271
|
+
* commonly import the package's PUBLIC entry (`import { persist } from
|
|
272
|
+
* 'zustand/middleware'`) rather than a relative path — without this,
|
|
273
|
+
* those land on an opaque `module:` node and the test never connects to
|
|
274
|
+
* the source it exercises (so `graphify tests` under-selects).
|
|
275
|
+
*/
|
|
276
|
+
selfNames?: string[];
|
|
277
|
+
}
|
|
278
|
+
interface ResolveStats {
|
|
279
|
+
/** Edge endpoints re-pointed from a guessed id to a real node id. */
|
|
280
|
+
resolvedEndpoints: number;
|
|
281
|
+
/** Placeholder module nodes dropped because the real file node replaced them. */
|
|
282
|
+
droppedPlaceholders: number;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Mutates `extractions` in place: re-points guessed edge endpoints at real
|
|
286
|
+
* node ids and drops the placeholder module nodes those guesses created.
|
|
287
|
+
*/
|
|
288
|
+
declare function resolveCrossFileReferences(extractions: ExtractionResult[], options?: ResolveOptions): ResolveStats;
|
|
289
|
+
|
|
147
290
|
/**
|
|
148
291
|
* Surface god-nodes (excessive fan-in/out), structural surprises (self
|
|
149
292
|
* references, AMBIGUOUS edges), and open questions (isolated nodes,
|
|
@@ -358,6 +501,73 @@ interface ExplainResult {
|
|
|
358
501
|
/** Plain-language-ready explanation of the node that best matches `query`. */
|
|
359
502
|
declare function explainNode(graph: Graph, query: string): ExplainResult | null;
|
|
360
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Graph-augmented retrieval (docs/KB-PROVIDER.md §4): the caller has
|
|
506
|
+
* already done similarity search (vector, keyword, hybrid — their
|
|
507
|
+
* pipeline, their embeddings); expandRetrieval() takes those hits and adds
|
|
508
|
+
* what similarity can't see — the nodes *structurally connected* to them.
|
|
509
|
+
* Pure and synchronous like every query function: Graph in, data out; no
|
|
510
|
+
* store access, no embedding calls.
|
|
511
|
+
*/
|
|
512
|
+
/**
|
|
513
|
+
* One hit from the caller's own search. `nodeId` when the caller stored
|
|
514
|
+
* graph node ids alongside its records at ingest time; otherwise `text`,
|
|
515
|
+
* resolved against the graph lexically (same resolver queryGraph uses).
|
|
516
|
+
*/
|
|
517
|
+
interface RetrievalHit {
|
|
518
|
+
nodeId?: string;
|
|
519
|
+
text?: string;
|
|
520
|
+
/** The caller's similarity score, echoed through to rank seeds. */
|
|
521
|
+
score?: number;
|
|
522
|
+
}
|
|
523
|
+
interface ExpandOptions {
|
|
524
|
+
/** Approximate cap, in tokens (~4 chars each), on the result. Default 2000. */
|
|
525
|
+
tokenBudget?: number;
|
|
526
|
+
maxHops?: number;
|
|
527
|
+
/** Restrict traversal (and reported edges) to these relations, e.g. ['contains', 'references']. */
|
|
528
|
+
relations?: Relation[];
|
|
529
|
+
}
|
|
530
|
+
interface ExpandedNode {
|
|
531
|
+
id: string;
|
|
532
|
+
label: string;
|
|
533
|
+
/** Node kind attr (chunk|section|entity|document|...) — absent on classic code nodes. */
|
|
534
|
+
kind?: string;
|
|
535
|
+
sourceFile: string;
|
|
536
|
+
sourceLocation: string;
|
|
537
|
+
/** 0 = was a seed (or an unresolvable hit echoed through). */
|
|
538
|
+
hops: number;
|
|
539
|
+
/** The edge that first led the traversal here (absent for seeds). */
|
|
540
|
+
via?: TraversalEdge;
|
|
541
|
+
/** The caller's similarity score — seeds only. */
|
|
542
|
+
seedScore?: number;
|
|
543
|
+
}
|
|
544
|
+
interface ExpandedContext {
|
|
545
|
+
/**
|
|
546
|
+
* Union of seed nodes and discovered neighbors, ranked: seeds first (by
|
|
547
|
+
* caller score desc), then by (hops asc, degree desc). Deduped across
|
|
548
|
+
* seeds. Hits that resolve to no graph node are echoed through with
|
|
549
|
+
* hops 0 and no expansion, so adopting this on a partially-ingested
|
|
550
|
+
* corpus never does worse than flat retrieval.
|
|
551
|
+
*/
|
|
552
|
+
nodes: ExpandedNode[];
|
|
553
|
+
/** Every edge between included nodes — provenance for citations. */
|
|
554
|
+
edges: TraversalEdge[];
|
|
555
|
+
communities: Array<{
|
|
556
|
+
id: number;
|
|
557
|
+
label: string;
|
|
558
|
+
seedCount: number;
|
|
559
|
+
}>;
|
|
560
|
+
/** True when the token budget stopped the traversal before the frontier was exhausted. */
|
|
561
|
+
truncated: boolean;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Expand the caller's top-K hits through the graph: one joint,
|
|
565
|
+
* token-budgeted BFS seeded from every hit at once, so overlapping
|
|
566
|
+
* neighborhoods dedupe and the budget is shared across seeds. Deterministic
|
|
567
|
+
* (lexical neighbor order, stable ranking) like the rest of the query layer.
|
|
568
|
+
*/
|
|
569
|
+
declare function expandRetrieval(graph: Graph, hits: RetrievalHit[], options?: ExpandOptions): ExpandedContext;
|
|
570
|
+
|
|
361
571
|
interface AffectedNode {
|
|
362
572
|
id: string;
|
|
363
573
|
label: string;
|
|
@@ -664,6 +874,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
|
|
|
664
874
|
label: z.ZodString;
|
|
665
875
|
sourceFile: z.ZodString;
|
|
666
876
|
sourceLocation: z.ZodString;
|
|
877
|
+
kind: z.ZodOptional<z.ZodString>;
|
|
667
878
|
}, z.core.$strip>>;
|
|
668
879
|
edges: z.ZodArray<z.ZodObject<{
|
|
669
880
|
source: z.ZodString;
|
|
@@ -680,6 +891,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
|
|
|
680
891
|
contains: "contains";
|
|
681
892
|
method: "method";
|
|
682
893
|
re_exports: "re_exports";
|
|
894
|
+
follows: "follows";
|
|
683
895
|
}>;
|
|
684
896
|
confidence: z.ZodEnum<{
|
|
685
897
|
EXTRACTED: "EXTRACTED";
|
|
@@ -705,4 +917,4 @@ declare class ExtractionValidationError extends Error {
|
|
|
705
917
|
*/
|
|
706
918
|
declare function validateExtraction(value: unknown, context?: string): ExtractionResult;
|
|
707
919
|
|
|
708
|
-
export { type AffectedNode, type Analysis, type ChangedSymbol, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RewiredSymbol, type RulesConfig, type SavedResult, type SerializedGraph, type TestSelection, type TraversalEdge, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, isTestFile, loadGraph, loadResults, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, validateExtraction, validateRules };
|
|
920
|
+
export { type AffectedNode, type Analysis, type ChangedSymbol, type ChunkOptions, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DocumentInput, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExpandOptions, type ExpandedContext, type ExpandedNode, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, type IngestOptions, type IngestResult, type IngestedChunk, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RetrievalHit, type RewiredSymbol, type RulesConfig, type SavedResult, type SemanticExtractor, type SerializedGraph, type TestSelection, type TraversalEdge, type UpdateCorpusOptions, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, expandRetrieval, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, ingestDocument, isTestFile, loadGraph, loadResults, mergeExtractionsInto, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, updateCorpusGraph, validateExtraction, validateRules };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,18 @@ import Graph from 'graphology';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
type Confidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
|
|
5
|
-
type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports';
|
|
5
|
+
type Relation = 'calls' | 'imports' | 'imports_from' | 'inherits' | 'implements' | 'mixes_in' | 'embeds' | 'references' | 'contains' | 'method' | 're_exports' | 'follows';
|
|
6
6
|
interface GraphNode {
|
|
7
7
|
id: string;
|
|
8
8
|
label: string;
|
|
9
9
|
sourceFile: string;
|
|
10
10
|
sourceLocation: string;
|
|
11
|
+
/**
|
|
12
|
+
* Node kind for document graphs (document|section|chunk|entity — see
|
|
13
|
+
* docs/KB-PROVIDER.md §5). Optional and schema-additive: classic code
|
|
14
|
+
* nodes encode kind inside the label ("class Foo") and omit this.
|
|
15
|
+
*/
|
|
16
|
+
kind?: string;
|
|
11
17
|
}
|
|
12
18
|
interface GraphEdge {
|
|
13
19
|
source: string;
|
|
@@ -89,28 +95,111 @@ declare function extract(filePath: string, displayPath?: string): Promise<Extrac
|
|
|
89
95
|
* same module) are kept as separate edges.
|
|
90
96
|
*/
|
|
91
97
|
declare function buildGraph(extractions: ExtractionResult[]): Graph;
|
|
98
|
+
/**
|
|
99
|
+
* The merge loop shared by buildGraph() (fresh graph) and
|
|
100
|
+
* updateCorpusGraph() (incremental, src/ingest/corpus.ts). Callers validate
|
|
101
|
+
* first — this assumes well-formed extractions. A node that already exists
|
|
102
|
+
* only as an auto-vivified placeholder (sourceFile '<unknown>') is upgraded
|
|
103
|
+
* in place when an extraction supplies the real node.
|
|
104
|
+
*/
|
|
105
|
+
declare function mergeExtractionsInto(graph: Graph, validated: ExtractionResult[]): void;
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Provider-agnostic interface for the semantic (LLM-driven) extraction
|
|
109
|
+
* pass. The first-class, tested path is Anthropic (see anthropic.ts) —
|
|
110
|
+
* this interface exists so an OpenAI-compatible endpoint can be swapped in
|
|
111
|
+
* later without redesigning callers. Do not build out a large provider
|
|
112
|
+
* matrix for v1 (see master prompt §7 v2 ideas).
|
|
113
|
+
*/
|
|
114
|
+
interface SemanticExtractor {
|
|
115
|
+
extractSemantic(path: string, content: string): Promise<ExtractionResult>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Smart document ingestion (docs/KB-PROVIDER.md §5): replaces the flat
|
|
120
|
+
* `text -> string[]` chunker contract with `document -> { chunks + graph
|
|
121
|
+
* fragment }`. One document per call — providers ingest from queue workers
|
|
122
|
+
* a page at a time; updateCorpusGraph() (corpus.ts) folds each fragment
|
|
123
|
+
* into the corpus graph. Structural pass only: sections, chunks,
|
|
124
|
+
* contains/follows edges, and explicit relative markdown links — fully
|
|
125
|
+
* offline and deterministic. Entities in prose need the optional
|
|
126
|
+
* caller-injected SemanticExtractor.
|
|
127
|
+
*/
|
|
128
|
+
interface DocumentInput {
|
|
94
129
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* 'zustand/middleware'`) rather than a relative path — without this,
|
|
98
|
-
* those land on an opaque `module:` node and the test never connects to
|
|
99
|
-
* the source it exercises (so `graphify tests` under-selects).
|
|
130
|
+
* Plain text or markdown. Binary parsing (PDF/DOCX/...) is the caller's
|
|
131
|
+
* job — this API takes extracted text.
|
|
100
132
|
*/
|
|
101
|
-
|
|
133
|
+
text: string;
|
|
134
|
+
/**
|
|
135
|
+
* Stable caller-side identity — becomes GraphNode.sourceFile and the id
|
|
136
|
+
* prefix for every node from this document. Re-ingesting the same
|
|
137
|
+
* sourceRef through updateCorpusGraph() replaces that document's subgraph.
|
|
138
|
+
*/
|
|
139
|
+
sourceRef: string;
|
|
140
|
+
/** Routes the structural pass. Markdown headings are honored unless this says text/plain. */
|
|
141
|
+
mimeType?: string;
|
|
142
|
+
title?: string;
|
|
143
|
+
/** Echoed onto every chunk, never interpreted. */
|
|
144
|
+
metadata?: Record<string, string>;
|
|
145
|
+
}
|
|
146
|
+
interface ChunkOptions {
|
|
147
|
+
/** Soft target per chunk, in estimated tokens (~4 chars each). Default 400. */
|
|
148
|
+
targetTokens?: number;
|
|
149
|
+
/** Hard cap — over-long paragraphs are split to fit. Default 512. */
|
|
150
|
+
maxTokens?: number;
|
|
151
|
+
/**
|
|
152
|
+
* Overlap is OFF by default: `follows` edges + expandRetrieval() replace
|
|
153
|
+
* what sliding-window overlap approximates. Callers migrating from
|
|
154
|
+
* window-chunkers can turn it back on.
|
|
155
|
+
*/
|
|
156
|
+
overlapTokens?: number;
|
|
102
157
|
}
|
|
103
|
-
interface
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
158
|
+
interface IngestOptions {
|
|
159
|
+
chunking?: ChunkOptions;
|
|
160
|
+
/**
|
|
161
|
+
* Optional semantic pass for entities/relationships in prose. Injected,
|
|
162
|
+
* never built-in — same stance as embeddings (docs/KB-PROVIDER.md §6).
|
|
163
|
+
* Omitted = structural-only, no LLM, no API key.
|
|
164
|
+
*/
|
|
165
|
+
semanticExtractor?: SemanticExtractor;
|
|
166
|
+
}
|
|
167
|
+
interface IngestedChunk {
|
|
168
|
+
/**
|
|
169
|
+
* Graph node id of this chunk — store it next to the chunk row: it is
|
|
170
|
+
* what makes expandRetrieval() hits O(1) instead of lexical.
|
|
171
|
+
*/
|
|
172
|
+
nodeId: string;
|
|
173
|
+
/** What the caller embeds and stores. */
|
|
174
|
+
content: string;
|
|
175
|
+
tokenEstimate: number;
|
|
176
|
+
/** Reading order within the document. */
|
|
177
|
+
index: number;
|
|
178
|
+
/** Heading trail, e.g. ["Q3 Results", "Revenue"]. Empty for plain text. */
|
|
179
|
+
sectionPath: string[];
|
|
180
|
+
/** sha256 of normalized content — caller-side dedup across sources. */
|
|
181
|
+
contentHash: string;
|
|
182
|
+
metadata: Record<string, string>;
|
|
183
|
+
}
|
|
184
|
+
interface IngestResult {
|
|
185
|
+
chunks: IngestedChunk[];
|
|
186
|
+
/** Graph fragment for this document — feed to updateCorpusGraph() or runPipeline's extraExtractions. */
|
|
187
|
+
extraction: ExtractionResult;
|
|
188
|
+
stats: {
|
|
189
|
+
sections: number;
|
|
190
|
+
entities: number;
|
|
191
|
+
tokenTotal: number;
|
|
192
|
+
};
|
|
108
193
|
}
|
|
109
194
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
195
|
+
* Turn one document into structure-aware chunks plus a typed graph
|
|
196
|
+
* fragment: a `document` node, `section` nodes nested via `contains`,
|
|
197
|
+
* `chunk` nodes under their section (`contains`), chained in reading order
|
|
198
|
+
* (`follows`), with relative markdown links as `references` edges. The
|
|
199
|
+
* chunks are graph nodes — the caller stores each chunk's nodeId, which is
|
|
200
|
+
* what fuses their vector search with expandRetrieval() at query time.
|
|
112
201
|
*/
|
|
113
|
-
declare function
|
|
202
|
+
declare function ingestDocument(input: DocumentInput, options?: IngestOptions): Promise<IngestResult>;
|
|
114
203
|
|
|
115
204
|
type ClusterAlgorithm = 'louvain' | 'leiden';
|
|
116
205
|
interface ClusterOptions {
|
|
@@ -144,6 +233,60 @@ interface ClusterOptions {
|
|
|
144
233
|
*/
|
|
145
234
|
declare function cluster(graph: Graph, options?: ClusterOptions): Graph;
|
|
146
235
|
|
|
236
|
+
interface UpdateCorpusOptions {
|
|
237
|
+
algorithm?: ClusterAlgorithm;
|
|
238
|
+
/**
|
|
239
|
+
* Clustering strategy per update. `'auto'` (default) keeps bulk loading
|
|
240
|
+
* safe without the caller thinking about it: new nodes adopt the majority
|
|
241
|
+
* community of their neighbors (cheap, local, deterministic), and a full
|
|
242
|
+
* global re-cluster runs only when accumulated churn since the last full
|
|
243
|
+
* pass exceeds `reclusterRatio` of the graph — or on a never-clustered
|
|
244
|
+
* graph. `true` forces a full re-cluster now (e.g. once after a batch);
|
|
245
|
+
* `false` skips community maintenance entirely for this call (churn is
|
|
246
|
+
* still tracked, so a later `'auto'` call sees the backlog).
|
|
247
|
+
*/
|
|
248
|
+
recluster?: boolean | 'auto';
|
|
249
|
+
/**
|
|
250
|
+
* Auto mode only: fraction of the graph's nodes that must have churned
|
|
251
|
+
* since the last full clustering before one is triggered. Default 0.1.
|
|
252
|
+
*/
|
|
253
|
+
reclusterRatio?: number;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Fold one document's extraction into an existing corpus graph — the
|
|
257
|
+
* streaming-ingestion companion to ingestDocument() (docs/KB-PROVIDER.md
|
|
258
|
+
* §5). Pass null for the store.load() no-graph-yet case. Re-ingesting a
|
|
259
|
+
* document is replace-not-duplicate: every node whose sourceFile matches
|
|
260
|
+
* one of the extraction's sourceFiles is dropped (with its edges) before
|
|
261
|
+
* the fragment is merged. Placeholder nodes that earlier documents' link
|
|
262
|
+
* edges auto-vivified are upgraded in place when this document is the real
|
|
263
|
+
* thing. Community upkeep is incremental by default and self-tunes to bulk
|
|
264
|
+
* loads — see UpdateCorpusOptions.recluster. Mutates and returns the graph.
|
|
265
|
+
*/
|
|
266
|
+
declare function updateCorpusGraph(graph: Graph | null, extraction: ExtractionResult, options?: UpdateCorpusOptions): Graph;
|
|
267
|
+
|
|
268
|
+
interface ResolveOptions {
|
|
269
|
+
/**
|
|
270
|
+
* The scanned package's own name(s) (from package.json). Tests very
|
|
271
|
+
* commonly import the package's PUBLIC entry (`import { persist } from
|
|
272
|
+
* 'zustand/middleware'`) rather than a relative path — without this,
|
|
273
|
+
* those land on an opaque `module:` node and the test never connects to
|
|
274
|
+
* the source it exercises (so `graphify tests` under-selects).
|
|
275
|
+
*/
|
|
276
|
+
selfNames?: string[];
|
|
277
|
+
}
|
|
278
|
+
interface ResolveStats {
|
|
279
|
+
/** Edge endpoints re-pointed from a guessed id to a real node id. */
|
|
280
|
+
resolvedEndpoints: number;
|
|
281
|
+
/** Placeholder module nodes dropped because the real file node replaced them. */
|
|
282
|
+
droppedPlaceholders: number;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Mutates `extractions` in place: re-points guessed edge endpoints at real
|
|
286
|
+
* node ids and drops the placeholder module nodes those guesses created.
|
|
287
|
+
*/
|
|
288
|
+
declare function resolveCrossFileReferences(extractions: ExtractionResult[], options?: ResolveOptions): ResolveStats;
|
|
289
|
+
|
|
147
290
|
/**
|
|
148
291
|
* Surface god-nodes (excessive fan-in/out), structural surprises (self
|
|
149
292
|
* references, AMBIGUOUS edges), and open questions (isolated nodes,
|
|
@@ -358,6 +501,73 @@ interface ExplainResult {
|
|
|
358
501
|
/** Plain-language-ready explanation of the node that best matches `query`. */
|
|
359
502
|
declare function explainNode(graph: Graph, query: string): ExplainResult | null;
|
|
360
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Graph-augmented retrieval (docs/KB-PROVIDER.md §4): the caller has
|
|
506
|
+
* already done similarity search (vector, keyword, hybrid — their
|
|
507
|
+
* pipeline, their embeddings); expandRetrieval() takes those hits and adds
|
|
508
|
+
* what similarity can't see — the nodes *structurally connected* to them.
|
|
509
|
+
* Pure and synchronous like every query function: Graph in, data out; no
|
|
510
|
+
* store access, no embedding calls.
|
|
511
|
+
*/
|
|
512
|
+
/**
|
|
513
|
+
* One hit from the caller's own search. `nodeId` when the caller stored
|
|
514
|
+
* graph node ids alongside its records at ingest time; otherwise `text`,
|
|
515
|
+
* resolved against the graph lexically (same resolver queryGraph uses).
|
|
516
|
+
*/
|
|
517
|
+
interface RetrievalHit {
|
|
518
|
+
nodeId?: string;
|
|
519
|
+
text?: string;
|
|
520
|
+
/** The caller's similarity score, echoed through to rank seeds. */
|
|
521
|
+
score?: number;
|
|
522
|
+
}
|
|
523
|
+
interface ExpandOptions {
|
|
524
|
+
/** Approximate cap, in tokens (~4 chars each), on the result. Default 2000. */
|
|
525
|
+
tokenBudget?: number;
|
|
526
|
+
maxHops?: number;
|
|
527
|
+
/** Restrict traversal (and reported edges) to these relations, e.g. ['contains', 'references']. */
|
|
528
|
+
relations?: Relation[];
|
|
529
|
+
}
|
|
530
|
+
interface ExpandedNode {
|
|
531
|
+
id: string;
|
|
532
|
+
label: string;
|
|
533
|
+
/** Node kind attr (chunk|section|entity|document|...) — absent on classic code nodes. */
|
|
534
|
+
kind?: string;
|
|
535
|
+
sourceFile: string;
|
|
536
|
+
sourceLocation: string;
|
|
537
|
+
/** 0 = was a seed (or an unresolvable hit echoed through). */
|
|
538
|
+
hops: number;
|
|
539
|
+
/** The edge that first led the traversal here (absent for seeds). */
|
|
540
|
+
via?: TraversalEdge;
|
|
541
|
+
/** The caller's similarity score — seeds only. */
|
|
542
|
+
seedScore?: number;
|
|
543
|
+
}
|
|
544
|
+
interface ExpandedContext {
|
|
545
|
+
/**
|
|
546
|
+
* Union of seed nodes and discovered neighbors, ranked: seeds first (by
|
|
547
|
+
* caller score desc), then by (hops asc, degree desc). Deduped across
|
|
548
|
+
* seeds. Hits that resolve to no graph node are echoed through with
|
|
549
|
+
* hops 0 and no expansion, so adopting this on a partially-ingested
|
|
550
|
+
* corpus never does worse than flat retrieval.
|
|
551
|
+
*/
|
|
552
|
+
nodes: ExpandedNode[];
|
|
553
|
+
/** Every edge between included nodes — provenance for citations. */
|
|
554
|
+
edges: TraversalEdge[];
|
|
555
|
+
communities: Array<{
|
|
556
|
+
id: number;
|
|
557
|
+
label: string;
|
|
558
|
+
seedCount: number;
|
|
559
|
+
}>;
|
|
560
|
+
/** True when the token budget stopped the traversal before the frontier was exhausted. */
|
|
561
|
+
truncated: boolean;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Expand the caller's top-K hits through the graph: one joint,
|
|
565
|
+
* token-budgeted BFS seeded from every hit at once, so overlapping
|
|
566
|
+
* neighborhoods dedupe and the budget is shared across seeds. Deterministic
|
|
567
|
+
* (lexical neighbor order, stable ranking) like the rest of the query layer.
|
|
568
|
+
*/
|
|
569
|
+
declare function expandRetrieval(graph: Graph, hits: RetrievalHit[], options?: ExpandOptions): ExpandedContext;
|
|
570
|
+
|
|
361
571
|
interface AffectedNode {
|
|
362
572
|
id: string;
|
|
363
573
|
label: string;
|
|
@@ -664,6 +874,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
|
|
|
664
874
|
label: z.ZodString;
|
|
665
875
|
sourceFile: z.ZodString;
|
|
666
876
|
sourceLocation: z.ZodString;
|
|
877
|
+
kind: z.ZodOptional<z.ZodString>;
|
|
667
878
|
}, z.core.$strip>>;
|
|
668
879
|
edges: z.ZodArray<z.ZodObject<{
|
|
669
880
|
source: z.ZodString;
|
|
@@ -680,6 +891,7 @@ declare const ExtractionResultSchema: z.ZodObject<{
|
|
|
680
891
|
contains: "contains";
|
|
681
892
|
method: "method";
|
|
682
893
|
re_exports: "re_exports";
|
|
894
|
+
follows: "follows";
|
|
683
895
|
}>;
|
|
684
896
|
confidence: z.ZodEnum<{
|
|
685
897
|
EXTRACTED: "EXTRACTED";
|
|
@@ -705,4 +917,4 @@ declare class ExtractionValidationError extends Error {
|
|
|
705
917
|
*/
|
|
706
918
|
declare function validateExtraction(value: unknown, context?: string): ExtractionResult;
|
|
707
919
|
|
|
708
|
-
export { type AffectedNode, type Analysis, type ChangedSymbol, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RewiredSymbol, type RulesConfig, type SavedResult, type SerializedGraph, type TestSelection, type TraversalEdge, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, isTestFile, loadGraph, loadResults, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, validateExtraction, validateRules };
|
|
920
|
+
export { type AffectedNode, type Analysis, type ChangedSymbol, type ChunkOptions, type ClusterAlgorithm, type ClusterOptions, type Confidence, type ContextOptions, type ContextPack, type ContextSnippet, type DependencyRule, type DocumentInput, type DsnQueryFn, EXTRACTOR_REGISTRY, type ExpandOptions, type ExpandedContext, type ExpandedNode, type ExplainResult, type ExportOptions, ExtractionCache, type ExtractionResult, ExtractionResultSchema, ExtractionValidationError, type ExtractionValidationIssue, type FileCategory, type FileManifest, type GraphDiff, type GraphEdge, type GraphNode, type GraphStore, type ImpactOptions, type ImpactResult, type IngestOptions, type IngestResult, type IngestedChunk, LocalFileGraphStore, type MergeEntry, type NodeMatch, type PathResult, type PipelineOptions, type PipelineResult, type QueryOptions, type QueryResult, type ReflectOptions, type Relation, type ResolveStats, type ResultOutcome, type ResultType, type RetrievalHit, type RewiredSymbol, type RulesConfig, type SavedResult, type SemanticExtractor, type SerializedGraph, type TestSelection, type TraversalEdge, type UpdateCorpusOptions, type Violation, type VisitedNode, affectedBy, analyze, buildContextPack, buildGraph, checkRules, cluster, collectFiles, deserializeGraph, diffGraphs, expandRetrieval, explainNode, exportGraph, extract, extractMysql, globToRegExp, graphForDirectory, ingestDocument, isTestFile, loadGraph, loadResults, mergeExtractionsInto, mergeGraphs, queryGraph, rankForTask, renderContextPack, renderLessons, renderReport, renderReview, resolveCrossFileReferences, resolveNode, reviewRevisions, runPipeline, saveResult, scoreNodes, serializeGraph, shortestPath, testsForChangedFiles, testsForNode, updateCorpusGraph, validateExtraction, validateRules };
|