@smeltjs/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +55 -0
- package/THIRD-PARTY.md +140 -0
- package/dist/apply.d.ts +145 -0
- package/dist/apply.d.ts.map +1 -0
- package/dist/apply.js +244 -0
- package/dist/apply.js.map +1 -0
- package/dist/cache/prefix.d.ts +164 -0
- package/dist/cache/prefix.d.ts.map +1 -0
- package/dist/cache/prefix.js +361 -0
- package/dist/cache/prefix.js.map +1 -0
- package/dist/cli/args.d.ts +71 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +308 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +161 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/config.d.ts +73 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +144 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/init.d.ts +56 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +449 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/report.d.ts +44 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +113 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/resolve.d.ts +103 -0
- package/dist/cli/resolve.d.ts.map +1 -0
- package/dist/cli/resolve.js +106 -0
- package/dist/cli/resolve.js.map +1 -0
- package/dist/cli/run.d.ts +99 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +351 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/detect.d.ts +16 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +18 -0
- package/dist/detect.js.map +1 -0
- package/dist/errors.d.ts +88 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +107 -0
- package/dist/errors.js.map +1 -0
- package/dist/hash.d.ts +13 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +16 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/lang/bash.d.ts +3 -0
- package/dist/lang/bash.d.ts.map +1 -0
- package/dist/lang/bash.js +28 -0
- package/dist/lang/bash.js.map +1 -0
- package/dist/lang/c.d.ts +3 -0
- package/dist/lang/c.d.ts.map +1 -0
- package/dist/lang/c.js +39 -0
- package/dist/lang/c.js.map +1 -0
- package/dist/lang/c_sharp.d.ts +3 -0
- package/dist/lang/c_sharp.d.ts.map +1 -0
- package/dist/lang/c_sharp.js +37 -0
- package/dist/lang/c_sharp.js.map +1 -0
- package/dist/lang/cpp.d.ts +3 -0
- package/dist/lang/cpp.d.ts.map +1 -0
- package/dist/lang/cpp.js +46 -0
- package/dist/lang/cpp.js.map +1 -0
- package/dist/lang/go.d.ts +3 -0
- package/dist/lang/go.d.ts.map +1 -0
- package/dist/lang/go.js +31 -0
- package/dist/lang/go.js.map +1 -0
- package/dist/lang/java.d.ts +3 -0
- package/dist/lang/java.d.ts.map +1 -0
- package/dist/lang/java.js +34 -0
- package/dist/lang/java.js.map +1 -0
- package/dist/lang/javascript.d.ts +3 -0
- package/dist/lang/javascript.d.ts.map +1 -0
- package/dist/lang/javascript.js +28 -0
- package/dist/lang/javascript.js.map +1 -0
- package/dist/lang/kotlin.d.ts +3 -0
- package/dist/lang/kotlin.d.ts.map +1 -0
- package/dist/lang/kotlin.js +37 -0
- package/dist/lang/kotlin.js.map +1 -0
- package/dist/lang/php.d.ts +3 -0
- package/dist/lang/php.d.ts.map +1 -0
- package/dist/lang/php.js +37 -0
- package/dist/lang/php.js.map +1 -0
- package/dist/lang/profile.d.ts +130 -0
- package/dist/lang/profile.d.ts.map +1 -0
- package/dist/lang/profile.js +2 -0
- package/dist/lang/profile.js.map +1 -0
- package/dist/lang/python.d.ts +3 -0
- package/dist/lang/python.d.ts.map +1 -0
- package/dist/lang/python.js +33 -0
- package/dist/lang/python.js.map +1 -0
- package/dist/lang/registry.d.ts +31 -0
- package/dist/lang/registry.d.ts.map +1 -0
- package/dist/lang/registry.js +89 -0
- package/dist/lang/registry.js.map +1 -0
- package/dist/lang/ruby.d.ts +3 -0
- package/dist/lang/ruby.d.ts.map +1 -0
- package/dist/lang/ruby.js +47 -0
- package/dist/lang/ruby.js.map +1 -0
- package/dist/lang/rust.d.ts +3 -0
- package/dist/lang/rust.d.ts.map +1 -0
- package/dist/lang/rust.js +45 -0
- package/dist/lang/rust.js.map +1 -0
- package/dist/lang/swift.d.ts +3 -0
- package/dist/lang/swift.d.ts.map +1 -0
- package/dist/lang/swift.js +34 -0
- package/dist/lang/swift.js.map +1 -0
- package/dist/lang/tsx.d.ts +4 -0
- package/dist/lang/tsx.d.ts.map +1 -0
- package/dist/lang/tsx.js +11 -0
- package/dist/lang/tsx.js.map +1 -0
- package/dist/lang/typescript.d.ts +18 -0
- package/dist/lang/typescript.d.ts.map +1 -0
- package/dist/lang/typescript.js +61 -0
- package/dist/lang/typescript.js.map +1 -0
- package/dist/net/policy.d.ts +49 -0
- package/dist/net/policy.d.ts.map +1 -0
- package/dist/net/policy.js +120 -0
- package/dist/net/policy.js.map +1 -0
- package/dist/plan/grammar.d.ts +38 -0
- package/dist/plan/grammar.d.ts.map +1 -0
- package/dist/plan/grammar.js +85 -0
- package/dist/plan/grammar.js.map +1 -0
- package/dist/plan/lexical.d.ts +51 -0
- package/dist/plan/lexical.d.ts.map +1 -0
- package/dist/plan/lexical.js +167 -0
- package/dist/plan/lexical.js.map +1 -0
- package/dist/plan/planners.d.ts +35 -0
- package/dist/plan/planners.d.ts.map +1 -0
- package/dist/plan/planners.js +26 -0
- package/dist/plan/planners.js.map +1 -0
- package/dist/plan/structural.d.ts +68 -0
- package/dist/plan/structural.d.ts.map +1 -0
- package/dist/plan/structural.js +458 -0
- package/dist/plan/structural.js.map +1 -0
- package/dist/repomap/cache.d.ts +53 -0
- package/dist/repomap/cache.d.ts.map +1 -0
- package/dist/repomap/cache.js +139 -0
- package/dist/repomap/cache.js.map +1 -0
- package/dist/repomap/map.d.ts +161 -0
- package/dist/repomap/map.d.ts.map +1 -0
- package/dist/repomap/map.js +302 -0
- package/dist/repomap/map.js.map +1 -0
- package/dist/repomap/rank.d.ts +52 -0
- package/dist/repomap/rank.d.ts.map +1 -0
- package/dist/repomap/rank.js +155 -0
- package/dist/repomap/rank.js.map +1 -0
- package/dist/repomap/tags.d.ts +47 -0
- package/dist/repomap/tags.d.ts.map +1 -0
- package/dist/repomap/tags.js +101 -0
- package/dist/repomap/tags.js.map +1 -0
- package/dist/retrieve.d.ts +14 -0
- package/dist/retrieve.d.ts.map +1 -0
- package/dist/retrieve.js +44 -0
- package/dist/retrieve.js.map +1 -0
- package/dist/stages.d.ts +22 -0
- package/dist/stages.d.ts.map +1 -0
- package/dist/stages.js +33 -0
- package/dist/stages.js.map +1 -0
- package/dist/stats.d.ts +40 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +37 -0
- package/dist/stats.js.map +1 -0
- package/dist/store-dir.d.ts +98 -0
- package/dist/store-dir.d.ts.map +1 -0
- package/dist/store-dir.js +379 -0
- package/dist/store-dir.js.map +1 -0
- package/dist/store.d.ts +36 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +56 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +298 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +68 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { FileTags } from './tags.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic PageRank over the cross-file reference graph.
|
|
4
|
+
*
|
|
5
|
+
* Modelled on Aider's repo-map ranking (https://aider.chat/docs/repomap.html; see
|
|
6
|
+
* `aider/repomap.py` in https://github.com/Aider-AI/aider): files are the graph's
|
|
7
|
+
* nodes, and a file that references an identifier defined in another file contributes
|
|
8
|
+
* a weighted edge toward the definer. A file's rank is then distributed across its
|
|
9
|
+
* definitions in proportion to the reference weight each one attracted. The idea is
|
|
10
|
+
* Aider's; only the implementation is this repository's.
|
|
11
|
+
*
|
|
12
|
+
* Deterministic by construction, which is what makes the map snapshot-testable:
|
|
13
|
+
* fixed damping, a fixed iteration count, nodes visited in sorted-path order, and a
|
|
14
|
+
* total tie-break (rank, then path, then name, then line). No `Math.random`, no
|
|
15
|
+
* `Date`, no map-iteration order leaking into the output.
|
|
16
|
+
*/
|
|
17
|
+
/** The damping factor. 0.85 is the classic PageRank constant; fixed, never sampled. */
|
|
18
|
+
export declare const PAGERANK_DAMPING = 0.85;
|
|
19
|
+
/** Fixed iteration count — no convergence test, so runtime and output never vary. */
|
|
20
|
+
export declare const PAGERANK_ITERATIONS = 50;
|
|
21
|
+
/** What the caller hands the ranker: one file's path and its extracted tags. */
|
|
22
|
+
export interface FileTagsEntry {
|
|
23
|
+
readonly path: string;
|
|
24
|
+
readonly tags: FileTags;
|
|
25
|
+
}
|
|
26
|
+
/** One definition with its computed rank and the measured counts that explain it. */
|
|
27
|
+
export interface RankedDefinition {
|
|
28
|
+
readonly path: string;
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly kind: string;
|
|
31
|
+
readonly line: number;
|
|
32
|
+
/** Accumulated PageRank share. `0` when no other file references this name. */
|
|
33
|
+
readonly rank: number;
|
|
34
|
+
/** Total references to this name across every scanned file (its own included). */
|
|
35
|
+
readonly refsIn: number;
|
|
36
|
+
/** Distinct files holding at least one of those references. */
|
|
37
|
+
readonly refsInFiles: number;
|
|
38
|
+
/** References the defining file makes to names defined in *other* files. */
|
|
39
|
+
readonly refsOut: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Rank every definition in the tag set.
|
|
43
|
+
*
|
|
44
|
+
* Only cross-file references move rank — a file citing its own definitions does not
|
|
45
|
+
* boost itself — but `refsIn`/`refsInFiles` count every reference, so the explanation
|
|
46
|
+
* a symbol carries never hides same-file usage.
|
|
47
|
+
*
|
|
48
|
+
* Returns all definitions (rank `0` included), sorted by rank descending with the
|
|
49
|
+
* stable tie-break: path ascending, then name ascending, then line ascending.
|
|
50
|
+
*/
|
|
51
|
+
export declare function rankDefinitions(files: readonly FileTagsEntry[]): readonly RankedDefinition[];
|
|
52
|
+
//# sourceMappingURL=rank.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.d.ts","sourceRoot":"","sources":["../../src/repomap/rank.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AAEH,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAkH5F"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic PageRank over the cross-file reference graph.
|
|
3
|
+
*
|
|
4
|
+
* Modelled on Aider's repo-map ranking (https://aider.chat/docs/repomap.html; see
|
|
5
|
+
* `aider/repomap.py` in https://github.com/Aider-AI/aider): files are the graph's
|
|
6
|
+
* nodes, and a file that references an identifier defined in another file contributes
|
|
7
|
+
* a weighted edge toward the definer. A file's rank is then distributed across its
|
|
8
|
+
* definitions in proportion to the reference weight each one attracted. The idea is
|
|
9
|
+
* Aider's; only the implementation is this repository's.
|
|
10
|
+
*
|
|
11
|
+
* Deterministic by construction, which is what makes the map snapshot-testable:
|
|
12
|
+
* fixed damping, a fixed iteration count, nodes visited in sorted-path order, and a
|
|
13
|
+
* total tie-break (rank, then path, then name, then line). No `Math.random`, no
|
|
14
|
+
* `Date`, no map-iteration order leaking into the output.
|
|
15
|
+
*/
|
|
16
|
+
/** The damping factor. 0.85 is the classic PageRank constant; fixed, never sampled. */
|
|
17
|
+
export const PAGERANK_DAMPING = 0.85;
|
|
18
|
+
/** Fixed iteration count — no convergence test, so runtime and output never vary. */
|
|
19
|
+
export const PAGERANK_ITERATIONS = 50;
|
|
20
|
+
/**
|
|
21
|
+
* Rank every definition in the tag set.
|
|
22
|
+
*
|
|
23
|
+
* Only cross-file references move rank — a file citing its own definitions does not
|
|
24
|
+
* boost itself — but `refsIn`/`refsInFiles` count every reference, so the explanation
|
|
25
|
+
* a symbol carries never hides same-file usage.
|
|
26
|
+
*
|
|
27
|
+
* Returns all definitions (rank `0` included), sorted by rank descending with the
|
|
28
|
+
* stable tie-break: path ascending, then name ascending, then line ascending.
|
|
29
|
+
*/
|
|
30
|
+
export function rankDefinitions(files) {
|
|
31
|
+
const sorted = [...files].toSorted((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
32
|
+
const paths = sorted.map((file) => file.path);
|
|
33
|
+
const defsByName = new Map();
|
|
34
|
+
const allDefs = [];
|
|
35
|
+
for (const { path, tags } of sorted) {
|
|
36
|
+
for (const def of tags.defs) {
|
|
37
|
+
const mutable = { path, name: def.name, kind: def.kind, line: def.line, rank: 0 };
|
|
38
|
+
allDefs.push(mutable);
|
|
39
|
+
const existing = defsByName.get(def.name);
|
|
40
|
+
if (existing === undefined)
|
|
41
|
+
defsByName.set(def.name, [mutable]);
|
|
42
|
+
else
|
|
43
|
+
existing.push(mutable);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Cross-file edges: referencing file → defining file, weighted by reference count.
|
|
47
|
+
// outWeight is each file's total outgoing weight — the PageRank edge denominator,
|
|
48
|
+
// which legitimately grows once per definer file so each edge keeps its full weight.
|
|
49
|
+
// refsOutByFile is the *measured* count a Law 2 explanation may quote: it adds
|
|
50
|
+
// `ref.count` exactly once per reference to a name some other file defines, no
|
|
51
|
+
// matter how many files define it. edgeWeights aggregates per file pair for the
|
|
52
|
+
// PageRank pass, while rank distribution below re-walks the refs so each
|
|
53
|
+
// *definition* receives its own share.
|
|
54
|
+
const outWeight = new Map(paths.map((path) => [path, 0]));
|
|
55
|
+
const refsOutByFile = new Map(paths.map((path) => [path, 0]));
|
|
56
|
+
const edgeWeights = new Map(paths.map((path) => [path, new Map()]));
|
|
57
|
+
const refsInTotal = new Map();
|
|
58
|
+
const refsInFiles = new Map();
|
|
59
|
+
for (const { path, tags } of sorted) {
|
|
60
|
+
for (const ref of tags.refs) {
|
|
61
|
+
const definers = defsByName.get(ref.name);
|
|
62
|
+
if (definers === undefined)
|
|
63
|
+
continue;
|
|
64
|
+
refsInTotal.set(ref.name, (refsInTotal.get(ref.name) ?? 0) + ref.count);
|
|
65
|
+
refsInFiles.set(ref.name, (refsInFiles.get(ref.name) ?? 0) + 1);
|
|
66
|
+
const targets = new Set(definers.map((def) => def.path));
|
|
67
|
+
let crossFile = false;
|
|
68
|
+
for (const target of [...targets].toSorted()) {
|
|
69
|
+
if (target === path)
|
|
70
|
+
continue;
|
|
71
|
+
crossFile = true;
|
|
72
|
+
outWeight.set(path, (outWeight.get(path) ?? 0) + ref.count);
|
|
73
|
+
const row = edgeWeights.get(path);
|
|
74
|
+
row.set(target, (row.get(target) ?? 0) + ref.count);
|
|
75
|
+
}
|
|
76
|
+
if (crossFile)
|
|
77
|
+
refsOutByFile.set(path, (refsOutByFile.get(path) ?? 0) + ref.count);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// PageRank, fixed iterations. Dangling files (no outgoing weight) spread their rank
|
|
81
|
+
// uniformly, the standard treatment; every sum runs in sorted-path order so the
|
|
82
|
+
// floating-point result is identical on every run.
|
|
83
|
+
const nodeCount = paths.length;
|
|
84
|
+
let rank = new Map(paths.map((path) => [path, nodeCount === 0 ? 0 : 1 / nodeCount]));
|
|
85
|
+
for (let iteration = 0; iteration < PAGERANK_ITERATIONS; iteration += 1) {
|
|
86
|
+
let danglingSum = 0;
|
|
87
|
+
for (const path of paths) {
|
|
88
|
+
if (outWeight.get(path) === 0)
|
|
89
|
+
danglingSum += rank.get(path);
|
|
90
|
+
}
|
|
91
|
+
const next = new Map();
|
|
92
|
+
const base = nodeCount === 0
|
|
93
|
+
? 0
|
|
94
|
+
: (1 - PAGERANK_DAMPING) / nodeCount + (PAGERANK_DAMPING * danglingSum) / nodeCount;
|
|
95
|
+
for (const path of paths)
|
|
96
|
+
next.set(path, base);
|
|
97
|
+
for (const source of paths) {
|
|
98
|
+
const total = outWeight.get(source);
|
|
99
|
+
if (total === 0)
|
|
100
|
+
continue;
|
|
101
|
+
const share = (PAGERANK_DAMPING * rank.get(source)) / total;
|
|
102
|
+
for (const [target, weight] of edgeWeights.get(source)) {
|
|
103
|
+
next.set(target, next.get(target) + share * weight);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
rank = next;
|
|
107
|
+
}
|
|
108
|
+
// Distribute each file's rank across the definitions that attracted it, exactly the
|
|
109
|
+
// way Aider does: every cross-file reference edge hands the referencing file's rank,
|
|
110
|
+
// scaled by the edge's share of that file's outgoing weight, to the definition it
|
|
111
|
+
// points at.
|
|
112
|
+
for (const { path, tags } of sorted) {
|
|
113
|
+
const total = outWeight.get(path);
|
|
114
|
+
if (total === 0)
|
|
115
|
+
continue;
|
|
116
|
+
const sourceRank = rank.get(path);
|
|
117
|
+
for (const ref of tags.refs) {
|
|
118
|
+
const definers = defsByName.get(ref.name);
|
|
119
|
+
if (definers === undefined)
|
|
120
|
+
continue;
|
|
121
|
+
for (const def of definers) {
|
|
122
|
+
if (def.path === path)
|
|
123
|
+
continue;
|
|
124
|
+
def.rank += (sourceRank * ref.count) / total;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const ranked = allDefs.map((def) => ({
|
|
129
|
+
path: def.path,
|
|
130
|
+
name: def.name,
|
|
131
|
+
kind: def.kind,
|
|
132
|
+
line: def.line,
|
|
133
|
+
rank: def.rank,
|
|
134
|
+
refsIn: refsInTotal.get(def.name) ?? 0,
|
|
135
|
+
refsInFiles: refsInFiles.get(def.name) ?? 0,
|
|
136
|
+
refsOut: refsOutByFile.get(def.path) ?? 0,
|
|
137
|
+
}));
|
|
138
|
+
return ranked.toSorted(compareRanked);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The total order every emitted map obeys: rank descending, then the stable
|
|
142
|
+
* tie-break by path, name, and line. Total, so equal-rank symbols land in the same
|
|
143
|
+
* place on every run and on every machine — remove any leg of it and the map's
|
|
144
|
+
* byte-for-byte determinism claim quietly dies.
|
|
145
|
+
*/
|
|
146
|
+
function compareRanked(a, b) {
|
|
147
|
+
if (a.rank !== b.rank)
|
|
148
|
+
return a.rank > b.rank ? -1 : 1;
|
|
149
|
+
if (a.path !== b.path)
|
|
150
|
+
return a.path < b.path ? -1 : 1;
|
|
151
|
+
if (a.name !== b.name)
|
|
152
|
+
return a.name < b.name ? -1 : 1;
|
|
153
|
+
return a.line - b.line;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=rank.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../src/repomap/rank.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { LanguageId } from '../types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Definition/reference tag extraction for the repo map — one file at a time, through
|
|
4
|
+
* the same bundled tree-sitter grammars the structural planner uses.
|
|
5
|
+
*
|
|
6
|
+
* Modelled on Aider's repo-map tags (https://aider.chat/docs/repomap.html): a *def* is
|
|
7
|
+
* a named declaration read off the parse tree, a *ref* is an identifier occurrence.
|
|
8
|
+
* Aider extracts both with per-language `.scm` query files; here the same facts come
|
|
9
|
+
* from a manual tree walk over each language's `repomap` profile section
|
|
10
|
+
* (`src/lang/`), so no query assets are added. The design is Aider's, not this
|
|
11
|
+
* project's.
|
|
12
|
+
*
|
|
13
|
+
* Honest scope, stated rather than implied: refs count `identifier`/`type_identifier`
|
|
14
|
+
* nodes only, so a member access like `config.load()` contributes `config`, not
|
|
15
|
+
* `load` — method references travel through the object they are called on. A def whose
|
|
16
|
+
* name is a destructuring pattern is skipped rather than guessed at.
|
|
17
|
+
*/
|
|
18
|
+
/** One named definition, read off the parse tree. `line` is 1-based. */
|
|
19
|
+
export interface DefinitionTag {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/** Human word for the node kind, e.g. `'function'` — same register as the planners. */
|
|
22
|
+
readonly kind: string;
|
|
23
|
+
readonly line: number;
|
|
24
|
+
}
|
|
25
|
+
/** One referenced identifier and how many times this file references it. */
|
|
26
|
+
export interface ReferenceTag {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly count: number;
|
|
29
|
+
}
|
|
30
|
+
/** Everything the repo map needs to know about one file. */
|
|
31
|
+
export interface FileTags {
|
|
32
|
+
/** In document order. */
|
|
33
|
+
readonly defs: readonly DefinitionTag[];
|
|
34
|
+
/** Sorted by name, so the extraction is deterministic end to end. */
|
|
35
|
+
readonly refs: readonly ReferenceTag[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract definition and reference tags from one file.
|
|
39
|
+
*
|
|
40
|
+
* Deterministic: same text, same language, identical tags — defs in document order,
|
|
41
|
+
* refs sorted by name. A def's own name node is not counted as a reference to itself.
|
|
42
|
+
*
|
|
43
|
+
* @throws {GrammarUnavailableError} when the grammar cannot load or produces no tree.
|
|
44
|
+
* Never a lexical guess — same no-fallback rule as the structural planner.
|
|
45
|
+
*/
|
|
46
|
+
export declare function extractTags(text: string, language: LanguageId): Promise<FileTags>;
|
|
47
|
+
//# sourceMappingURL=tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/repomap/tags.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AAEH,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,4DAA4D;AAC5D,MAAM,WAAW,QAAQ;IACvB,yBAAyB;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IACxC,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAC;CACxC;AAiCD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8DvF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Parser } from 'web-tree-sitter';
|
|
2
|
+
import { GrammarUnavailableError } from '../errors.js';
|
|
3
|
+
import { profileFor } from '../lang/registry.js';
|
|
4
|
+
import { loadGrammar } from '../plan/grammar.js';
|
|
5
|
+
/**
|
|
6
|
+
* C/C++ specifier node types that name a *usage site* as readily as a definition:
|
|
7
|
+
* `struct point p;` parses as a `struct_specifier` with a `name` and no `body`, same
|
|
8
|
+
* as the `struct point { … };` that actually defines it. Only the bodied form is a
|
|
9
|
+
* definition. Without this check a mere mention earns a `defined at` receipt, *and*
|
|
10
|
+
* its name node lands in `defNameStarts` — so the real definition's cross-file
|
|
11
|
+
* references silently vanish from the map.
|
|
12
|
+
*/
|
|
13
|
+
const BODY_REQUIRED_TYPES = new Set([
|
|
14
|
+
'struct_specifier',
|
|
15
|
+
'union_specifier',
|
|
16
|
+
'enum_specifier',
|
|
17
|
+
'class_specifier',
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* A local `const x = …` inside a function body is not a map-worthy symbol. A
|
|
21
|
+
* `variable_declarator` counts as a definition only at module top level — directly
|
|
22
|
+
* under the program node, or under an `export` statement that is.
|
|
23
|
+
*/
|
|
24
|
+
function isDefinitionSite(node) {
|
|
25
|
+
if (BODY_REQUIRED_TYPES.has(node.type) && node.childForFieldName('body') === null) {
|
|
26
|
+
return false; // a bodiless specifier is a usage or forward declaration, not a definition
|
|
27
|
+
}
|
|
28
|
+
if (node.type !== 'variable_declarator')
|
|
29
|
+
return true;
|
|
30
|
+
const container = node.parent?.parent;
|
|
31
|
+
if (container === null || container === undefined)
|
|
32
|
+
return false;
|
|
33
|
+
if (container.type === 'program')
|
|
34
|
+
return true;
|
|
35
|
+
return container.type === 'export_statement' && container.parent?.type === 'program';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract definition and reference tags from one file.
|
|
39
|
+
*
|
|
40
|
+
* Deterministic: same text, same language, identical tags — defs in document order,
|
|
41
|
+
* refs sorted by name. A def's own name node is not counted as a reference to itself.
|
|
42
|
+
*
|
|
43
|
+
* @throws {GrammarUnavailableError} when the grammar cannot load or produces no tree.
|
|
44
|
+
* Never a lexical guess — same no-fallback rule as the structural planner.
|
|
45
|
+
*/
|
|
46
|
+
export async function extractTags(text, language) {
|
|
47
|
+
const grammar = await loadGrammar(language);
|
|
48
|
+
// The per-language facts live on the language's profile (`src/lang/`). A profile
|
|
49
|
+
// without a repomap section contributes nothing — no symbols, never wrong ones.
|
|
50
|
+
const repomap = profileFor(language).repomap;
|
|
51
|
+
const defKinds = repomap?.defKinds ?? {};
|
|
52
|
+
const refTypes = repomap?.refTypes ?? [];
|
|
53
|
+
const parser = new Parser();
|
|
54
|
+
let tree = null;
|
|
55
|
+
try {
|
|
56
|
+
parser.setLanguage(grammar);
|
|
57
|
+
tree = parser.parse(text);
|
|
58
|
+
if (tree === null) {
|
|
59
|
+
throw new GrammarUnavailableError(`smelt: the ${language} parser returned no tree, so no tags can be extracted. ` +
|
|
60
|
+
`Refusing to guess at definitions that were never parsed.`);
|
|
61
|
+
}
|
|
62
|
+
const defs = [];
|
|
63
|
+
const refCounts = new Map();
|
|
64
|
+
/** Start indices of def-name nodes, so a definition never references itself. */
|
|
65
|
+
const defNameStarts = new Set();
|
|
66
|
+
// Depth-first, document order, via an explicit stack — recursion depth on a real
|
|
67
|
+
// file is the tree's depth, which nothing bounds.
|
|
68
|
+
const stack = [tree.rootNode];
|
|
69
|
+
while (stack.length > 0) {
|
|
70
|
+
const node = stack.pop();
|
|
71
|
+
const kind = defKinds[node.type];
|
|
72
|
+
if (kind !== undefined && isDefinitionSite(node)) {
|
|
73
|
+
const nameNode = node.childForFieldName('name');
|
|
74
|
+
// Only a plain identifier names a def; a destructuring pattern is skipped, not
|
|
75
|
+
// guessed at.
|
|
76
|
+
if (nameNode !== null && nameNode.type.endsWith('identifier')) {
|
|
77
|
+
defs.push({ name: nameNode.text, kind, line: node.startPosition.row + 1 });
|
|
78
|
+
defNameStarts.add(nameNode.startIndex);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (refTypes.includes(node.type) && !defNameStarts.has(node.startIndex)) {
|
|
82
|
+
refCounts.set(node.text, (refCounts.get(node.text) ?? 0) + 1);
|
|
83
|
+
}
|
|
84
|
+
const children = node.namedChildren;
|
|
85
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
86
|
+
const child = children[i];
|
|
87
|
+
if (child !== null && child !== undefined)
|
|
88
|
+
stack.push(child);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const refs = [...refCounts.entries()]
|
|
92
|
+
.toSorted(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
93
|
+
.map(([name, count]) => ({ name, count }));
|
|
94
|
+
return { defs, refs };
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
tree?.delete();
|
|
98
|
+
parser.delete();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/repomap/tags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA0CjD;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IACvD,kBAAkB;IAClB,iBAAiB;IACjB,gBAAgB;IAChB,iBAAiB;CAClB,CAAC,CAAC;AAEH;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,IAAU;IAClC,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QAClF,OAAO,KAAK,CAAC,CAAC,2EAA2E;IAC3F,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,qBAAqB;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;IACtC,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAChE,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,SAAS,CAAC,IAAI,KAAK,kBAAkB,IAAI,SAAS,CAAC,MAAM,EAAE,IAAI,KAAK,SAAS,CAAC;AACvF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,QAAoB;IAClE,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC5C,iFAAiF;IACjF,gFAAgF;IAChF,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;IAC7C,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,EAAE,CAAC;IAEzC,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IAC5B,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,uBAAuB,CAC/B,cAAc,QAAQ,yDAAyD;gBAC7E,0DAA0D,CAC7D,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAoB,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC5C,gFAAgF;QAChF,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;QAExC,iFAAiF;QACjF,kDAAkD;QAClD,MAAM,KAAK,GAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;YAE1B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,IAAI,KAAK,SAAS,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;gBAChD,+EAA+E;gBAC/E,cAAc;gBACd,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;oBAC9D,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;oBAC3E,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;YAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACxE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;oBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;aAClC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAE7C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;YAAS,CAAC;QACT,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,EAAE,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ElisionStore, RetrieveTool } from './types.ts';
|
|
2
|
+
/** The tool name smelt's markers reference. Consumers hard-code it; do not rename it. */
|
|
3
|
+
export declare const RETRIEVE_TOOL_NAME = "smelt_retrieve";
|
|
4
|
+
/**
|
|
5
|
+
* Wrap a store as the tool a model calls.
|
|
6
|
+
*
|
|
7
|
+
* Why the description says "ask whenever it might matter": under-retrieval is the
|
|
8
|
+
* failure mode that looks like success. A model that never calls this produces a
|
|
9
|
+
* confident answer built on material it never saw, and the retrieve rate reads 0% —
|
|
10
|
+
* which is indistinguishable from perfect pruning. Encouraging retrieval keeps the
|
|
11
|
+
* signal in {@link ElisionStore.stats} honest.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createRetrieveTool(store: ElisionStore): RetrieveTool;
|
|
14
|
+
//# sourceMappingURL=retrieve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieve.d.ts","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7D,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAwBnD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAWpE"}
|
package/dist/retrieve.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { defaultMarker } from './apply.js';
|
|
2
|
+
/** The tool name smelt's markers reference. Consumers hard-code it; do not rename it. */
|
|
3
|
+
export const RETRIEVE_TOOL_NAME = 'smelt_retrieve';
|
|
4
|
+
/**
|
|
5
|
+
* The example marker inside the tool description is *rendered by the real marker
|
|
6
|
+
* builder*, never hand-written — the description is the one string a model reads to
|
|
7
|
+
* recognize markers, and an example whose shape drifted from the wire format
|
|
8
|
+
* (`<<smelt: …>>` when real markers say `<<smelt/v1: …>>`) would teach the model to
|
|
9
|
+
* miss every marker it actually receives. `test/guards/marker-format.test.ts` pins
|
|
10
|
+
* this to `MARKER_FORMAT_VERSION`.
|
|
11
|
+
*/
|
|
12
|
+
const EXAMPLE_MARKER = defaultMarker({
|
|
13
|
+
hash: 'a1b2c3d4e5f60718',
|
|
14
|
+
bytes: 412,
|
|
15
|
+
rule: 'sibling-collapse',
|
|
16
|
+
explanation: 'collapsed 3 sibling functions',
|
|
17
|
+
});
|
|
18
|
+
const DESCRIPTION = 'Return the exact original text that was elided from a previous tool result. ' +
|
|
19
|
+
`Context you were given may contain markers like \`${EXAMPLE_MARKER}\`. ` +
|
|
20
|
+
'Call this with that hash to get those bytes ' +
|
|
21
|
+
'back verbatim. Nothing was deleted — it is all still here. Ask whenever the elided ' +
|
|
22
|
+
'material might matter; guessing at what a marker hid is never correct.';
|
|
23
|
+
/**
|
|
24
|
+
* Wrap a store as the tool a model calls.
|
|
25
|
+
*
|
|
26
|
+
* Why the description says "ask whenever it might matter": under-retrieval is the
|
|
27
|
+
* failure mode that looks like success. A model that never calls this produces a
|
|
28
|
+
* confident answer built on material it never saw, and the retrieve rate reads 0% —
|
|
29
|
+
* which is indistinguishable from perfect pruning. Encouraging retrieval keeps the
|
|
30
|
+
* signal in {@link ElisionStore.stats} honest.
|
|
31
|
+
*/
|
|
32
|
+
export function createRetrieveTool(store) {
|
|
33
|
+
return {
|
|
34
|
+
name: RETRIEVE_TOOL_NAME,
|
|
35
|
+
description: DESCRIPTION,
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: { hash: { type: 'string' } },
|
|
39
|
+
required: ['hash'],
|
|
40
|
+
},
|
|
41
|
+
invoke: ({ hash }) => store.retrieve(hash),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=retrieve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,aAAa,CAAC;IACnC,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,+BAA+B;CAC7C,CAAC,CAAC;AAEH,MAAM,WAAW,GACf,8EAA8E;IAC9E,qDAAqD,cAAc,MAAM;IACzE,8CAA8C;IAC9C,qFAAqF;IACrF,wEAAwE,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,WAAW;QACxB,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YACxC,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;QACD,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;KAC3C,CAAC;AACJ,CAAC"}
|
package/dist/stages.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DistillStage, RerankStage } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The reranker that ships with smelt: one that refuses.
|
|
4
|
+
*
|
|
5
|
+
* It exists so that "reranking is a seam, not a feature" is enforced rather than
|
|
6
|
+
* promised. Wire this in and you get an exception naming the interface you were
|
|
7
|
+
* supposed to implement. There is no default hosted reranker, no bundled key handling,
|
|
8
|
+
* and no `SMELT_RERANK_API_KEY` — the first of those to appear breaks Law 1 for every
|
|
9
|
+
* consumer at once, including the ones who never read the changelog.
|
|
10
|
+
*/
|
|
11
|
+
export declare const unconfiguredRerankStage: RerankStage;
|
|
12
|
+
/**
|
|
13
|
+
* Learned distillation, same treatment, for a different reason.
|
|
14
|
+
*
|
|
15
|
+
* Distillation is out of v1 because a model-written summary cannot satisfy Law 2. "The
|
|
16
|
+
* model condensed this" does not say what was removed, and once the text is rewritten
|
|
17
|
+
* there is nothing left to store under a hash. If this ever ships it will store the
|
|
18
|
+
* original, explain itself in the same rule-named terms every other elision uses, and
|
|
19
|
+
* be reversible — or it will not ship.
|
|
20
|
+
*/
|
|
21
|
+
export declare const unconfiguredDistillStage: DistillStage;
|
|
22
|
+
//# sourceMappingURL=stages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stages.d.ts","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAmB,WAAW,EAAqB,MAAM,YAAY,CAAC;AAEhG;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAYrC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,EAAE,YAQtC,CAAC"}
|
package/dist/stages.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NotImplementedError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* The reranker that ships with smelt: one that refuses.
|
|
4
|
+
*
|
|
5
|
+
* It exists so that "reranking is a seam, not a feature" is enforced rather than
|
|
6
|
+
* promised. Wire this in and you get an exception naming the interface you were
|
|
7
|
+
* supposed to implement. There is no default hosted reranker, no bundled key handling,
|
|
8
|
+
* and no `SMELT_RERANK_API_KEY` — the first of those to appear breaks Law 1 for every
|
|
9
|
+
* consumer at once, including the ones who never read the changelog.
|
|
10
|
+
*/
|
|
11
|
+
export const unconfiguredRerankStage = {
|
|
12
|
+
id: 'rerank/unconfigured',
|
|
13
|
+
rerank(_candidates, _query) {
|
|
14
|
+
throw new NotImplementedError('reranking', 'docs/HANDOFF.md § "Explicitly out of v1" — implement `RerankStage` in your own ' +
|
|
15
|
+
'code, with your own key, so the network call is visible in your source');
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Learned distillation, same treatment, for a different reason.
|
|
20
|
+
*
|
|
21
|
+
* Distillation is out of v1 because a model-written summary cannot satisfy Law 2. "The
|
|
22
|
+
* model condensed this" does not say what was removed, and once the text is rewritten
|
|
23
|
+
* there is nothing left to store under a hash. If this ever ships it will store the
|
|
24
|
+
* original, explain itself in the same rule-named terms every other elision uses, and
|
|
25
|
+
* be reversible — or it will not ship.
|
|
26
|
+
*/
|
|
27
|
+
export const unconfiguredDistillStage = {
|
|
28
|
+
id: 'distill/unconfigured',
|
|
29
|
+
distill(_text, _budgetBytes) {
|
|
30
|
+
throw new NotImplementedError('learned distillation', 'docs/HANDOFF.md § "Explicitly out of v1"');
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=stages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stages.js","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAgB;IAClD,EAAE,EAAE,qBAAqB;IACzB,MAAM,CACJ,WAAuC,EACvC,MAAc;QAEd,MAAM,IAAI,mBAAmB,CAC3B,WAAW,EACX,iFAAiF;YAC/E,wEAAwE,CAC3E,CAAC;IACJ,CAAC;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAiB;IACpD,EAAE,EAAE,sBAAsB;IAC1B,OAAO,CAAC,KAAa,EAAE,YAAoB;QACzC,MAAM,IAAI,mBAAmB,CAC3B,sBAAsB,EACtB,0CAA0C,CAC3C,CAAC;IACJ,CAAC;CACF,CAAC"}
|
package/dist/stats.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { RetrieveStats } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The directly-observed half of {@link RetrieveStats}: the five counts a store reads
|
|
4
|
+
* off its own records — a map size, a directory scan, a journal fold. Nothing in here
|
|
5
|
+
* is derived; every field is a fact the store witnessed. The derived half —
|
|
6
|
+
* `expansionRate` and `allElisionsRetrieved`, the honesty arithmetic of Law 3 — is
|
|
7
|
+
* computed from these by {@link retrieveStats}, in exactly one place.
|
|
8
|
+
*/
|
|
9
|
+
export type RawRetrieveCounters = Omit<RetrieveStats, 'expansionRate' | 'allElisionsRetrieved'>;
|
|
10
|
+
/**
|
|
11
|
+
* The one derivation of the honesty arithmetic, shared by every store.
|
|
12
|
+
*
|
|
13
|
+
* `expansionRate` is the number this project exists to keep honest, and
|
|
14
|
+
* `allElisionsRetrieved` is the one degenerate outcome it names (HANDOFF Decision 4).
|
|
15
|
+
* When each store derived them privately, the two copies could drift — and a store
|
|
16
|
+
* whose arithmetic drifted flattering-ward would be the exact silent failure Law 3's
|
|
17
|
+
* counters exist to refuse. So the seam between a store and its stats is narrowed to
|
|
18
|
+
* raw counters: an {@link ElisionStore} adapter *supplies counts*
|
|
19
|
+
* ({@link RawRetrieveCounters}) and never derives the metric.
|
|
20
|
+
*
|
|
21
|
+
* **Contract for adapter authors.** A custom store implements a
|
|
22
|
+
* `rawCounters(): RawRetrieveCounters` method (or any equivalent that gathers the five
|
|
23
|
+
* counts from its own records) and delegates its public `stats()` to this function:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* stats(): RetrieveStats {
|
|
27
|
+
* return retrieveStats(this.rawCounters());
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* This is a free wrapper function rather than an abstract base class, deliberately —
|
|
32
|
+
* it is the least-breaking shape. The stores share no storage machinery (one is a
|
|
33
|
+
* `Map`, one is a directory), so a base class would couple every adapter's inheritance
|
|
34
|
+
* chain to smelt's for the sake of two lines of arithmetic; an existing `ElisionStore`
|
|
35
|
+
* implementation keeps its own hierarchy and adopts this contract by changing only its
|
|
36
|
+
* `stats()` body. The {@link ElisionStore} interface itself is unchanged: consumers
|
|
37
|
+
* still call `stats()` and never see this seam.
|
|
38
|
+
*/
|
|
39
|
+
export declare function retrieveStats(raw: RawRetrieveCounters): RetrieveStats;
|
|
40
|
+
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,aAAa,EAAE,eAAe,GAAG,sBAAsB,CAAC,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,mBAAmB,GAAG,aAAa,CAMrE"}
|
package/dist/stats.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one derivation of the honesty arithmetic, shared by every store.
|
|
3
|
+
*
|
|
4
|
+
* `expansionRate` is the number this project exists to keep honest, and
|
|
5
|
+
* `allElisionsRetrieved` is the one degenerate outcome it names (HANDOFF Decision 4).
|
|
6
|
+
* When each store derived them privately, the two copies could drift — and a store
|
|
7
|
+
* whose arithmetic drifted flattering-ward would be the exact silent failure Law 3's
|
|
8
|
+
* counters exist to refuse. So the seam between a store and its stats is narrowed to
|
|
9
|
+
* raw counters: an {@link ElisionStore} adapter *supplies counts*
|
|
10
|
+
* ({@link RawRetrieveCounters}) and never derives the metric.
|
|
11
|
+
*
|
|
12
|
+
* **Contract for adapter authors.** A custom store implements a
|
|
13
|
+
* `rawCounters(): RawRetrieveCounters` method (or any equivalent that gathers the five
|
|
14
|
+
* counts from its own records) and delegates its public `stats()` to this function:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* stats(): RetrieveStats {
|
|
18
|
+
* return retrieveStats(this.rawCounters());
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* This is a free wrapper function rather than an abstract base class, deliberately —
|
|
23
|
+
* it is the least-breaking shape. The stores share no storage machinery (one is a
|
|
24
|
+
* `Map`, one is a directory), so a base class would couple every adapter's inheritance
|
|
25
|
+
* chain to smelt's for the sake of two lines of arithmetic; an existing `ElisionStore`
|
|
26
|
+
* implementation keeps its own hierarchy and adopts this contract by changing only its
|
|
27
|
+
* `stats()` body. The {@link ElisionStore} interface itself is unchanged: consumers
|
|
28
|
+
* still call `stats()` and never see this seam.
|
|
29
|
+
*/
|
|
30
|
+
export function retrieveStats(raw) {
|
|
31
|
+
return {
|
|
32
|
+
...raw,
|
|
33
|
+
expansionRate: raw.elisionsStored === 0 ? 0 : raw.uniqueRetrieved / raw.elisionsStored,
|
|
34
|
+
allElisionsRetrieved: raw.elisionsStored > 0 && raw.uniqueRetrieved === raw.elisionsStored,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,aAAa,CAAC,GAAwB;IACpD,OAAO;QACL,GAAG,GAAG;QACN,aAAa,EAAE,GAAG,CAAC,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,cAAc;QACtF,oBAAoB,EAAE,GAAG,CAAC,cAAc,GAAG,CAAC,IAAI,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC,cAAc;KAC3F,CAAC;AACJ,CAAC"}
|