@toolbaux/guardian 0.1.20 → 0.1.21
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/commands/search.js +57 -18
- package/package.json +1 -1
package/dist/commands/search.js
CHANGED
|
@@ -11,6 +11,7 @@ export async function runSearch(options) {
|
|
|
11
11
|
const heatmap = await loadHeatmap(inputDir);
|
|
12
12
|
const funcIntel = await loadFunctionIntelligence(inputDir);
|
|
13
13
|
const types = normalizeTypes(options.types);
|
|
14
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
14
15
|
const matches = searchSnapshots({
|
|
15
16
|
architecture,
|
|
16
17
|
ux,
|
|
@@ -18,6 +19,8 @@ export async function runSearch(options) {
|
|
|
18
19
|
types,
|
|
19
20
|
heatmap,
|
|
20
21
|
funcIntel,
|
|
22
|
+
projectRoot,
|
|
23
|
+
topN: options.topN ?? 10,
|
|
21
24
|
});
|
|
22
25
|
const content = renderSearchMarkdown(options.query, matches);
|
|
23
26
|
if (options.output) {
|
|
@@ -113,7 +116,7 @@ function scoreItem(queryTokens, item) {
|
|
|
113
116
|
return Math.min(1, total / queryTokens.length);
|
|
114
117
|
}
|
|
115
118
|
function searchSnapshots(params) {
|
|
116
|
-
const { architecture, ux, query, types, heatmap, funcIntel } = params;
|
|
119
|
+
const { architecture, ux, query, types, heatmap, funcIntel, projectRoot, topN } = params;
|
|
117
120
|
const queryTokens = tokenize(query);
|
|
118
121
|
const matches = [];
|
|
119
122
|
const pageUsage = buildComponentPageUsage(ux);
|
|
@@ -243,16 +246,23 @@ function searchSnapshots(params) {
|
|
|
243
246
|
}
|
|
244
247
|
if (types.has("functions") && funcIntel) {
|
|
245
248
|
const queryTokens = tokenize(query);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
text: [...fn.stringLiterals, ...fn.regexPatterns, ...fn.calls, fn.language],
|
|
252
|
-
});
|
|
253
|
-
if (score <= 0)
|
|
249
|
+
const fnMatches = [];
|
|
250
|
+
// Build a map: file → model field names (feature 3 — field augmentation)
|
|
251
|
+
const fileToFields = new Map();
|
|
252
|
+
for (const model of architecture.data_models) {
|
|
253
|
+
if (!model.file)
|
|
254
254
|
continue;
|
|
255
|
-
const
|
|
255
|
+
const existing = fileToFields.get(model.file) ?? [];
|
|
256
|
+
fileToFields.set(model.file, [...existing, ...model.fields]);
|
|
257
|
+
}
|
|
258
|
+
// Helper: relativize a path if it looks absolute
|
|
259
|
+
const relativize = (filePath) => {
|
|
260
|
+
if (!path.isAbsolute(filePath))
|
|
261
|
+
return filePath;
|
|
262
|
+
return path.relative(projectRoot, filePath);
|
|
263
|
+
};
|
|
264
|
+
// Helper: build detail lines for a function hit
|
|
265
|
+
const buildDetail = (fn, relFile) => {
|
|
256
266
|
const detail = [];
|
|
257
267
|
if (fn.stringLiterals.length > 0) {
|
|
258
268
|
detail.push(`Literals: ${formatList(fn.stringLiterals.slice(0, 3).map((l) => `"${l.slice(0, 60)}"`), 3)}`);
|
|
@@ -263,42 +273,71 @@ function searchSnapshots(params) {
|
|
|
263
273
|
if (fn.calls.length > 0) {
|
|
264
274
|
detail.push(`Calls: ${formatList(fn.calls, 5)}`);
|
|
265
275
|
}
|
|
266
|
-
|
|
276
|
+
// Feature 3 — append model field names for the file this function lives in
|
|
277
|
+
const fields = fileToFields.get(fn.file) ?? fileToFields.get(relFile);
|
|
278
|
+
if (fields && fields.length > 0) {
|
|
279
|
+
detail.push(`Model fields: ${formatList(fields.slice(0, 8), 8)}`);
|
|
280
|
+
}
|
|
281
|
+
return detail;
|
|
282
|
+
};
|
|
283
|
+
// 1. Name match — function / theorem name contains a query token
|
|
284
|
+
for (const fn of funcIntel.functions) {
|
|
285
|
+
const score = scoreItem(queryTokens, {
|
|
286
|
+
name: fn.name,
|
|
287
|
+
file: fn.file,
|
|
288
|
+
text: [...fn.stringLiterals, ...fn.regexPatterns, ...fn.calls, fn.language],
|
|
289
|
+
});
|
|
290
|
+
if (score <= 0)
|
|
291
|
+
continue;
|
|
292
|
+
const relFile = relativize(fn.file);
|
|
293
|
+
const lineRange = `${fn.lines[0]}–${fn.lines[1]}`;
|
|
294
|
+
const detail = buildDetail(fn, relFile);
|
|
295
|
+
fnMatches.push({
|
|
267
296
|
type: "functions",
|
|
268
297
|
name: `${fn.name} (${fn.language})`,
|
|
269
298
|
score,
|
|
270
299
|
markdown: [
|
|
271
|
-
`**${fn.name}** · ${
|
|
300
|
+
`**${fn.name}** · ${relFile}:${lineRange} · ${fn.language}${fn.isAsync ? " · async" : ""}`,
|
|
272
301
|
...detail,
|
|
273
302
|
],
|
|
274
303
|
});
|
|
275
304
|
}
|
|
276
305
|
// 2. Literal index match — query token appears in a function's string/regex literals
|
|
277
|
-
// (
|
|
278
|
-
// the function name itself doesn't match)
|
|
306
|
+
// Uses proper scoreItem() ranking instead of hardcoded 0.6 to prevent noise flooding.
|
|
279
307
|
for (const tok of queryTokens) {
|
|
280
308
|
const hits = funcIntel.literal_index[tok.toLowerCase()];
|
|
281
309
|
if (!hits)
|
|
282
310
|
continue;
|
|
283
311
|
for (const hit of hits) {
|
|
284
312
|
// Skip if we already emitted this function via name match above
|
|
285
|
-
if (
|
|
313
|
+
if (fnMatches.some((m) => m.type === "functions" && m.name.startsWith(hit.function + " ("))) {
|
|
286
314
|
continue;
|
|
287
315
|
}
|
|
288
316
|
const fn = funcIntel.functions.find((f) => f.file === hit.file && f.name === hit.function);
|
|
289
317
|
if (!fn)
|
|
290
318
|
continue;
|
|
291
|
-
|
|
319
|
+
const score = scoreItem(queryTokens, {
|
|
320
|
+
name: fn.name,
|
|
321
|
+
file: fn.file,
|
|
322
|
+
text: [...fn.stringLiterals, ...fn.regexPatterns, ...fn.calls, fn.language],
|
|
323
|
+
});
|
|
324
|
+
const relFile = relativize(fn.file);
|
|
325
|
+
const detail = buildDetail(fn, relFile);
|
|
326
|
+
fnMatches.push({
|
|
292
327
|
type: "functions",
|
|
293
328
|
name: `${fn.name} (${fn.language})`,
|
|
294
|
-
score: 0.
|
|
329
|
+
score: Math.max(score, 0.2), // floor at 0.2 so literal hits still surface but rank below name matches
|
|
295
330
|
markdown: [
|
|
296
|
-
`**${fn.name}** · ${
|
|
331
|
+
`**${fn.name}** · ${relFile}:${fn.lines[0]}–${fn.lines[1]} · ${fn.language}`,
|
|
297
332
|
`Matched literal/pattern containing "${tok}"`,
|
|
333
|
+
...detail,
|
|
298
334
|
],
|
|
299
335
|
});
|
|
300
336
|
}
|
|
301
337
|
}
|
|
338
|
+
// Feature 2 — rank by score, cap at topN to prevent context flooding
|
|
339
|
+
fnMatches.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
340
|
+
matches.push(...fnMatches.slice(0, topN));
|
|
302
341
|
}
|
|
303
342
|
return matches.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
304
343
|
}
|
package/package.json
CHANGED