@wrongstack/tools 1.0.0 → 1.0.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/auto-proceed-loop-guard.d.ts +1 -1
- package/dist/auto-proceed-loop-guard.js +1 -1
- package/dist/bash.js +1 -1
- package/dist/builtin.js +15 -15
- package/dist/{chunk-VVZPQPTY.js → chunk-3AU6CZWL.js} +4 -2
- package/dist/{chunk-N3K2SFEI.js → chunk-52UXXQMP.js} +1408 -200
- package/dist/{chunk-NN2SNP2R.js → chunk-6H4ERJP7.js} +137 -21
- package/dist/{chunk-DK4J2WMH.js → chunk-7XUEPBNT.js} +2 -2
- package/dist/{chunk-IYH4OHBP.js → chunk-DKHMPKDG.js} +2 -2
- package/dist/{chunk-NU2IZ72J.js → chunk-DL2ARI4S.js} +7 -5
- package/dist/{chunk-M6UV67BH.js → chunk-EL2NJXSK.js} +2 -2
- package/dist/{chunk-YEISRT4S.js → chunk-HWXIRVKH.js} +1408 -235
- package/dist/{chunk-GKSG5CPH.js → chunk-KVTEIUND.js} +2 -2
- package/dist/{chunk-GDDXX27P.js → chunk-MBR5VAB5.js} +2 -2
- package/dist/{chunk-KDEXUQY7.js → chunk-MGPR7VG3.js} +261 -258
- package/dist/{chunk-YULVUTK5.js → chunk-NFX2IUQA.js} +3 -3
- package/dist/{chunk-37QKUQ2Z.js → chunk-NL2S2CXD.js} +1 -1
- package/dist/{chunk-ZTCJCYLN.js → chunk-TI5COGLY.js} +49 -6
- package/dist/{chunk-CGXTR34H.js → chunk-TU5MTW2S.js} +2 -2
- package/dist/{chunk-AB2CAUOP.js → chunk-UCXFTCAI.js} +186 -36
- package/dist/{chunk-MK2JK35J.js → chunk-UOYRAS4J.js} +3 -3
- package/dist/{chunk-UCYQTZ7F.js → chunk-ZKC5UNGD.js} +56 -2
- package/dist/codebase-index/atlas-brief.d.ts +90 -0
- package/dist/codebase-index/atlas-export.d.ts +38 -0
- package/dist/codebase-index/atlas-projection.d.ts +114 -0
- package/dist/codebase-index/atlas-types.d.ts +90 -0
- package/dist/codebase-index/background-indexer.d.ts +15 -4
- package/dist/codebase-index/codebase-context-tool.d.ts +54 -0
- package/dist/codebase-index/concept-enrichment.d.ts +154 -0
- package/dist/codebase-index/context-retrieval.d.ts +91 -0
- package/dist/codebase-index/embedding-pass.d.ts +95 -0
- package/dist/codebase-index/graph-adjacency-cache.d.ts +36 -0
- package/dist/codebase-index/graph-rank-pass.d.ts +46 -0
- package/dist/codebase-index/graph-rank.d.ts +198 -0
- package/dist/codebase-index/index-service.d.ts +15 -1
- package/dist/codebase-index/index.d.ts +34 -19
- package/dist/codebase-index/index.js +126 -9
- package/dist/codebase-index/parser-worker-script.js +3 -3
- package/dist/codebase-index/perf-metrics.d.ts +19 -0
- package/dist/codebase-index/project-server-protocol.d.ts +2 -0
- package/dist/codebase-index/project-server-query-cache.d.ts +4 -0
- package/dist/codebase-index/project-server.js +54 -7
- package/dist/codebase-index/repo-map-fallback.d.ts +18 -0
- package/dist/codebase-index/repo-map-render.d.ts +42 -0
- package/dist/codebase-index/repo-map-types.d.ts +21 -0
- package/dist/codebase-index/repo-map.d.ts +28 -16
- package/dist/codebase-index/schema.d.ts +20 -0
- package/dist/codebase-index/worker-protocol.d.ts +47 -1
- package/dist/codebase-index/worker.js +11 -5
- package/dist/codebase-index/writer-concepts.d.ts +84 -0
- package/dist/codebase-index/writer-graph-decorate.d.ts +30 -0
- package/dist/codebase-index/writer-graph-reader.d.ts +22 -0
- package/dist/codebase-index/writer-rank.d.ts +78 -0
- package/dist/codebase-index/writer-schema.d.ts +51 -0
- package/dist/codebase-index/writer-vectors.d.ts +53 -0
- package/dist/codebase-index/writer.d.ts +59 -0
- package/dist/edit.js +7 -7
- package/dist/exec.js +1 -1
- package/dist/{go-parser-D7ELWQ7F.js → go-parser-QIEXU3P2.js} +4 -2
- package/dist/index.d.ts +48 -48
- package/dist/index.js +43 -19
- package/dist/pack.js +16 -16
- package/dist/{parser-dispatch-BDVCJXTV.js → parser-dispatch-37FT3EXQ.js} +4 -4
- package/dist/patch.js +7 -7
- package/dist/{py-parser-QC6MR4GB.js → py-parser-6GJTGUXK.js} +3 -3
- package/dist/read.js +7 -7
- package/dist/replace.js +7 -7
- package/dist/tool-tier.js +17 -17
- package/dist/write.js +7 -7
- package/package.json +5 -5
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Atlas brief — what an agent should already know before it asks anything.
|
|
3
|
+
*
|
|
4
|
+
* Every other part of this subsystem answers a question. This one answers the
|
|
5
|
+
* question nobody asks: *what is this repository?* An agent that has to
|
|
6
|
+
* discover the shape of an 8,000-file monorepo by search costs a dozen tool
|
|
7
|
+
* calls per session and rediscovers the same thing every session. A few
|
|
8
|
+
* hundred tokens of "here are the twenty files everything depends on, here is
|
|
9
|
+
* what each package is for" removes that repeatedly-paid cost.
|
|
10
|
+
*
|
|
11
|
+
* ## Why it is a structure, not a string
|
|
12
|
+
*
|
|
13
|
+
* The renderer lives with the caller because the budget does. The CLI's
|
|
14
|
+
* system-prompt contributor has a hard token ceiling and trims from the
|
|
15
|
+
* bottom; a webui panel or a `--brief` flag would want different cuts of the
|
|
16
|
+
* same data. Building a string here would force one budget on all of them.
|
|
17
|
+
*
|
|
18
|
+
* ## Why it reports staleness rather than hiding it
|
|
19
|
+
*
|
|
20
|
+
* A brief describing code that has since changed is worse than no brief: it
|
|
21
|
+
* is confidently wrong in a way the reader cannot detect. `staleFiles` is
|
|
22
|
+
* carried so the caller can say so out loud.
|
|
23
|
+
*/
|
|
24
|
+
import { type IndexStore } from './writer.js';
|
|
25
|
+
/** Hubs named in the brief. Past roughly this many, it stops being a summary. */
|
|
26
|
+
export declare const BRIEF_HUB_LIMIT = 20;
|
|
27
|
+
/** Packages named in the brief. */
|
|
28
|
+
export declare const BRIEF_PACKAGE_LIMIT = 12;
|
|
29
|
+
/** Subsystem one-liners included when the concept layer has run. */
|
|
30
|
+
export declare const BRIEF_SUBSYSTEM_LIMIT = 10;
|
|
31
|
+
export interface AtlasBriefHub {
|
|
32
|
+
path: string;
|
|
33
|
+
/** Rank relative to the most central file in the repository. */
|
|
34
|
+
rank: number;
|
|
35
|
+
/** Concept-layer summary, when the layer has run for this file. */
|
|
36
|
+
concept?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
export interface AtlasBriefPackage {
|
|
39
|
+
name: string;
|
|
40
|
+
files: number;
|
|
41
|
+
/** The package's most central file. */
|
|
42
|
+
hub: string;
|
|
43
|
+
}
|
|
44
|
+
export interface AtlasBriefSubsystem {
|
|
45
|
+
name: string;
|
|
46
|
+
summary: string;
|
|
47
|
+
}
|
|
48
|
+
export interface AtlasBrief {
|
|
49
|
+
/** True once the rank pass has run; false means only counts are meaningful. */
|
|
50
|
+
ranked: boolean;
|
|
51
|
+
counts: {
|
|
52
|
+
files: number;
|
|
53
|
+
symbols: number;
|
|
54
|
+
packages: number;
|
|
55
|
+
};
|
|
56
|
+
hubs: AtlasBriefHub[];
|
|
57
|
+
packages: AtlasBriefPackage[];
|
|
58
|
+
subsystems: AtlasBriefSubsystem[];
|
|
59
|
+
/**
|
|
60
|
+
* Files whose content changed since the written atlas was generated, or
|
|
61
|
+
* `undefined` when no atlas has been written — a project that never ran
|
|
62
|
+
* `--write` is not stale, it simply has no projection.
|
|
63
|
+
*/
|
|
64
|
+
staleFiles?: number | undefined;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the brief from an open store.
|
|
68
|
+
*
|
|
69
|
+
* Reads only what it names: the ranked head, not the whole ranking. On an
|
|
70
|
+
* index with no ranks this returns `ranked: false` and empty lists rather
|
|
71
|
+
* than falling back to a filename heuristic — a guess presented as the
|
|
72
|
+
* repository's structure is the failure mode this whole subsystem exists to
|
|
73
|
+
* remove.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildAtlasBrief(store: IndexStore, projectRoot: string): Promise<AtlasBrief>;
|
|
76
|
+
/** Reported when a project has no index to summarise. */
|
|
77
|
+
export type AtlasBriefIndexMissing = {
|
|
78
|
+
indexed: false;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Build a project's brief, owning the store lifetime.
|
|
82
|
+
*
|
|
83
|
+
* Refuses without an index for the same reason every other project-level
|
|
84
|
+
* entry point does: acquiring a store CREATES the database, and a prompt
|
|
85
|
+
* contributor must never be the thing that indexes a repository.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildProjectAtlasBrief(projectRoot: string, opts?: {
|
|
88
|
+
indexDir?: string | undefined;
|
|
89
|
+
}): Promise<AtlasBrief | AtlasBriefIndexMissing>;
|
|
90
|
+
//# sourceMappingURL=atlas-brief.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-file, dependency-free HTML rendering of the atlas.
|
|
3
|
+
*
|
|
4
|
+
* The WebUI's CodeMap is the interactive view, but it needs a running server, a
|
|
5
|
+
* built index and a browser pointed at localhost. This is the other half: one
|
|
6
|
+
* file you can attach to a pull request, publish as a CI artifact, or mail to
|
|
7
|
+
* somebody who will never install the toolchain.
|
|
8
|
+
*
|
|
9
|
+
* ## Why it embeds rather than fetches
|
|
10
|
+
*
|
|
11
|
+
* A page that fetches `atlas.json` beside it does not work from `file://` in
|
|
12
|
+
* any modern browser — the request is a cross-origin one to a null origin. The
|
|
13
|
+
* data is inlined as JSON so a double-click opens a working map.
|
|
14
|
+
*
|
|
15
|
+
* ## Why the layout is computed here, not in the page
|
|
16
|
+
*
|
|
17
|
+
* A force simulation in the page would make the same input produce a slightly
|
|
18
|
+
* different picture on every open. Positions are computed once, deterministic
|
|
19
|
+
* given the document, so two exports of the same index are byte-identical and
|
|
20
|
+
* a committed export diffs meaningfully.
|
|
21
|
+
*/
|
|
22
|
+
import type { AtlasDocument } from './atlas-types.js';
|
|
23
|
+
/** Packages drawn on the canvas. Beyond this the picture stops being readable. */
|
|
24
|
+
export declare const EXPORT_PACKAGE_LIMIT = 40;
|
|
25
|
+
/** Files listed in the ranked table. */
|
|
26
|
+
export declare const EXPORT_FILE_LIMIT = 120;
|
|
27
|
+
export interface AtlasExportOptions {
|
|
28
|
+
/** Repository name shown in the header. */
|
|
29
|
+
projectName?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Render the atlas as one self-contained HTML document.
|
|
33
|
+
*
|
|
34
|
+
* Deterministic: the same {@link AtlasDocument} always produces the same bytes,
|
|
35
|
+
* with no timestamp, no random ids and no measurement of the host.
|
|
36
|
+
*/
|
|
37
|
+
export declare function renderAtlasHtml(document: AtlasDocument, options?: AtlasExportOptions): string;
|
|
38
|
+
//# sourceMappingURL=atlas-export.d.ts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atlas — a committable projection of the codebase index.
|
|
3
|
+
*
|
|
4
|
+
* The index itself is a 180 MB SQLite database under `~/.wrongstack/projects/`:
|
|
5
|
+
* machine-local, binary, and unreviewable. That means every fresh clone, every
|
|
6
|
+
* CI run and every teammate starts cold, and nobody can see in a pull request
|
|
7
|
+
* what the index believes about the code.
|
|
8
|
+
*
|
|
9
|
+
* The atlas is the derived, human-sized answer to that: a small, deterministic
|
|
10
|
+
* set of files written into the repository at `.wrongstack/atlas/`. The
|
|
11
|
+
* database stays canonical — this is strictly a one-way projection, and
|
|
12
|
+
* regenerating it from an unchanged index must produce byte-identical output.
|
|
13
|
+
*
|
|
14
|
+
* ## Determinism is a hard requirement
|
|
15
|
+
*
|
|
16
|
+
* Anything that varies between two runs over the same index turns every
|
|
17
|
+
* indexing run into a merge conflict. So: no timestamps, no host paths, keys
|
|
18
|
+
* emitted in sorted order, floats rounded to a fixed precision, and LF line
|
|
19
|
+
* endings (the pre-commit hook enforces those repo-wide anyway).
|
|
20
|
+
*
|
|
21
|
+
* ## Staleness is reported, never guessed
|
|
22
|
+
*
|
|
23
|
+
* A committed atlas describing code that has since moved is worse than no
|
|
24
|
+
* atlas, because it is confidently wrong. {@link checkAtlasFreshness} compares
|
|
25
|
+
* the recorded per-file content hashes against the live index and says exactly
|
|
26
|
+
* which files drifted.
|
|
27
|
+
*/
|
|
28
|
+
import type { AtlasDocument, AtlasEdge, AtlasFile, AtlasManifest, AtlasPackage, AtlasProjection, AtlasSymbol } from './atlas-types.js';
|
|
29
|
+
import type { FileRankRow } from './graph-rank.js';
|
|
30
|
+
import { type IndexStore } from './writer.js';
|
|
31
|
+
/** Directory the projection is written to, relative to the project root. */
|
|
32
|
+
export declare const ATLAS_DIR: string;
|
|
33
|
+
export declare const ATLAS_JSON = "atlas.json";
|
|
34
|
+
export declare const ATLAS_MARKDOWN = "ATLAS.md";
|
|
35
|
+
export declare const ATLAS_MANIFEST = "manifest.json";
|
|
36
|
+
/**
|
|
37
|
+
* Bumped when the emitted shape changes. A reader seeing a different schema
|
|
38
|
+
* reports the atlas as stale rather than misinterpreting its fields.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ATLAS_SCHEMA = 2;
|
|
41
|
+
/**
|
|
42
|
+
* Files carried in the projection. Enough to describe the architecture,
|
|
43
|
+
* small enough to read in a diff — the full 8k-file list would be neither.
|
|
44
|
+
*/
|
|
45
|
+
export declare const ATLAS_FILE_LIMIT = 300;
|
|
46
|
+
export type { AtlasDocument, AtlasEdge, AtlasFile, AtlasManifest, AtlasPackage, AtlasProjection, AtlasSymbol, };
|
|
47
|
+
/**
|
|
48
|
+
* Build the projection from the index. Pure with respect to the filesystem —
|
|
49
|
+
* {@link writeAtlas} is what touches disk.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildAtlas(store: IndexStore, projectRoot: string): AtlasProjection;
|
|
52
|
+
export interface WrittenAtlas {
|
|
53
|
+
dir: string;
|
|
54
|
+
files: string[];
|
|
55
|
+
fileCount: number;
|
|
56
|
+
packageCount: number;
|
|
57
|
+
}
|
|
58
|
+
/** Write the projection into `<projectRoot>/.wrongstack/atlas/`. */
|
|
59
|
+
export declare function writeAtlas(store: IndexStore, projectRoot: string): Promise<WrittenAtlas>;
|
|
60
|
+
export interface AtlasFreshness {
|
|
61
|
+
fresh: boolean;
|
|
62
|
+
/** Why it is not fresh. Absent when `fresh`. */
|
|
63
|
+
reason?: 'missing' | 'unreadable' | 'schema' | 'drift' | undefined;
|
|
64
|
+
/** Atlas files whose content hash no longer matches the index. */
|
|
65
|
+
changed: string[];
|
|
66
|
+
/** Atlas files the index no longer knows about. */
|
|
67
|
+
removed: string[];
|
|
68
|
+
/** Indexed files absent from the atlas manifest. */
|
|
69
|
+
added: number;
|
|
70
|
+
/** True when the whole-repository digest still matches. */
|
|
71
|
+
digestMatches: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Files listed in a drift report before it is summarised rather than enumerated. */
|
|
74
|
+
export declare const MAX_REPORTED_DRIFT = 20;
|
|
75
|
+
/**
|
|
76
|
+
* Compare the committed atlas against the live index.
|
|
77
|
+
*
|
|
78
|
+
* Reads only the manifest — the atlas document and markdown are derived from
|
|
79
|
+
* the same data, so a manifest that matches means all three are current.
|
|
80
|
+
*/
|
|
81
|
+
export declare function checkAtlasFreshness(store: IndexStore, projectRoot: string): Promise<AtlasFreshness>;
|
|
82
|
+
/**
|
|
83
|
+
* Project-level entry points.
|
|
84
|
+
*
|
|
85
|
+
* These own the store lifetime so callers outside this package never have to
|
|
86
|
+
* touch `indexStorePool`. Both refuse to run when there is no index yet:
|
|
87
|
+
* opening a store CREATES the database, and neither writing an atlas nor
|
|
88
|
+
* checking one should be the thing that indexes a project.
|
|
89
|
+
*/
|
|
90
|
+
export type AtlasIndexMissing = {
|
|
91
|
+
indexed: false;
|
|
92
|
+
};
|
|
93
|
+
/** Write the atlas for a project, or report that it has no index yet. */
|
|
94
|
+
export declare function writeProjectAtlas(projectRoot: string, opts?: {
|
|
95
|
+
indexDir?: string | undefined;
|
|
96
|
+
}): Promise<WrittenAtlas | AtlasIndexMissing>;
|
|
97
|
+
/** Check a project's written atlas against its index. */
|
|
98
|
+
export declare function checkProjectAtlasFreshness(projectRoot: string, opts?: {
|
|
99
|
+
indexDir?: string | undefined;
|
|
100
|
+
}): Promise<AtlasFreshness | AtlasIndexMissing>;
|
|
101
|
+
/**
|
|
102
|
+
* Build a project's static HTML atlas, or report that it has no index yet.
|
|
103
|
+
*
|
|
104
|
+
* Returns the markup rather than writing it: where the file belongs is the
|
|
105
|
+
* caller's decision (a CI artifact directory, a temp file the CLI opens, an
|
|
106
|
+
* HTTP response body), and this package should not guess.
|
|
107
|
+
*/
|
|
108
|
+
export declare function exportProjectAtlasHtml(projectRoot: string, opts?: {
|
|
109
|
+
indexDir?: string | undefined;
|
|
110
|
+
projectName?: string | undefined;
|
|
111
|
+
}): Promise<string | AtlasIndexMissing>;
|
|
112
|
+
/** Re-exported for callers that render rank rows next to atlas output. */
|
|
113
|
+
export type { FileRankRow };
|
|
114
|
+
//# sourceMappingURL=atlas-projection.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes the Codebase Atlas projection emits.
|
|
3
|
+
*
|
|
4
|
+
* They live apart from the code that builds them so that a renderer can name
|
|
5
|
+
* what it renders without importing the builder: `atlas-projection` produces
|
|
6
|
+
* these and calls `atlas-export` to render them, and `atlas-export` needs the
|
|
7
|
+
* types but must never reach back for the builder. Declaring them here is what
|
|
8
|
+
* keeps that a one-way edge instead of a module cycle.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is plain data — no imports, no behaviour. `atlas-projection`
|
|
11
|
+
* re-exports the whole set, so consumers keep importing from there.
|
|
12
|
+
*/
|
|
13
|
+
export interface AtlasSymbol {
|
|
14
|
+
name: string;
|
|
15
|
+
kind: string;
|
|
16
|
+
line: number;
|
|
17
|
+
}
|
|
18
|
+
export interface AtlasFile {
|
|
19
|
+
path: string;
|
|
20
|
+
rank: number;
|
|
21
|
+
inDeg: number;
|
|
22
|
+
outDeg: number;
|
|
23
|
+
package: string;
|
|
24
|
+
symbols: AtlasSymbol[];
|
|
25
|
+
/** Concept-layer summary. Omitted entirely when the layer has not run. */
|
|
26
|
+
concept?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AtlasPackage {
|
|
29
|
+
name: string;
|
|
30
|
+
files: number;
|
|
31
|
+
/** Highest-ranked file in the package. */
|
|
32
|
+
hub: string;
|
|
33
|
+
rank: number;
|
|
34
|
+
/** Subsystem summary for this package, when the concept layer derived one. */
|
|
35
|
+
summary?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A package-level dependency, aggregated from the reference graph.
|
|
39
|
+
*
|
|
40
|
+
* Only the package level is projected. File-level edges would be tens of
|
|
41
|
+
* thousands of rows — a diff nobody can read and a file nobody can render,
|
|
42
|
+
* for a level of detail the index itself answers better on demand.
|
|
43
|
+
*/
|
|
44
|
+
export interface AtlasEdge {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
weight: number;
|
|
48
|
+
refType: string;
|
|
49
|
+
}
|
|
50
|
+
export interface AtlasDocument {
|
|
51
|
+
schema: number;
|
|
52
|
+
counts: {
|
|
53
|
+
files: number;
|
|
54
|
+
symbols: number;
|
|
55
|
+
packages: number;
|
|
56
|
+
};
|
|
57
|
+
packages: AtlasPackage[];
|
|
58
|
+
edges: AtlasEdge[];
|
|
59
|
+
files: AtlasFile[];
|
|
60
|
+
}
|
|
61
|
+
export interface AtlasManifest {
|
|
62
|
+
schema: number;
|
|
63
|
+
/**
|
|
64
|
+
* `files`/`symbols` are the index's own totals, for display.
|
|
65
|
+
*
|
|
66
|
+
* `tracked` is the size of the file set the digest was computed over — the
|
|
67
|
+
* only number a freshness check may compare its own file set against. The
|
|
68
|
+
* two can differ (a file with no symbols is tracked but may not count
|
|
69
|
+
* towards `files`), and comparing across them reported phantom additions.
|
|
70
|
+
*/
|
|
71
|
+
counts: {
|
|
72
|
+
files: number;
|
|
73
|
+
symbols: number;
|
|
74
|
+
tracked: number;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* sha-256 over every indexed file's `path\0contentHash`, sorted. Catches a
|
|
78
|
+
* change anywhere in the repository, including files the atlas does not
|
|
79
|
+
* itself carry.
|
|
80
|
+
*/
|
|
81
|
+
digest: string;
|
|
82
|
+
/** Per-file content hashes, for the files the atlas describes. */
|
|
83
|
+
files: Record<string, string>;
|
|
84
|
+
}
|
|
85
|
+
export interface AtlasProjection {
|
|
86
|
+
document: AtlasDocument;
|
|
87
|
+
manifest: AtlasManifest;
|
|
88
|
+
markdown: string;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=atlas-types.d.ts.map
|
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
* for the TUI status chip and the search/stats tools' gating.
|
|
27
27
|
*/
|
|
28
28
|
import { type CircuitSnapshot } from './circuit-breaker.js';
|
|
29
|
+
import type { ContextResult } from './context-retrieval.js';
|
|
29
30
|
import { type IncomingCallsResult, type OutgoingCallsResult } from './index-service.js';
|
|
30
31
|
import { type ProjectIndexServerClientHealth, type ProjectIndexServerConnectionState, type ProjectIndexServerShutdownResult } from './project-server-client.js';
|
|
31
32
|
import type { CodeMapGraph, IndexResult, IndexStats } from './schema.js';
|
|
32
|
-
import type { CallRefsOpArgs, FileGraphOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs, SymbolGraphOpArgs } from './worker-protocol.js';
|
|
33
|
+
import type { CallRefsOpArgs, ContextOpArgs, FileGraphOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs, SymbolGraphOpArgs, VectorSearchOpArgs, VectorSearchOpResult } from './worker-protocol.js';
|
|
33
34
|
/** True once the first full-project index has completed (success or failure). */
|
|
34
35
|
export declare function isIndexReady(): boolean;
|
|
35
36
|
/**
|
|
@@ -89,14 +90,13 @@ export declare function enqueueReindex(opts: {
|
|
|
89
90
|
* Per-project trailing coalescing window. After a file's debounce timer
|
|
90
91
|
* fires, the ready batch stays open for this long before flushing. Any file
|
|
91
92
|
* whose timer fires within the window joins the same batch and resets the
|
|
92
|
-
* timer (sliding). Default: 50ms
|
|
93
|
-
* debounce timer fires (legacy behavior — staggered bursts don't coalesce).
|
|
93
|
+
* timer (sliding). Default: 50ms; set to 0 for immediate flush.
|
|
94
94
|
*/
|
|
95
95
|
coalesceWindowMs?: number | undefined;
|
|
96
96
|
/** Watchdog timeout per file. Default: 30s. */
|
|
97
97
|
timeoutMs?: number | undefined;
|
|
98
98
|
onError?: ((err: unknown) => void) | undefined;
|
|
99
|
-
}): void
|
|
99
|
+
}): Promise<void>;
|
|
100
100
|
/** Cancel all pending debounced reindexes. For teardown / tests. */
|
|
101
101
|
export declare function cancelPendingReindexes(): void;
|
|
102
102
|
/**
|
|
@@ -115,6 +115,17 @@ export declare function codebaseIndexStats(args: StatsOpArgs, opts?: {
|
|
|
115
115
|
timeoutMs?: number | undefined;
|
|
116
116
|
signal?: AbortSignal | undefined;
|
|
117
117
|
}): Promise<IndexStats>;
|
|
118
|
+
/**
|
|
119
|
+
* Personalised retrieval — the single call that answers "which files does this
|
|
120
|
+
* task touch?", served by the same per-project index process so the wiring
|
|
121
|
+
* graph is built once per generation rather than once per query.
|
|
122
|
+
*/
|
|
123
|
+
export declare function codebaseContext(args: ContextOpArgs): Promise<ContextResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Nearest files to an already-embedded query vector. The model stays in the
|
|
126
|
+
* caller's process; only numbers cross IPC.
|
|
127
|
+
*/
|
|
128
|
+
export declare function codebaseVectorSearch(args: VectorSearchOpArgs): Promise<VectorSearchOpResult>;
|
|
118
129
|
/** Package dependency graph, served by the same per-project index process. */
|
|
119
130
|
export declare function packageGraphService(args: StatsOpArgs): Promise<CodeMapGraph>;
|
|
120
131
|
/** File dependency graph, served by the same per-project index process. */
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `codebase-context` tool — one call that answers "which files does this task
|
|
3
|
+
* touch, and what is in them?".
|
|
4
|
+
*
|
|
5
|
+
* The index already exposed the ingredients: `codebase-search` finds symbols
|
|
6
|
+
* by name, `codebase-skeleton` shows a file's shape, `codebase-incoming-calls`
|
|
7
|
+
* finds callers. Composing them was left to the model, which meant re-deriving
|
|
8
|
+
* the same chain on every task and spending several round trips on plumbing.
|
|
9
|
+
* This tool runs the composition server-side: lexical search seeds a
|
|
10
|
+
* personalised PageRank walk over the reference graph, and what comes back is
|
|
11
|
+
* the ranked file set with the declarations that matter in each.
|
|
12
|
+
*
|
|
13
|
+
* It deliberately returns signatures and line numbers rather than source. The
|
|
14
|
+
* point is to make the subsequent `read` land in the right place, not to
|
|
15
|
+
* replace it — inlining bodies here would spend the caller's context on code
|
|
16
|
+
* they may not need.
|
|
17
|
+
*/
|
|
18
|
+
import type { Tool } from '@wrongstack/core/types';
|
|
19
|
+
import type { ContextEntry } from './context-retrieval.js';
|
|
20
|
+
import type { EmbeddingPort } from './embedding-pass.js';
|
|
21
|
+
export interface CodebaseContextInput {
|
|
22
|
+
/** What you are trying to do or understand, in plain words. */
|
|
23
|
+
query: string;
|
|
24
|
+
/** Files to return. Defaults to 12, capped at 50. */
|
|
25
|
+
limit?: number | undefined;
|
|
26
|
+
/** Declarations shown per file. Defaults to 4, capped at 20. */
|
|
27
|
+
symbolsPerFile?: number | undefined;
|
|
28
|
+
/** Restrict results to a project-relative path prefix. */
|
|
29
|
+
pathPrefix?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface CodebaseContextOutput {
|
|
32
|
+
query: string;
|
|
33
|
+
entries: ContextEntry[];
|
|
34
|
+
/** Lexical hits that seeded the walk. */
|
|
35
|
+
seedCount: number;
|
|
36
|
+
/** Semantic hits that also seeded it. Zero without an embedding model. */
|
|
37
|
+
semanticSeedCount: number;
|
|
38
|
+
/** Files the walk reached before truncation to `limit`. */
|
|
39
|
+
totalCandidates: number;
|
|
40
|
+
/**
|
|
41
|
+
* `ok` — a ranked answer. `no-index` — nothing indexed yet.
|
|
42
|
+
* `no-matches` — the index exists but the query matched nothing.
|
|
43
|
+
* `unranked` — symbols exist but no reference graph, so results are lexical
|
|
44
|
+
* only. `error` — the lookup itself failed; see `error`.
|
|
45
|
+
*/
|
|
46
|
+
indexStatus: 'ok' | 'no-index' | 'no-matches' | 'unranked' | 'error';
|
|
47
|
+
/** True when a cached answer from a previous generation was served. */
|
|
48
|
+
stale?: boolean | undefined;
|
|
49
|
+
error?: string | undefined;
|
|
50
|
+
}
|
|
51
|
+
/** Install (or clear) the query-time embedding model. */
|
|
52
|
+
export declare function setContextQueryEmbedder(port: EmbeddingPort | undefined): void;
|
|
53
|
+
export declare const codebaseContextTool: Tool<CodebaseContextInput, CodebaseContextOutput>;
|
|
54
|
+
//# sourceMappingURL=codebase-context-tool.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The concept-layer enrichment pass.
|
|
3
|
+
*
|
|
4
|
+
* Walks the index's files most-central-first and asks a model, once per file,
|
|
5
|
+
* what the file is for. The answer is a short plain-English summary plus a
|
|
6
|
+
* **crux**: the line span that actually carries the file's meaning. A summary
|
|
7
|
+
* can drift from the truth; a pointer into the source cannot, so the two are
|
|
8
|
+
* always stored together.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is a separate pass
|
|
11
|
+
*
|
|
12
|
+
* Model calls take seconds. `runIndexerAtomic` is a SQLite write transaction —
|
|
13
|
+
* holding one open across thousands of network round trips would block every
|
|
14
|
+
* other writer for the duration and roll the whole thing back on the first
|
|
15
|
+
* failure. So enrichment runs on its own, outside the indexer, writing each
|
|
16
|
+
* result as it arrives.
|
|
17
|
+
*
|
|
18
|
+
* ## Why it is affordable
|
|
19
|
+
*
|
|
20
|
+
* `files.content_hash` already exists and is exactly the right cache key: a
|
|
21
|
+
* file whose bytes have not changed is never re-sent. A first pass over this
|
|
22
|
+
* repository is thousands of calls; every pass after it is only the files that
|
|
23
|
+
* actually changed. That is the difference between a one-off cost and a
|
|
24
|
+
* recurring one.
|
|
25
|
+
*
|
|
26
|
+
* ## Why it never takes the caller down
|
|
27
|
+
*
|
|
28
|
+
* Enrichment is an optional layer over a working index. A model that refuses,
|
|
29
|
+
* times out, or returns something unparseable degrades one file's summary — it
|
|
30
|
+
* is recorded in `errors` and the walk continues. Cancellation is honoured
|
|
31
|
+
* between files, and everything already written stays written.
|
|
32
|
+
*/
|
|
33
|
+
import { type IndexStore } from './writer.js';
|
|
34
|
+
/** What the summariser is given about one file. */
|
|
35
|
+
export interface SummarizeFileInput {
|
|
36
|
+
/** Project-relative path, for the model's benefit. */
|
|
37
|
+
file: string;
|
|
38
|
+
/** Absolute path, if the summariser wants to read more itself. */
|
|
39
|
+
absolutePath: string;
|
|
40
|
+
language: string;
|
|
41
|
+
/** File source, already truncated to {@link MAX_SOURCE_CHARS}. */
|
|
42
|
+
source: string;
|
|
43
|
+
/** Whether `source` was cut short. */
|
|
44
|
+
truncated: boolean;
|
|
45
|
+
/** Declarations the index recorded, as orientation. */
|
|
46
|
+
declarations: ReadonlyArray<{
|
|
47
|
+
name: string;
|
|
48
|
+
kind: string;
|
|
49
|
+
line: number;
|
|
50
|
+
}>;
|
|
51
|
+
/** A previous, now-outdated summary, when one exists. */
|
|
52
|
+
staleSummary?: string | undefined;
|
|
53
|
+
signal?: AbortSignal | undefined;
|
|
54
|
+
}
|
|
55
|
+
export interface SummarizeFileResult {
|
|
56
|
+
/** One or two sentences on what the file is for. */
|
|
57
|
+
summary: string;
|
|
58
|
+
/** 1-based inclusive line span of the load-bearing lines. */
|
|
59
|
+
cruxStart?: number | undefined;
|
|
60
|
+
cruxEnd?: number | undefined;
|
|
61
|
+
/** Model identifier, recorded so a later pass can tell what produced this. */
|
|
62
|
+
model?: string | undefined;
|
|
63
|
+
}
|
|
64
|
+
export interface SummarizeSubsystemInput {
|
|
65
|
+
/** Package or directory label. */
|
|
66
|
+
name: string;
|
|
67
|
+
files: ReadonlyArray<{
|
|
68
|
+
file: string;
|
|
69
|
+
summary: string;
|
|
70
|
+
rank: number;
|
|
71
|
+
}>;
|
|
72
|
+
signal?: AbortSignal | undefined;
|
|
73
|
+
}
|
|
74
|
+
export interface SummarizeSubsystemResult {
|
|
75
|
+
summary: string;
|
|
76
|
+
/**
|
|
77
|
+
* Other subsystem names this one relates to, with a relation from the closed
|
|
78
|
+
* vocabulary. Unknown relations and unknown targets are dropped by the caller.
|
|
79
|
+
*/
|
|
80
|
+
relations?: ReadonlyArray<{
|
|
81
|
+
to: string;
|
|
82
|
+
relation: string;
|
|
83
|
+
}> | undefined;
|
|
84
|
+
model?: string | undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The host-supplied model transport.
|
|
88
|
+
*
|
|
89
|
+
* Injected rather than imported: producing a summary needs a configured
|
|
90
|
+
* provider and the model-tier policy, both of which live in the host. This
|
|
91
|
+
* mirrors how SAGE takes `getLlmCall` — `packages/tools` stays free of
|
|
92
|
+
* provider wiring, and a host that supplies no port simply gets no concepts.
|
|
93
|
+
*/
|
|
94
|
+
export interface SummarizerPort {
|
|
95
|
+
describeFile(input: SummarizeFileInput): Promise<SummarizeFileResult | null>;
|
|
96
|
+
describeSubsystem?(input: SummarizeSubsystemInput): Promise<SummarizeSubsystemResult | null>;
|
|
97
|
+
}
|
|
98
|
+
/** Source sent per file. Enough for a summary; short enough to stay cheap. */
|
|
99
|
+
export declare const MAX_SOURCE_CHARS = 12000;
|
|
100
|
+
/** Crux span ceiling. Graft uses twelve lines; longer stops being a pointer. */
|
|
101
|
+
export declare const MAX_CRUX_LINES = 12;
|
|
102
|
+
export declare const DEFAULT_CONCURRENCY = 5;
|
|
103
|
+
/** Summary length ceiling, so one verbose model cannot bloat the layer. */
|
|
104
|
+
export declare const MAX_SUMMARY_CHARS = 400;
|
|
105
|
+
export interface EnrichOptions {
|
|
106
|
+
/** Stop after this many files. The natural way to sample the cost first. */
|
|
107
|
+
maxFiles?: number | undefined;
|
|
108
|
+
/** Files summarised in parallel. */
|
|
109
|
+
concurrency?: number | undefined;
|
|
110
|
+
/** Re-summarise files whose summary is already current. */
|
|
111
|
+
force?: boolean | undefined;
|
|
112
|
+
/** Also derive the subsystem layer, when the port supports it. */
|
|
113
|
+
subsystems?: boolean | undefined;
|
|
114
|
+
signal?: AbortSignal | undefined;
|
|
115
|
+
onProgress?: ((done: number, total: number) => void) | undefined;
|
|
116
|
+
}
|
|
117
|
+
export interface EnrichResult {
|
|
118
|
+
/** Files sent to the model. */
|
|
119
|
+
summarised: number;
|
|
120
|
+
/** Files skipped because their stored summary already matched. */
|
|
121
|
+
cached: number;
|
|
122
|
+
/** Files the model declined or failed on. */
|
|
123
|
+
failed: number;
|
|
124
|
+
/** Concepts marked stale before the walk started. */
|
|
125
|
+
markedStale: number;
|
|
126
|
+
/** Concepts dropped because their file left the index. */
|
|
127
|
+
pruned: number;
|
|
128
|
+
subsystems: number;
|
|
129
|
+
durationMs: number;
|
|
130
|
+
errors: string[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Run one enrichment pass.
|
|
134
|
+
*
|
|
135
|
+
* `relativeOf` is injected for the same reason the retrieval walk takes it —
|
|
136
|
+
* the caller owns what "project-relative" means, and this module stays free of
|
|
137
|
+
* path policy.
|
|
138
|
+
*/
|
|
139
|
+
export declare function enrichConcepts(store: IndexStore, port: SummarizerPort, relativeOf: (file: string) => string, options?: EnrichOptions): Promise<EnrichResult>;
|
|
140
|
+
/** Reported when a project has no index to enrich. */
|
|
141
|
+
export type ConceptIndexMissing = {
|
|
142
|
+
indexed: false;
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Enrich a project's concept layer, owning the store lifetime so callers
|
|
146
|
+
* outside this package never touch `indexStorePool`.
|
|
147
|
+
*
|
|
148
|
+
* Refuses to run without an existing index: opening a store CREATES the
|
|
149
|
+
* database, and spending money summarising an empty index helps nobody.
|
|
150
|
+
*/
|
|
151
|
+
export declare function enrichProjectConcepts(projectRoot: string, port: SummarizerPort, options?: EnrichOptions & {
|
|
152
|
+
indexDir?: string | undefined;
|
|
153
|
+
}): Promise<EnrichResult | ConceptIndexMissing>;
|
|
154
|
+
//# sourceMappingURL=concept-enrichment.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Personalised retrieval — "which files does this task touch?"
|
|
3
|
+
*
|
|
4
|
+
* Lexical search proposes, the graph disposes. A BM25/FTS query produces a set
|
|
5
|
+
* of seed symbols with scores; those scores become the restart distribution of
|
|
6
|
+
* a personalised PageRank walk over the wiring graph. The walk then surfaces
|
|
7
|
+
* what the seeds are structurally attached to: the interface a matched
|
|
8
|
+
* function implements, the module every matched call site imports, the store
|
|
9
|
+
* behind the handler that matched by name.
|
|
10
|
+
*
|
|
11
|
+
* This is the piece that turns the index from a set of primitives an agent has
|
|
12
|
+
* to compose by hand — search, then skeleton, then incoming-calls, then read —
|
|
13
|
+
* into one answer. The composition was previously re-derived by the model on
|
|
14
|
+
* every task, at the cost of several round trips and a great deal of judgement
|
|
15
|
+
* spent on plumbing rather than on the problem.
|
|
16
|
+
*
|
|
17
|
+
* What comes back is declarations, not source: file, relevance, and the
|
|
18
|
+
* matching symbols with their signatures and line numbers. Reading the actual
|
|
19
|
+
* code stays a deliberate `read` — the point is to make that read land in the
|
|
20
|
+
* right place the first time.
|
|
21
|
+
*/
|
|
22
|
+
import type { IndexStore } from './writer.js';
|
|
23
|
+
/** One symbol worth showing inside a returned file. */
|
|
24
|
+
export interface ContextSymbol {
|
|
25
|
+
name: string;
|
|
26
|
+
kind: string;
|
|
27
|
+
line: number;
|
|
28
|
+
signature: string;
|
|
29
|
+
/** True when lexical search matched this symbol directly. */
|
|
30
|
+
seed: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** One file in the answer, most relevant first. */
|
|
33
|
+
export interface ContextEntry {
|
|
34
|
+
/** Project-relative POSIX path. */
|
|
35
|
+
file: string;
|
|
36
|
+
/** Relevance to this query, normalised so the top entry is 1.0. */
|
|
37
|
+
relevance: number;
|
|
38
|
+
/** Whether any symbol here matched the query lexically. */
|
|
39
|
+
matched: boolean;
|
|
40
|
+
symbols: ContextSymbol[];
|
|
41
|
+
}
|
|
42
|
+
export interface ContextResult {
|
|
43
|
+
query: string;
|
|
44
|
+
entries: ContextEntry[];
|
|
45
|
+
/** Lexical hits that seeded the walk. Zero means nothing matched. */
|
|
46
|
+
seedCount: number;
|
|
47
|
+
/** Semantic hits that also seeded it. */
|
|
48
|
+
semanticSeedCount: number;
|
|
49
|
+
/** Files the walk reached before truncation to `limit`. */
|
|
50
|
+
totalCandidates: number;
|
|
51
|
+
indexStatus: 'ok' | 'no-index' | 'no-matches' | 'unranked';
|
|
52
|
+
}
|
|
53
|
+
export interface ContextOptions {
|
|
54
|
+
query: string;
|
|
55
|
+
/**
|
|
56
|
+
* Files a semantic search already matched, with their cosine scores.
|
|
57
|
+
*
|
|
58
|
+
* Supplied by the caller rather than computed here because the embedding
|
|
59
|
+
* model lives host-side — functions cannot cross the daemon's IPC boundary,
|
|
60
|
+
* so only the resulting file scores travel. These become additional restart
|
|
61
|
+
* mass, letting a query phrased in the problem's vocabulary reach code whose
|
|
62
|
+
* identifiers never use those words.
|
|
63
|
+
*/
|
|
64
|
+
vectorFiles?: ReadonlyArray<{
|
|
65
|
+
file: string;
|
|
66
|
+
score: number;
|
|
67
|
+
}> | undefined;
|
|
68
|
+
/** Files to return. */
|
|
69
|
+
limit?: number | undefined;
|
|
70
|
+
/** Symbols to show per file. */
|
|
71
|
+
symbolsPerFile?: number | undefined;
|
|
72
|
+
/** Restrict results to files under this project-relative prefix. */
|
|
73
|
+
pathPrefix?: string | undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Lexical hits used as restart mass. More seeds make the walk broader and
|
|
77
|
+
* blunter; this is enough to cover a multi-word query's separate senses
|
|
78
|
+
* without letting a common token dominate.
|
|
79
|
+
*/
|
|
80
|
+
export declare const SEED_LIMIT = 40;
|
|
81
|
+
export declare const DEFAULT_LIMIT = 12;
|
|
82
|
+
export declare const DEFAULT_SYMBOLS_PER_FILE = 4;
|
|
83
|
+
/**
|
|
84
|
+
* Run the personalised walk and group the winners into files.
|
|
85
|
+
*
|
|
86
|
+
* Returns an empty result rather than throwing whenever the index cannot
|
|
87
|
+
* answer — an unbuilt index and a query nobody matches are ordinary states,
|
|
88
|
+
* and `indexStatus` says which one happened.
|
|
89
|
+
*/
|
|
90
|
+
export declare function retrieveContext(store: IndexStore, projectRoot: string, indexDir: string | undefined, options: ContextOptions, relativeOf: (file: string) => string): ContextResult;
|
|
91
|
+
//# sourceMappingURL=context-retrieval.d.ts.map
|