@vibgrate/cli 2026.819.3 → 2026.824.3
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/{agent-AFMDB4GM.js → agent-6SJVWFPV.js} +12 -12
- package/dist/{agent-AFMDB4GM.js.map → agent-6SJVWFPV.js.map} +1 -1
- package/dist/{approvals-F2DJGPU7.js → approvals-5UBLBNJF.js} +5 -5
- package/dist/{approvals-F2DJGPU7.js.map → approvals-5UBLBNJF.js.map} +1 -1
- package/dist/{attach-XTX2Q3AD.js → attach-PSGCCJMA.js} +3 -3
- package/dist/{attach-XTX2Q3AD.js.map → attach-PSGCCJMA.js.map} +1 -1
- package/dist/{baseline-KTE2C3LW.js → baseline-HPXACNFU.js} +3 -3
- package/dist/{baseline-KTE2C3LW.js.map → baseline-HPXACNFU.js.map} +1 -1
- package/dist/{chunk-PQISIPYP.js → chunk-2IY24TTR.js} +3 -3
- package/dist/{chunk-PQISIPYP.js.map → chunk-2IY24TTR.js.map} +1 -1
- package/dist/{chunk-DMI4BMUL.js → chunk-3PNAQUZM.js} +3 -3
- package/dist/{chunk-DMI4BMUL.js.map → chunk-3PNAQUZM.js.map} +1 -1
- package/dist/{chunk-HVJHFTIV.js → chunk-4PDWV4AE.js} +10 -12
- package/dist/chunk-4PDWV4AE.js.map +1 -0
- package/dist/{chunk-SOAEPTSW.js → chunk-5QO2GFAE.js} +27 -23
- package/dist/chunk-5QO2GFAE.js.map +1 -0
- package/dist/chunk-AGOKTI77.js +6 -0
- package/dist/{chunk-MQJY4EQV.js.map → chunk-AGOKTI77.js.map} +1 -1
- package/dist/{chunk-UP2BR6TV.js → chunk-B5GOCQFB.js} +8 -8
- package/dist/{chunk-UP2BR6TV.js.map → chunk-B5GOCQFB.js.map} +1 -1
- package/dist/{chunk-5R4HYDTC.js → chunk-CJ2R3LVX.js} +4 -4
- package/dist/{chunk-5R4HYDTC.js.map → chunk-CJ2R3LVX.js.map} +1 -1
- package/dist/{chunk-C5AOLTQS.js → chunk-III7S75O.js} +53 -463
- package/dist/chunk-III7S75O.js.map +1 -0
- package/dist/{chunk-3SOJZ3VV.js → chunk-IKB5U3AH.js} +3 -3
- package/dist/{chunk-3SOJZ3VV.js.map → chunk-IKB5U3AH.js.map} +1 -1
- package/dist/{chunk-QOU2Q5L4.js → chunk-JL66EPZ4.js} +221 -10
- package/dist/chunk-JL66EPZ4.js.map +1 -0
- package/dist/{chunk-5RMME7PT.js → chunk-KBLKNR3V.js} +3 -3
- package/dist/{chunk-5RMME7PT.js.map → chunk-KBLKNR3V.js.map} +1 -1
- package/dist/{chunk-XAS45W3G.js → chunk-KRE7SXSA.js} +11 -12
- package/dist/chunk-KRE7SXSA.js.map +1 -0
- package/dist/{chunk-XZDI2N7M.js → chunk-LA4CETXN.js} +3 -3
- package/dist/{chunk-XZDI2N7M.js.map → chunk-LA4CETXN.js.map} +1 -1
- package/dist/{chunk-UEXDSWKN.js → chunk-LMHKIQA5.js} +3 -3
- package/dist/{chunk-UEXDSWKN.js.map → chunk-LMHKIQA5.js.map} +1 -1
- package/dist/{chunk-WI66PODG.js → chunk-M5QSBXA6.js} +17 -4
- package/dist/chunk-M5QSBXA6.js.map +1 -0
- package/dist/{chunk-EXFA7QFC.js → chunk-QP3P4V4I.js} +10 -8
- package/dist/chunk-QP3P4V4I.js.map +1 -0
- package/dist/{chunk-CEJIX3O7.js → chunk-RAEHWCN6.js} +6 -6
- package/dist/{chunk-CEJIX3O7.js.map → chunk-RAEHWCN6.js.map} +1 -1
- package/dist/{chunk-ARGYMFST.js → chunk-SGIFJIII.js} +3 -3
- package/dist/{chunk-ARGYMFST.js.map → chunk-SGIFJIII.js.map} +1 -1
- package/dist/{chunk-UAPA5AHX.js → chunk-TWWVTKA7.js} +35 -14
- package/dist/chunk-TWWVTKA7.js.map +1 -0
- package/dist/chunk-USW5WHUG.js +605 -0
- package/dist/chunk-USW5WHUG.js.map +1 -0
- package/dist/{chunk-3XMTRK24.js → chunk-UUNEUEPQ.js} +6 -6
- package/dist/{chunk-3XMTRK24.js.map → chunk-UUNEUEPQ.js.map} +1 -1
- package/dist/{chunk-HRYF4PWE.js → chunk-WATYIYWP.js} +3 -3
- package/dist/{chunk-HRYF4PWE.js.map → chunk-WATYIYWP.js.map} +1 -1
- package/dist/{chunk-TD4JAEMH.js → chunk-XHIWQGRE.js} +5 -5
- package/dist/{chunk-TD4JAEMH.js.map → chunk-XHIWQGRE.js.map} +1 -1
- package/dist/{chunk-HB3SONYP.js → chunk-YJ23GGAJ.js} +3 -3
- package/dist/{chunk-HB3SONYP.js.map → chunk-YJ23GGAJ.js.map} +1 -1
- package/dist/cli.js +377 -542
- package/dist/cli.js.map +1 -1
- package/dist/{daemon-VMQKIIYP.js → daemon-AEL5N4SJ.js} +9 -9
- package/dist/{daemon-VMQKIIYP.js.map → daemon-AEL5N4SJ.js.map} +1 -1
- package/dist/embed-worker-main.js +3 -3
- package/dist/{ensure-map-YIUJWMBK.js → ensure-map-YL5NR6UH.js} +14 -14
- package/dist/{ensure-map-YIUJWMBK.js.map → ensure-map-YL5NR6UH.js.map} +1 -1
- package/dist/{graph-backend-FDZUY4TR.js → graph-backend-LEPXFPXQ.js} +7 -7
- package/dist/{graph-backend-FDZUY4TR.js.map → graph-backend-LEPXFPXQ.js.map} +1 -1
- package/dist/index.d.ts +72 -130
- package/dist/index.js +14 -14
- package/dist/{interactive-CS4PGZPK.js → interactive-YJ37ZWTA.js} +26 -25
- package/dist/interactive-YJ37ZWTA.js.map +1 -0
- package/dist/load-LT6R73IZ.js +11 -0
- package/dist/{load-4EY64JJO.js.map → load-LT6R73IZ.js.map} +1 -1
- package/dist/parse-worker.js +3 -2
- package/dist/parse-worker.js.map +1 -1
- package/dist/{run-outcome-T7VDBNWB.js → run-outcome-YQBPYNEY.js} +5 -5
- package/dist/{run-outcome-T7VDBNWB.js.map → run-outcome-YQBPYNEY.js.map} +1 -1
- package/dist/{runtime-session-WZLVADPQ.js → runtime-session-JSH5UL7Z.js} +9 -9
- package/dist/{runtime-session-WZLVADPQ.js.map → runtime-session-JSH5UL7Z.js.map} +1 -1
- package/dist/{session-VPP2VGUN.js → session-3FLVZRMM.js} +8 -8
- package/dist/{session-VPP2VGUN.js.map → session-3FLVZRMM.js.map} +1 -1
- package/dist/{stream-json-XXCFN4MF.js → stream-json-VJ2O52CR.js} +12 -12
- package/dist/{stream-json-XXCFN4MF.js.map → stream-json-VJ2O52CR.js.map} +1 -1
- package/dist/version-VUTM46C5.js +4 -0
- package/dist/{version-2UBJ7N2I.js.map → version-VUTM46C5.js.map} +1 -1
- package/dist/{vg-mcp-bridge-H6GXYNKV.js → vg-mcp-bridge-UUMEOJSO.js} +8 -8
- package/dist/{vg-mcp-bridge-H6GXYNKV.js.map → vg-mcp-bridge-UUMEOJSO.js.map} +1 -1
- package/dist/{vgd-SBZO4ONQ.js → vgd-XFT574U6.js} +8 -8
- package/dist/vgd-XFT574U6.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-C5AOLTQS.js.map +0 -1
- package/dist/chunk-EXFA7QFC.js.map +0 -1
- package/dist/chunk-HVJHFTIV.js.map +0 -1
- package/dist/chunk-MQJY4EQV.js +0 -6
- package/dist/chunk-QOU2Q5L4.js.map +0 -1
- package/dist/chunk-SOAEPTSW.js.map +0 -1
- package/dist/chunk-UAPA5AHX.js.map +0 -1
- package/dist/chunk-VEQA2FSM.js +0 -699
- package/dist/chunk-VEQA2FSM.js.map +0 -1
- package/dist/chunk-WI66PODG.js.map +0 -1
- package/dist/chunk-XAS45W3G.js.map +0 -1
- package/dist/interactive-CS4PGZPK.js.map +0 -1
- package/dist/load-4EY64JJO.js +0 -11
- package/dist/version-2UBJ7N2I.js +0 -4
- package/dist/vgd-SBZO4ONQ.js.map +0 -1
package/dist/chunk-VEQA2FSM.js
DELETED
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
import { cosine } from './chunk-DMI4BMUL.js';
|
|
2
|
-
import { bigramConsumedTokens, WEAK_TERMS, expandConcepts } from './chunk-C5AOLTQS.js';
|
|
3
|
-
|
|
4
|
-
// src/engine/relations.ts
|
|
5
|
-
var GraphIndex = class {
|
|
6
|
-
constructor(graph) {
|
|
7
|
-
this.graph = graph;
|
|
8
|
-
for (const n of graph.nodes) this.nodeById.set(n.id, n);
|
|
9
|
-
for (const e of graph.edges) {
|
|
10
|
-
push(this.outById, e.src, e);
|
|
11
|
-
push(this.inById, e.dst, e);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
graph;
|
|
15
|
-
nodeById = /* @__PURE__ */ new Map();
|
|
16
|
-
outById = /* @__PURE__ */ new Map();
|
|
17
|
-
inById = /* @__PURE__ */ new Map();
|
|
18
|
-
out(id, kind) {
|
|
19
|
-
const all = this.outById.get(id) ?? [];
|
|
20
|
-
return kind ? all.filter((e) => e.kind === kind) : all;
|
|
21
|
-
}
|
|
22
|
-
in(id, kind) {
|
|
23
|
-
const all = this.inById.get(id) ?? [];
|
|
24
|
-
return kind ? all.filter((e) => e.kind === kind) : all;
|
|
25
|
-
}
|
|
26
|
-
node(id) {
|
|
27
|
-
return this.nodeById.get(id);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Resolved nodes called by `id` — invocations (`call`) plus structural
|
|
31
|
-
* dependency references (`references`, e.g. a constructor-injected field's
|
|
32
|
-
* type), since both represent real usage a caller/impact question cares
|
|
33
|
-
* about. `references` is otherwise only emitted by the precise SCIP/tsc
|
|
34
|
-
* rungs and (for Java DI wiring) the heuristic rung — never a guess.
|
|
35
|
-
*/
|
|
36
|
-
callees(id) {
|
|
37
|
-
return this.resolveTargets(this.out(id).filter(isUsageEdge), "dst");
|
|
38
|
-
}
|
|
39
|
-
/** Resolved nodes that call or structurally reference `id`. */
|
|
40
|
-
callers(id) {
|
|
41
|
-
return this.resolveTargets(this.in(id).filter(isUsageEdge), "src");
|
|
42
|
-
}
|
|
43
|
-
resolveTargets(edges, end) {
|
|
44
|
-
const out = [];
|
|
45
|
-
for (const e of edges) {
|
|
46
|
-
const node = this.nodeById.get(e[end]);
|
|
47
|
-
if (node) out.push({ edge: e, node });
|
|
48
|
-
}
|
|
49
|
-
return out;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
var USAGE_KINDS = /* @__PURE__ */ new Set(["call", "references"]);
|
|
53
|
-
function isUsageEdge(e) {
|
|
54
|
-
return USAGE_KINDS.has(e.kind);
|
|
55
|
-
}
|
|
56
|
-
function push(map, key, value) {
|
|
57
|
-
const list = map.get(key);
|
|
58
|
-
if (list) list.push(value);
|
|
59
|
-
else map.set(key, [value]);
|
|
60
|
-
}
|
|
61
|
-
var INDEX_CACHE = /* @__PURE__ */ new WeakMap();
|
|
62
|
-
function indexFor(graph) {
|
|
63
|
-
let idx = INDEX_CACHE.get(graph);
|
|
64
|
-
if (!idx) {
|
|
65
|
-
idx = new GraphIndex(graph);
|
|
66
|
-
INDEX_CACHE.set(graph, idx);
|
|
67
|
-
}
|
|
68
|
-
return idx;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// src/engine/user-ask.ts
|
|
72
|
-
function userAskFromInstruction(instruction) {
|
|
73
|
-
if (!instruction) return instruction;
|
|
74
|
-
const fenced = /\n---\s*\n## User attachments\b[^\n]*\n?/;
|
|
75
|
-
const m = fenced.exec(instruction);
|
|
76
|
-
if (m && m.index != null) {
|
|
77
|
-
return instruction.slice(0, m.index).trimEnd();
|
|
78
|
-
}
|
|
79
|
-
const bare = instruction.search(/(?:^|\n)## User attachments\b/);
|
|
80
|
-
if (bare > 0) {
|
|
81
|
-
return instruction.slice(0, bare).trimEnd();
|
|
82
|
-
}
|
|
83
|
-
if (bare === 0) {
|
|
84
|
-
return "";
|
|
85
|
-
}
|
|
86
|
-
return instruction;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// src/engine/query.ts
|
|
90
|
-
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
91
|
-
"the",
|
|
92
|
-
"a",
|
|
93
|
-
"an",
|
|
94
|
-
"is",
|
|
95
|
-
"are",
|
|
96
|
-
"was",
|
|
97
|
-
"were",
|
|
98
|
-
"be",
|
|
99
|
-
"to",
|
|
100
|
-
"of",
|
|
101
|
-
"in",
|
|
102
|
-
"on",
|
|
103
|
-
"for",
|
|
104
|
-
"and",
|
|
105
|
-
"or",
|
|
106
|
-
"where",
|
|
107
|
-
"what",
|
|
108
|
-
"which",
|
|
109
|
-
"how",
|
|
110
|
-
"do",
|
|
111
|
-
"does",
|
|
112
|
-
"did",
|
|
113
|
-
"i",
|
|
114
|
-
"we",
|
|
115
|
-
"it",
|
|
116
|
-
"this",
|
|
117
|
-
"that",
|
|
118
|
-
"with",
|
|
119
|
-
"when",
|
|
120
|
-
"who",
|
|
121
|
-
"why",
|
|
122
|
-
"can",
|
|
123
|
-
"should",
|
|
124
|
-
"my",
|
|
125
|
-
"our",
|
|
126
|
-
"you",
|
|
127
|
-
"your",
|
|
128
|
-
"from",
|
|
129
|
-
"by",
|
|
130
|
-
"at",
|
|
131
|
-
"as",
|
|
132
|
-
// Discovery-question scaffolding: words a caller uses to FRAME the ask
|
|
133
|
-
// ("find the code responsible for X", "I need to modify X, where do I
|
|
134
|
-
// start?", "explain how X works in this codebase") rather than to name the
|
|
135
|
-
// target. Left in, these compete on equal footing with the real identifier
|
|
136
|
-
// terms and can outrank it outright — e.g. "find the code responsible for
|
|
137
|
-
// deleteAsync" let "find" alone drag in every FindByIdAsync/FindAll method
|
|
138
|
-
// in the repo, none of them the target (VG-LOCATE-FAILURE-ANALYSIS.md).
|
|
139
|
-
"find",
|
|
140
|
-
"code",
|
|
141
|
-
"responsible",
|
|
142
|
-
"need",
|
|
143
|
-
"modify",
|
|
144
|
-
"me",
|
|
145
|
-
"implementation",
|
|
146
|
-
"explain",
|
|
147
|
-
"works",
|
|
148
|
-
"codebase",
|
|
149
|
-
"contains",
|
|
150
|
-
"file",
|
|
151
|
-
// String/URL occurrence scaffolding: "does not exist", "find occurrences of …"
|
|
152
|
-
// must not light up DoesNot*/NonExisting*/commandExists via weak substrings
|
|
153
|
-
// (field report: https://…/signup does not exist find occurrences).
|
|
154
|
-
"not",
|
|
155
|
-
"no",
|
|
156
|
-
"exist",
|
|
157
|
-
"exists",
|
|
158
|
-
"existing",
|
|
159
|
-
"occurrence",
|
|
160
|
-
"occurrences",
|
|
161
|
-
"occurence",
|
|
162
|
-
"occurences",
|
|
163
|
-
"every",
|
|
164
|
-
"place",
|
|
165
|
-
"places",
|
|
166
|
-
"string",
|
|
167
|
-
"literal",
|
|
168
|
-
"text",
|
|
169
|
-
"search",
|
|
170
|
-
"locate",
|
|
171
|
-
"look",
|
|
172
|
-
"looking",
|
|
173
|
-
"show",
|
|
174
|
-
"list",
|
|
175
|
-
"all"
|
|
176
|
-
]);
|
|
177
|
-
function cleanUrlNeedle(raw) {
|
|
178
|
-
let s = raw.trim();
|
|
179
|
-
s = s.replace(/[.,;:!)\]}>]+$/g, "");
|
|
180
|
-
if (s.endsWith("?")) {
|
|
181
|
-
const qi = s.lastIndexOf("?");
|
|
182
|
-
if (qi === s.length - 1) s = s.slice(0, -1);
|
|
183
|
-
}
|
|
184
|
-
return s;
|
|
185
|
-
}
|
|
186
|
-
function extractLiteralNeedles(question) {
|
|
187
|
-
const out = [];
|
|
188
|
-
const seen = /* @__PURE__ */ new Set();
|
|
189
|
-
const push2 = (raw, kind) => {
|
|
190
|
-
let s = raw.trim();
|
|
191
|
-
s = kind === "url" ? cleanUrlNeedle(s) : s.replace(/[.,;:!)\]}>?]+$/g, "");
|
|
192
|
-
if (s.length < 2 || seen.has(s)) return;
|
|
193
|
-
seen.add(s);
|
|
194
|
-
out.push(s);
|
|
195
|
-
};
|
|
196
|
-
for (const m of question.matchAll(/https?:\/\/[^\s"'`<>]+/gi)) push2(m[0], "url");
|
|
197
|
-
for (const m of question.matchAll(/(["'`])((?:(?!\1)[^\\]|\\.){2,})\1/g)) {
|
|
198
|
-
push2(m[2].replace(/\\(.)/g, "$1"), "quote");
|
|
199
|
-
}
|
|
200
|
-
return out;
|
|
201
|
-
}
|
|
202
|
-
function stripLiteralNeedles(question) {
|
|
203
|
-
let s = question;
|
|
204
|
-
for (const m of question.matchAll(/https?:\/\/[^\s"'`<>]+/gi)) {
|
|
205
|
-
s = s.split(m[0]).join(" ");
|
|
206
|
-
}
|
|
207
|
-
for (const lit of extractLiteralNeedles(question)) {
|
|
208
|
-
s = s.split(lit).join(" ");
|
|
209
|
-
}
|
|
210
|
-
s = s.replace(/(["'`])\s*\1/g, " ");
|
|
211
|
-
return s.replace(/\s+/g, " ").trim();
|
|
212
|
-
}
|
|
213
|
-
function residualForSymbolSearch(question) {
|
|
214
|
-
const literals = extractLiteralNeedles(question);
|
|
215
|
-
if (literals.length === 0) return question.trim();
|
|
216
|
-
const stripped = stripLiteralNeedles(question);
|
|
217
|
-
return tokenize(stripped).length === 0 ? "" : stripped;
|
|
218
|
-
}
|
|
219
|
-
function isLocateOnlyInstruction(instruction) {
|
|
220
|
-
const q = userAskFromInstruction(instruction).trim();
|
|
221
|
-
if (!q) return false;
|
|
222
|
-
const needles = extractLiteralNeedles(q);
|
|
223
|
-
const hasLocateFrame = /\b(where\s+is|where\s+are|find\s+(all\s+|every\s+)?occurrence|find\s+occurrences|does\s+not\s+exist|locate|search\s+for)\b/i.test(
|
|
224
|
-
q
|
|
225
|
-
) || needles.length > 0 && /^https?:\/\//i.test(q);
|
|
226
|
-
const withoutExist = q.replace(/\bdoes\s+not\s+exist\b/gi, " ");
|
|
227
|
-
if (/\b(fix|edit|change|replace|implement|refactor|rename|patch|create\s+file|delete\s+file|add\s+a\b|remove\s+the\b)\b/i.test(
|
|
228
|
-
withoutExist
|
|
229
|
-
)) {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
if (needles.some((n) => /^https?:\/\//i.test(n))) return true;
|
|
233
|
-
return hasLocateFrame && needles.length > 0;
|
|
234
|
-
}
|
|
235
|
-
function queryGraph(graph, question, options = {}) {
|
|
236
|
-
const budget = options.budget ?? 2e3;
|
|
237
|
-
const limit = options.limit ?? 12;
|
|
238
|
-
const literals = extractLiteralNeedles(question);
|
|
239
|
-
const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;
|
|
240
|
-
const prep = prepareTerms(forTokens, options.relevance, priorResidual(options.priorQuestion));
|
|
241
|
-
const weightOf = termWeights(graph, prep.terms.map((t) => t.term));
|
|
242
|
-
const index = indexFor(graph);
|
|
243
|
-
const scored = [];
|
|
244
|
-
if (prep.hasContent) {
|
|
245
|
-
for (const node of graph.nodes) {
|
|
246
|
-
if (node.kind === "file" || node.kind === "external") continue;
|
|
247
|
-
const { score, why } = scoreNode(node, prep.terms, weightOf);
|
|
248
|
-
if (score > 0) {
|
|
249
|
-
const affinity = topicAffinity(node.id, options.relevance, options.topicTags);
|
|
250
|
-
scored.push({
|
|
251
|
-
node,
|
|
252
|
-
score: round(score * affinity.boost * (1 + IMPORTANCE_WEIGHT * node.importance)),
|
|
253
|
-
why: why + affinity.label
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
|
|
258
|
-
}
|
|
259
|
-
const seeds = scored.slice(0, limit);
|
|
260
|
-
const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);
|
|
261
|
-
return { question, matches: seeds, context, tokensEstimate };
|
|
262
|
-
}
|
|
263
|
-
var RRF_K = 60;
|
|
264
|
-
function ranksOf(scores, nameOf) {
|
|
265
|
-
const ids = [...scores.entries()].filter(([, s]) => s > 0).sort((a, b) => b[1] - a[1] || nameOf(a[0]).localeCompare(nameOf(b[0]))).map(([id]) => id);
|
|
266
|
-
return new Map(ids.map((id, i) => [id, i + 1]));
|
|
267
|
-
}
|
|
268
|
-
async function queryGraphSemantic(graph, question, options) {
|
|
269
|
-
const budget = options.budget ?? 2e3;
|
|
270
|
-
const limit = options.limit ?? 12;
|
|
271
|
-
const literals = extractLiteralNeedles(question);
|
|
272
|
-
const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;
|
|
273
|
-
const prep = prepareTerms(forTokens, options.relevance, priorResidual(options.priorQuestion));
|
|
274
|
-
const terms = prep.terms.map((t) => t.term);
|
|
275
|
-
const weightOf = termWeights(graph, terms);
|
|
276
|
-
const index = indexFor(graph);
|
|
277
|
-
const queryVec = options.semanticRanked ? null : await options.embedder?.embedQuery(forTokens || question);
|
|
278
|
-
const lexRaw = /* @__PURE__ */ new Map();
|
|
279
|
-
const whyById = /* @__PURE__ */ new Map();
|
|
280
|
-
if (prep.hasContent) {
|
|
281
|
-
for (const node of graph.nodes) {
|
|
282
|
-
if (node.kind === "file" || node.kind === "external") continue;
|
|
283
|
-
const { score, why } = scoreNode(node, prep.terms, weightOf);
|
|
284
|
-
lexRaw.set(node.id, score);
|
|
285
|
-
whyById.set(node.id, why);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
const scored = [];
|
|
289
|
-
if (terms.length > 0 || literals.length === 0) {
|
|
290
|
-
const semRaw = /* @__PURE__ */ new Map();
|
|
291
|
-
const nodeById = /* @__PURE__ */ new Map();
|
|
292
|
-
for (const node of graph.nodes) {
|
|
293
|
-
if (node.kind === "file" || node.kind === "external") continue;
|
|
294
|
-
nodeById.set(node.id, node);
|
|
295
|
-
if (options.semanticRanked || !queryVec) continue;
|
|
296
|
-
const vec = options.nodeVectors?.get(node.id);
|
|
297
|
-
const sem = vec && terms.length > 0 ? Math.max(0, cosine(queryVec, vec)) : 0;
|
|
298
|
-
if (sem > 0) semRaw.set(node.id, sem);
|
|
299
|
-
}
|
|
300
|
-
if (options.semanticRanked && terms.length > 0) {
|
|
301
|
-
for (const { id, score } of options.semanticRanked) {
|
|
302
|
-
if (score > 0 && nodeById.has(id)) semRaw.set(id, score);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
const nameOf = (id) => nodeById.get(id)?.qualifiedName ?? id;
|
|
306
|
-
const lexRanks = ranksOf(lexRaw, nameOf);
|
|
307
|
-
const semRanks = ranksOf(semRaw, nameOf);
|
|
308
|
-
for (const [id, node] of nodeById) {
|
|
309
|
-
const lr = lexRanks.get(id);
|
|
310
|
-
const sr = semRanks.get(id);
|
|
311
|
-
if (lr === void 0 && sr === void 0) continue;
|
|
312
|
-
const fused = (lr !== void 0 ? 1 / (RRF_K + lr) : 0) + (sr !== void 0 ? 1 / (RRF_K + sr) : 0);
|
|
313
|
-
const sem = semRaw.get(id) ?? 0;
|
|
314
|
-
const lexWhy = whyById.get(id);
|
|
315
|
-
const affinity = topicAffinity(id, options.relevance, options.topicTags);
|
|
316
|
-
const why = (lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : "weak match")) + affinity.label;
|
|
317
|
-
scored.push({ node, score: round(fused * affinity.boost * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
|
|
318
|
-
}
|
|
319
|
-
scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
|
|
320
|
-
}
|
|
321
|
-
const seeds = scored.slice(0, limit);
|
|
322
|
-
const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);
|
|
323
|
-
return { question, matches: seeds, context, tokensEstimate };
|
|
324
|
-
}
|
|
325
|
-
function tokenize(q) {
|
|
326
|
-
return [
|
|
327
|
-
...new Set(
|
|
328
|
-
q.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 1 && !STOPWORDS.has(t))
|
|
329
|
-
)
|
|
330
|
-
];
|
|
331
|
-
}
|
|
332
|
-
function tokenizeOrdered(q) {
|
|
333
|
-
return q.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 1);
|
|
334
|
-
}
|
|
335
|
-
var WEAK_TERM_WEIGHT = 0.25;
|
|
336
|
-
var EXPANSION_WEIGHT = 0.7;
|
|
337
|
-
var COVERAGE_BONUS = 0.35;
|
|
338
|
-
var CARRY_WEIGHT = 0.5;
|
|
339
|
-
function prepareTerms(forTokens, relevance, prior) {
|
|
340
|
-
const base = tokenize(forTokens);
|
|
341
|
-
const ordered = tokenizeOrdered(forTokens);
|
|
342
|
-
const baseSet = new Set(base);
|
|
343
|
-
const consumed = bigramConsumedTokens(ordered);
|
|
344
|
-
const terms = base.map((t) => ({ term: t, weak: WEAK_TERMS.has(t) || consumed.has(t) }));
|
|
345
|
-
const have = new Set(base);
|
|
346
|
-
for (const e of expandConcepts(ordered)) {
|
|
347
|
-
if (baseSet.has(e.term)) continue;
|
|
348
|
-
terms.push({ term: e.term, weak: false, from: e.from });
|
|
349
|
-
have.add(e.term);
|
|
350
|
-
}
|
|
351
|
-
for (const e of relevance?.expansions ?? []) {
|
|
352
|
-
if (have.has(e.term)) continue;
|
|
353
|
-
terms.push({ term: e.term, weak: false, from: e.from, providerWeight: e.weight });
|
|
354
|
-
have.add(e.term);
|
|
355
|
-
}
|
|
356
|
-
if (prior?.trim()) {
|
|
357
|
-
const priorOrdered = tokenizeOrdered(prior);
|
|
358
|
-
const priorConsumed = bigramConsumedTokens(priorOrdered);
|
|
359
|
-
for (const t of tokenize(prior)) {
|
|
360
|
-
if (have.has(t) || WEAK_TERMS.has(t) || priorConsumed.has(t)) continue;
|
|
361
|
-
terms.push({ term: t, weak: false, carried: true });
|
|
362
|
-
have.add(t);
|
|
363
|
-
}
|
|
364
|
-
for (const e of expandConcepts(priorOrdered)) {
|
|
365
|
-
if (have.has(e.term)) continue;
|
|
366
|
-
terms.push({ term: e.term, weak: false, from: e.from, carried: true });
|
|
367
|
-
have.add(e.term);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
const hasContent = terms.some((t) => !t.weak);
|
|
371
|
-
return { terms, hasContent };
|
|
372
|
-
}
|
|
373
|
-
function priorResidual(prior) {
|
|
374
|
-
const raw = prior?.trim();
|
|
375
|
-
if (!raw) return null;
|
|
376
|
-
return extractLiteralNeedles(raw).length > 0 ? stripLiteralNeedles(raw) : raw;
|
|
377
|
-
}
|
|
378
|
-
function conceptMapLines(question, relevance, priorQuestion) {
|
|
379
|
-
const cap = (xs) => [...new Set(xs)].slice(0, 8).join(", ");
|
|
380
|
-
const literals = extractLiteralNeedles(question);
|
|
381
|
-
const residual = literals.length > 0 ? stripLiteralNeedles(question) : question;
|
|
382
|
-
const lines = [];
|
|
383
|
-
const byFrom = /* @__PURE__ */ new Map();
|
|
384
|
-
for (const e of expandConcepts(tokenizeOrdered(residual))) {
|
|
385
|
-
const list = byFrom.get(e.from) ?? [];
|
|
386
|
-
list.push(e.term);
|
|
387
|
-
byFrom.set(e.from, list);
|
|
388
|
-
}
|
|
389
|
-
for (const [from, terms] of byFrom) {
|
|
390
|
-
lines.push(`- "${from}" in the ask implies these codebase identifiers: ${cap(terms)}.`);
|
|
391
|
-
}
|
|
392
|
-
const matches = relevance?.taxonomy ?? [];
|
|
393
|
-
if (matches.length) {
|
|
394
|
-
const seenPath = /* @__PURE__ */ new Set();
|
|
395
|
-
const levels = [];
|
|
396
|
-
for (const m of matches) {
|
|
397
|
-
for (const l of m.levels) {
|
|
398
|
-
if (seenPath.has(l.path)) continue;
|
|
399
|
-
seenPath.add(l.path);
|
|
400
|
-
levels.push({ path: l.path, id: l.id, depth: l.path.split("/").length, terms: l.terms });
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
const deepest = Math.max(...levels.map((l) => l.depth));
|
|
404
|
-
const describe = (l, lead) => l.terms.length ? `${lead} ${l.id}; code for it typically uses: ${cap(l.terms)}.` : `${lead} ${l.id}.`;
|
|
405
|
-
for (const l of levels.filter((l2) => l2.depth === deepest)) {
|
|
406
|
-
lines.push(describe(l, "- The ask is specifically about"));
|
|
407
|
-
}
|
|
408
|
-
const deepestIds = new Set(levels.filter((l) => l.depth === deepest).map((l) => l.id));
|
|
409
|
-
for (const v of (relevance?.vendors ?? []).filter((v2) => v2.score > 0).slice(0, 3)) {
|
|
410
|
-
if (deepestIds.has(v.name)) {
|
|
411
|
-
if (v.from !== v.name) lines.push(`- That product was typed "${v.from}".`);
|
|
412
|
-
continue;
|
|
413
|
-
}
|
|
414
|
-
lines.push(
|
|
415
|
-
v.from === v.name ? `- It names the product "${v.name}".` : `- It names the product "${v.name}" (typed "${v.from}").`
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
const named = new Set((relevance?.vendors ?? []).map((v) => v.name));
|
|
419
|
-
for (let d = deepest - 1; d >= 1; d--) {
|
|
420
|
-
for (const l of levels.filter((l2) => l2.depth === d && !named.has(l2.id))) {
|
|
421
|
-
lines.push(describe(l, "- More broadly, this is"));
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
} else if (relevance?.topics?.length) {
|
|
425
|
-
const topicVocab = /* @__PURE__ */ new Map();
|
|
426
|
-
for (const e of relevance.expansions ?? []) {
|
|
427
|
-
const list = topicVocab.get(e.from) ?? [];
|
|
428
|
-
list.push(e.term);
|
|
429
|
-
topicVocab.set(e.from, list);
|
|
430
|
-
}
|
|
431
|
-
for (const t of relevance.topics.slice(0, 3)) {
|
|
432
|
-
const vocab = topicVocab.get(t.id);
|
|
433
|
-
lines.push(
|
|
434
|
-
vocab?.length ? `- The ask is about the "${t.id}" domain; code for it typically uses: ${cap(vocab)}.` : `- The ask is about the "${t.id}" domain.`
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
const standards = relevance?.standards ?? [];
|
|
439
|
-
if (standards.length) {
|
|
440
|
-
const specs = standards.filter((st) => st.kind !== "regulation");
|
|
441
|
-
const rules = standards.filter((st) => st.kind === "regulation");
|
|
442
|
-
const render = (list) => list.slice(0, 3).map((st) => st.publisher ? `${st.name} (${st.publisher})` : st.name).join("; ");
|
|
443
|
-
if (specs.length) lines.push(`- Standards that govern this area: ${render(specs)}.`);
|
|
444
|
-
if (rules.length) lines.push(`- Regulations that apply here: ${render(rules)}.`);
|
|
445
|
-
}
|
|
446
|
-
const categories = relevance?.categories ?? [];
|
|
447
|
-
if (categories.length) {
|
|
448
|
-
lines.push(`- Related categories: ${cap(categories)}.`);
|
|
449
|
-
}
|
|
450
|
-
const fileHints = relevance?.files ?? [];
|
|
451
|
-
if (fileHints.length) {
|
|
452
|
-
lines.push(`- Files for this kind of work are usually named or extended: ${cap(fileHints)}.`);
|
|
453
|
-
}
|
|
454
|
-
for (const c of (relevance?.corrections ?? []).slice(0, 3)) {
|
|
455
|
-
lines.push(`- Read "${c.from}" in the ask as "${c.to}".`);
|
|
456
|
-
}
|
|
457
|
-
const priorText = priorResidual(priorQuestion);
|
|
458
|
-
if (priorText) {
|
|
459
|
-
const current = new Set(tokenize(residual));
|
|
460
|
-
const consumed = bigramConsumedTokens(tokenizeOrdered(priorText));
|
|
461
|
-
const carried = tokenize(priorText).filter((t) => !current.has(t) && !WEAK_TERMS.has(t) && !consumed.has(t));
|
|
462
|
-
if (carried.length) {
|
|
463
|
-
lines.push(`- This is a follow-up; topic words carried from the previous ask: ${cap(carried)}.`);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (lines.length) {
|
|
467
|
-
lines.push(
|
|
468
|
-
'- Seed notation below: `a\u2192b` = ask term "a" implied identifier "b"; `~t` = word-form match; `\u21A9t` = carried from the previous ask.'
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
return lines;
|
|
472
|
-
}
|
|
473
|
-
function scoreNode(node, terms, weightOf = () => 1) {
|
|
474
|
-
let score = 0;
|
|
475
|
-
let contentHits = 0;
|
|
476
|
-
const hits = [];
|
|
477
|
-
const name = node.name.toLowerCase();
|
|
478
|
-
const qn = node.qualifiedName.toLowerCase();
|
|
479
|
-
const file = node.file.toLowerCase();
|
|
480
|
-
const dirSegments = new Set(file.split("/").slice(0, -1));
|
|
481
|
-
const nameParts = identifierParts(node.name);
|
|
482
|
-
for (const { term: t, weak, from, providerWeight, carried } of terms) {
|
|
483
|
-
const expansionW = providerWeight !== void 0 ? Math.min(providerWeight, EXPANSION_WEIGHT) : from ? EXPANSION_WEIGHT : 1;
|
|
484
|
-
const w = weightOf(t) * (weak ? WEAK_TERM_WEIGHT : 1) * expansionW * (carried ? CARRY_WEIGHT : 1);
|
|
485
|
-
const label = (carried ? "\u21A9" : "") + (from ? `${from}\u2192${t}` : t);
|
|
486
|
-
let contribution = 0;
|
|
487
|
-
if (name === t) {
|
|
488
|
-
contribution = 10 * w;
|
|
489
|
-
hits.push(label);
|
|
490
|
-
} else if (nameParts.has(t)) {
|
|
491
|
-
contribution = 6 * w;
|
|
492
|
-
hits.push(label);
|
|
493
|
-
} else if (name.includes(t)) {
|
|
494
|
-
contribution = 4 * w;
|
|
495
|
-
hits.push(label);
|
|
496
|
-
} else if (qn.includes(t)) {
|
|
497
|
-
contribution = 3 * w;
|
|
498
|
-
hits.push(label);
|
|
499
|
-
} else if (fuzzyPartMatch(t, nameParts)) {
|
|
500
|
-
contribution = 2 * w;
|
|
501
|
-
hits.push((carried ? "\u21A9" : "") + (from ? `~${from}\u2192${t}` : `~${t}`));
|
|
502
|
-
} else if (dirSegments.has(t)) {
|
|
503
|
-
contribution = 2 * w;
|
|
504
|
-
hits.push(label);
|
|
505
|
-
} else if (file.includes(t)) {
|
|
506
|
-
contribution = 1 * w;
|
|
507
|
-
hits.push(label);
|
|
508
|
-
}
|
|
509
|
-
if (contribution > 0) {
|
|
510
|
-
score += contribution;
|
|
511
|
-
if (!weak) contentHits += 1;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (contentHits === 0) return { score: 0, why: "" };
|
|
515
|
-
score *= 1 + COVERAGE_BONUS * Math.min(contentHits - 1, 4);
|
|
516
|
-
return { score, why: hits.length ? `matched: ${hits.join(", ")}` : "" };
|
|
517
|
-
}
|
|
518
|
-
var IMPORTANCE_WEIGHT = 0.4;
|
|
519
|
-
var TOPIC_AFFINITY_WEIGHT = 0.3;
|
|
520
|
-
function sharedDepth(a, b) {
|
|
521
|
-
const as = a.split("/");
|
|
522
|
-
const bs = b.split("/");
|
|
523
|
-
let n = 0;
|
|
524
|
-
while (n < as.length && n < bs.length && as[n] === bs[n]) n++;
|
|
525
|
-
return n;
|
|
526
|
-
}
|
|
527
|
-
function topicAffinity(nodeId, relevance, topicTags) {
|
|
528
|
-
if (!topicTags) return { boost: 1, label: "" };
|
|
529
|
-
const tags = topicTags.get(nodeId);
|
|
530
|
-
if (!tags?.length) return { boost: 1, label: "" };
|
|
531
|
-
const paths = relevance?.taxonomy ?? [];
|
|
532
|
-
if (paths.length && tags.some((t) => t.includes("/"))) {
|
|
533
|
-
let best = 0;
|
|
534
|
-
let label = "";
|
|
535
|
-
for (const match of paths) {
|
|
536
|
-
const askDepth = match.path.split("/").length;
|
|
537
|
-
for (const tag of tags) {
|
|
538
|
-
const shared = sharedDepth(match.path, tag);
|
|
539
|
-
if (shared === 0) continue;
|
|
540
|
-
const affinity2 = shared / askDepth * match.score;
|
|
541
|
-
if (affinity2 > best) {
|
|
542
|
-
best = affinity2;
|
|
543
|
-
label = shared === askDepth ? match.path : match.path.split("/").slice(0, shared).join("/");
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
if (best <= 0) return { boost: 1, label: "" };
|
|
548
|
-
return { boost: 1 + TOPIC_AFFINITY_WEIGHT * Math.min(best, 1), label: `, topic:${label}` };
|
|
549
|
-
}
|
|
550
|
-
if (!relevance?.topics?.length) return { boost: 1, label: "" };
|
|
551
|
-
let affinity = 0;
|
|
552
|
-
const matched = [];
|
|
553
|
-
for (const t of relevance.topics) {
|
|
554
|
-
if (tags.includes(t.id)) {
|
|
555
|
-
affinity += t.score;
|
|
556
|
-
matched.push(t.id);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
if (affinity <= 0) return { boost: 1, label: "" };
|
|
560
|
-
return { boost: 1 + TOPIC_AFFINITY_WEIGHT * Math.min(affinity, 1), label: `, topic:${matched.sort().join("+")}` };
|
|
561
|
-
}
|
|
562
|
-
function termWeights(graph, terms) {
|
|
563
|
-
if (terms.length === 0) return () => 1;
|
|
564
|
-
const df = /* @__PURE__ */ new Map();
|
|
565
|
-
let n = 0;
|
|
566
|
-
for (const node of graph.nodes) {
|
|
567
|
-
if (node.kind === "file" || node.kind === "external") continue;
|
|
568
|
-
n++;
|
|
569
|
-
const name = node.name.toLowerCase();
|
|
570
|
-
const parts = identifierParts(node.name);
|
|
571
|
-
for (const t of terms) {
|
|
572
|
-
if (parts.has(t) || name.includes(t)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
const w = /* @__PURE__ */ new Map();
|
|
576
|
-
for (const t of terms) {
|
|
577
|
-
const idf = Math.log((n + 1) / ((df.get(t) ?? 0) + 1)) + 1;
|
|
578
|
-
w.set(t, Math.max(0.5, Math.min(8, idf)));
|
|
579
|
-
}
|
|
580
|
-
return (t) => w.get(t) ?? 1;
|
|
581
|
-
}
|
|
582
|
-
function identifierParts(name) {
|
|
583
|
-
return new Set(
|
|
584
|
-
name.split(/[^\p{L}\p{N}]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/u).filter(Boolean).map((s) => s.toLowerCase())
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
function fuzzyPartMatch(term, parts) {
|
|
588
|
-
for (const part of parts) {
|
|
589
|
-
const shared = sharedPrefixLen(term, part);
|
|
590
|
-
if (shared >= 5 && shared >= 0.6 * Math.min(term.length, part.length)) return true;
|
|
591
|
-
}
|
|
592
|
-
return false;
|
|
593
|
-
}
|
|
594
|
-
function sharedPrefixLen(a, b) {
|
|
595
|
-
const n = Math.min(a.length, b.length);
|
|
596
|
-
let i = 0;
|
|
597
|
-
while (i < n && a[i] === b[i]) i++;
|
|
598
|
-
return i;
|
|
599
|
-
}
|
|
600
|
-
function buildContext(graph, index, question, seeds, budget, literals = []) {
|
|
601
|
-
const lines = [];
|
|
602
|
-
lines.push(`# Context for: ${question}`);
|
|
603
|
-
lines.push("");
|
|
604
|
-
if (literals.length) {
|
|
605
|
-
lines.push("## Literal string(s) to locate (exact text, not symbol names)");
|
|
606
|
-
for (const lit of literals) lines.push(`- \`${lit}\``);
|
|
607
|
-
lines.push(
|
|
608
|
-
"_Use a quoted phrase / URL search (`search_symbols` or `search_code` with the exact needle) for occurrences. Graph symbols below (if any) are residual identifier hints only._"
|
|
609
|
-
);
|
|
610
|
-
lines.push("");
|
|
611
|
-
}
|
|
612
|
-
if (seeds.length === 0) {
|
|
613
|
-
lines.push(
|
|
614
|
-
literals.length ? "_No graph symbols matched residual terms \u2014 this is expected for a pure string/URL occurrence search. Run a literal sweep on the needle(s) above._" : "_No matching symbols found in the map. Try different terms, or `vg hubs` for the most important code._"
|
|
615
|
-
);
|
|
616
|
-
const text2 = lines.join("\n");
|
|
617
|
-
return { context: text2, tokensEstimate: estimateTokens(text2) };
|
|
618
|
-
}
|
|
619
|
-
for (const { node, why } of seeds) {
|
|
620
|
-
const block = [];
|
|
621
|
-
block.push(`## ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start})`);
|
|
622
|
-
if (node.signature) block.push("`" + node.signature + "`");
|
|
623
|
-
const callees = index.callees(node.id).map((x) => x.node.qualifiedName);
|
|
624
|
-
const callers = index.callers(node.id).map((x) => x.node.qualifiedName);
|
|
625
|
-
if (callees.length) block.push(`calls: ${unique(callees).slice(0, 8).join(", ")}`);
|
|
626
|
-
if (callers.length) block.push(`called by: ${unique(callers).slice(0, 8).join(", ")}`);
|
|
627
|
-
block.push(`importance ${node.importance.toFixed(3)} \xB7 area #${node.area}${node.isHub ? " \xB7 hub" : ""} \xB7 ${why}`);
|
|
628
|
-
block.push("");
|
|
629
|
-
const candidate = lines.concat(block).join("\n");
|
|
630
|
-
if (estimateTokens(candidate) > budget && lines.length > 2) break;
|
|
631
|
-
lines.push(...block);
|
|
632
|
-
}
|
|
633
|
-
const text = lines.join("\n");
|
|
634
|
-
return { context: text, tokensEstimate: estimateTokens(text) };
|
|
635
|
-
}
|
|
636
|
-
function unique(xs) {
|
|
637
|
-
return [...new Set(xs)];
|
|
638
|
-
}
|
|
639
|
-
function estimateTokens(text) {
|
|
640
|
-
return Math.ceil(text.length / 4);
|
|
641
|
-
}
|
|
642
|
-
function round(x) {
|
|
643
|
-
return Math.round(x * 1e6) / 1e6;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// src/engine/impact.ts
|
|
647
|
-
var DEPEND_EDGES = /* @__PURE__ */ new Set(["call", "import", "extends", "implements", "references"]);
|
|
648
|
-
function impactOf(graph, rootId, opts = {}) {
|
|
649
|
-
const maxDepth = Math.max(1, opts.depth ?? 4);
|
|
650
|
-
const index = indexFor(graph);
|
|
651
|
-
const root = index.node(rootId);
|
|
652
|
-
const affected = /* @__PURE__ */ new Map();
|
|
653
|
-
let minEdgeConfidence = 1;
|
|
654
|
-
let frontier = [{ id: rootId, conf: 1 }];
|
|
655
|
-
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
656
|
-
const nextFrontier = [];
|
|
657
|
-
for (const { id, conf } of frontier) {
|
|
658
|
-
for (const e of index.in(id)) {
|
|
659
|
-
if (!DEPEND_EDGES.has(e.kind)) continue;
|
|
660
|
-
minEdgeConfidence = Math.min(minEdgeConfidence, e.confidence);
|
|
661
|
-
const dependentId = e.src;
|
|
662
|
-
if (dependentId === rootId || affected.has(dependentId)) continue;
|
|
663
|
-
const node = index.node(dependentId);
|
|
664
|
-
if (!node) continue;
|
|
665
|
-
const conf2 = round2(conf * e.confidence * 0.9);
|
|
666
|
-
affected.set(dependentId, {
|
|
667
|
-
id: node.id,
|
|
668
|
-
name: node.qualifiedName,
|
|
669
|
-
kind: node.kind,
|
|
670
|
-
file: node.file,
|
|
671
|
-
line: node.span.start,
|
|
672
|
-
depth,
|
|
673
|
-
confidence: conf2
|
|
674
|
-
});
|
|
675
|
-
nextFrontier.push({ id: dependentId, conf: conf2 });
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
frontier = nextFrontier;
|
|
679
|
-
if (frontier.length === 0) break;
|
|
680
|
-
}
|
|
681
|
-
const items = [...affected.values()].sort(
|
|
682
|
-
(a, b) => a.depth - b.depth || b.confidence - a.confidence || a.name.localeCompare(b.name)
|
|
683
|
-
);
|
|
684
|
-
return {
|
|
685
|
-
root: { id: rootId, name: root?.qualifiedName ?? rootId },
|
|
686
|
-
depth: maxDepth,
|
|
687
|
-
affected: items,
|
|
688
|
-
direct: items.filter((i) => i.depth === 1).length,
|
|
689
|
-
transitive: items.filter((i) => i.depth > 1).length,
|
|
690
|
-
minEdgeConfidence: round2(minEdgeConfidence)
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
function round2(x) {
|
|
694
|
-
return Math.round(x * 1e6) / 1e6;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
export { GraphIndex, conceptMapLines, extractLiteralNeedles, identifierParts, impactOf, indexFor, isLocateOnlyInstruction, queryGraph, queryGraphSemantic, residualForSymbolSearch, userAskFromInstruction };
|
|
698
|
-
//# sourceMappingURL=chunk-VEQA2FSM.js.map
|
|
699
|
-
//# sourceMappingURL=chunk-VEQA2FSM.js.map
|