sweet-search 2.5.2 → 2.5.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/core/cli.js +24 -3
- package/core/graph/graph-expansion.js +215 -36
- package/core/graph/graph-extractor.js +196 -11
- package/core/graph/graph-search.js +395 -92
- package/core/graph/hcgs-generator.js +2 -1
- package/core/graph/index.js +2 -0
- package/core/graph/repo-map.js +28 -6
- package/core/graph/structural-answer-cues.js +168 -0
- package/core/graph/structural-callsite-hints.js +40 -0
- package/core/graph/structural-context-format.js +40 -0
- package/core/graph/structural-context.js +450 -0
- package/core/graph/structural-forward-push.js +156 -0
- package/core/graph/structural-header-context.js +19 -0
- package/core/graph/structural-importance.js +148 -0
- package/core/graph/structural-pagerank.js +197 -0
- package/core/graph/summary-manager.js +13 -9
- package/core/incremental-indexing/application/dirty-scan.mjs +236 -0
- package/core/incremental-indexing/application/file-watcher.mjs +197 -0
- package/core/incremental-indexing/application/maintenance-handlers.mjs +519 -0
- package/core/incremental-indexing/application/maintenance-worker.mjs +380 -0
- package/core/incremental-indexing/application/operator-cli.mjs +554 -0
- package/core/incremental-indexing/application/production-li-delta.mjs +192 -0
- package/core/incremental-indexing/application/production-reconciler-helpers.mjs +107 -0
- package/core/incremental-indexing/application/production-reconciler.mjs +583 -0
- package/core/incremental-indexing/application/reconciler.mjs +477 -0
- package/core/incremental-indexing/application/tombstone-injector.mjs +148 -0
- package/core/incremental-indexing/domain/chunk-identity.mjs +260 -0
- package/core/incremental-indexing/domain/encoder-deps.mjs +193 -0
- package/core/incremental-indexing/domain/encoder-input.mjs +225 -0
- package/core/incremental-indexing/domain/interval-autotune.mjs +255 -0
- package/core/incremental-indexing/domain/reconcile-counters.mjs +149 -0
- package/core/incremental-indexing/domain/watermark-scheduler.mjs +239 -0
- package/core/incremental-indexing/infrastructure/artifact-temp-sweep.mjs +163 -0
- package/core/incremental-indexing/infrastructure/baseline-readiness.mjs +121 -0
- package/core/incremental-indexing/infrastructure/dirty-set.mjs +233 -0
- package/core/incremental-indexing/infrastructure/graph-gc.mjs +314 -0
- package/core/incremental-indexing/infrastructure/hashing.mjs +298 -0
- package/core/incremental-indexing/infrastructure/hcgs-invalidation.mjs +182 -0
- package/core/incremental-indexing/infrastructure/li-segment-merge.mjs +278 -0
- package/core/incremental-indexing/infrastructure/li-segment-state.mjs +173 -0
- package/core/incremental-indexing/infrastructure/lockfile.mjs +119 -0
- package/core/incremental-indexing/infrastructure/maintenance-state-reader.mjs +283 -0
- package/core/incremental-indexing/infrastructure/manifest.mjs +194 -0
- package/core/incremental-indexing/infrastructure/path-filter.mjs +190 -0
- package/core/incremental-indexing/infrastructure/reader-heartbeat.mjs +201 -0
- package/core/incremental-indexing/infrastructure/schema-migrations.mjs +257 -0
- package/core/incremental-indexing/infrastructure/sparse-gram-delta.mjs +335 -0
- package/core/incremental-indexing/infrastructure/sqlite-fts5.mjs +176 -0
- package/core/incremental-indexing/infrastructure/staleness-display.mjs +105 -0
- package/core/incremental-indexing/infrastructure/tombstone-bitmap.mjs +234 -0
- package/core/incremental-indexing/infrastructure/vector-delta-writer.mjs +359 -0
- package/core/incremental-indexing/infrastructure/vector-gc.mjs +133 -0
- package/core/incremental-indexing/infrastructure/worktree-stamp.mjs +155 -0
- package/core/incremental-indexing/infrastructure/wsl2-detect.mjs +115 -0
- package/core/indexing/admission-policy.js +139 -0
- package/core/indexing/artifact-builder.js +29 -12
- package/core/indexing/ast-chunker.js +107 -30
- package/core/indexing/dedup/exemplar-selector.js +19 -1
- package/core/indexing/gitignore-filter.js +223 -0
- package/core/indexing/incremental-tracker.js +99 -30
- package/core/indexing/index-codebase-v21.js +6 -5
- package/core/indexing/index-maintainer.mjs +698 -6
- package/core/indexing/indexer-ann.js +99 -15
- package/core/indexing/indexer-build.js +158 -45
- package/core/indexing/indexer-empty-baseline.js +80 -0
- package/core/indexing/indexer-manifest.js +66 -0
- package/core/indexing/indexer-phases.js +56 -23
- package/core/indexing/indexer-sparse-gram.js +54 -13
- package/core/indexing/indexer-utils.js +26 -208
- package/core/indexing/indexing-file-policy.js +32 -7
- package/core/indexing/maintainer-launcher.mjs +137 -0
- package/core/indexing/merkle-tracker.js +251 -244
- package/core/indexing/model-pool.js +46 -5
- package/core/infrastructure/code-graph-repository.js +758 -6
- package/core/infrastructure/code-graph-visibility.js +157 -0
- package/core/infrastructure/codebase-repository.js +100 -13
- package/core/infrastructure/config/search.js +1 -1
- package/core/infrastructure/db-utils.js +118 -0
- package/core/infrastructure/dedup-hashing.js +10 -13
- package/core/infrastructure/hardware-capability.js +17 -7
- package/core/infrastructure/index.js +8 -2
- package/core/infrastructure/language-patterns/maps.js +4 -1
- package/core/infrastructure/language-patterns/registry-core.js +56 -17
- package/core/infrastructure/language-patterns/registry-object-oriented.js +12 -5
- package/core/infrastructure/language-patterns.js +69 -0
- package/core/infrastructure/model-registry.js +20 -0
- package/core/infrastructure/native-inference.js +7 -12
- package/core/infrastructure/native-resolver.js +52 -37
- package/core/infrastructure/native-sparse-gram.js +261 -20
- package/core/infrastructure/native-tokenizer.js +6 -15
- package/core/infrastructure/simd-distance.js +10 -16
- package/core/infrastructure/sparse-gram-delta-reader.js +76 -0
- package/core/infrastructure/structural-alias-resolver.js +122 -0
- package/core/infrastructure/structural-candidate-ranker.js +34 -0
- package/core/infrastructure/structural-context-repository.js +472 -0
- package/core/infrastructure/structural-context-utils.js +51 -0
- package/core/infrastructure/structural-graph-signals.js +121 -0
- package/core/infrastructure/structural-qualified-resolution.js +15 -0
- package/core/infrastructure/structural-source-definitions.js +100 -0
- package/core/infrastructure/tombstone-bitmap-reader.js +139 -0
- package/core/infrastructure/tree-sitter-provider.js +811 -37
- package/core/prompt-optimization/data/p7-final/sweet-search-system-prompt.md +50 -0
- package/core/query/query-router.js +55 -5
- package/core/ranking/file-kind-ranking.js +2192 -15
- package/core/ranking/late-interaction-index.js +87 -12
- package/core/search/cli-decoration.js +290 -0
- package/core/search/context-expander.js +988 -78
- package/core/search/index.js +1 -0
- package/core/search/output-policy.js +275 -0
- package/core/search/search-anchor.js +499 -0
- package/core/search/search-boost.js +93 -1
- package/core/search/search-cli.js +61 -204
- package/core/search/search-hybrid.js +250 -10
- package/core/search/search-pattern-chunks.js +57 -8
- package/core/search/search-pattern-planner.js +68 -9
- package/core/search/search-pattern-prefilter.js +30 -10
- package/core/search/search-pattern-ripgrep.js +40 -4
- package/core/search/search-pattern-sparse-overlay.js +256 -0
- package/core/search/search-pattern.js +117 -29
- package/core/search/search-postprocess.js +479 -5
- package/core/search/search-read-semantic.js +260 -23
- package/core/search/search-read.js +82 -64
- package/core/search/search-reader-pin.js +71 -0
- package/core/search/search-rrf.js +279 -0
- package/core/search/search-semantic.js +110 -5
- package/core/search/search-server.js +130 -57
- package/core/search/search-trace.js +107 -0
- package/core/search/server-identity.js +93 -0
- package/core/search/session-daemon-prewarm.mjs +33 -10
- package/core/search/sweet-search.js +399 -7
- package/core/skills/sweet-index/SKILL.md +8 -6
- package/core/vector-store/binary-hnsw-index.js +194 -30
- package/core/vector-store/float-vector-store.js +96 -6
- package/core/vector-store/hnsw-index.js +220 -49
- package/eval/agent-read-workflows/bin/_ss-helpers.mjs +471 -0
- package/eval/agent-read-workflows/bin/ss-find +15 -0
- package/eval/agent-read-workflows/bin/ss-grep +12 -0
- package/eval/agent-read-workflows/bin/ss-read +14 -0
- package/eval/agent-read-workflows/bin/ss-search +18 -0
- package/eval/agent-read-workflows/bin/ss-semantic +12 -0
- package/eval/agent-read-workflows/bin/ss-trace +11 -0
- package/mcp/read-tool.js +109 -0
- package/mcp/server.js +55 -15
- package/mcp/tool-handlers.js +14 -124
- package/mcp/trace-tool.js +81 -0
- package/package.json +25 -10
- package/scripts/hooks/intercept-read.mjs +55 -0
- package/scripts/hooks/remind-tools.mjs +40 -0
- package/scripts/init.js +698 -54
- package/scripts/inject-agent-instructions.js +431 -0
- package/scripts/install-prompt-reminders.js +188 -0
- package/scripts/install-tool-enforcement.js +220 -0
- package/scripts/smoke-test.js +12 -9
- package/scripts/uninstall.js +276 -18
- package/scripts/write-claude-rules.js +110 -0
|
@@ -399,12 +399,13 @@ async function generateSummariesForEntities(entityIds, options = {}) {
|
|
|
399
399
|
} = options;
|
|
400
400
|
|
|
401
401
|
const Database = (await import('better-sqlite3')).default;
|
|
402
|
-
const { applyReadPragmas } = await import('../infrastructure/db-utils.js');
|
|
402
|
+
const { applyReadPragmas, assertInClauseSize } = await import('../infrastructure/db-utils.js');
|
|
403
403
|
const db = new Database(dbPath, { readonly: true });
|
|
404
404
|
applyReadPragmas(db);
|
|
405
405
|
|
|
406
406
|
try {
|
|
407
407
|
// Fetch entities by IDs
|
|
408
|
+
assertInClauseSize(entityIds.length, 'hcgs-generator.fetchEntitiesByIds');
|
|
408
409
|
const placeholders = entityIds.map(() => '?').join(',');
|
|
409
410
|
const entities = db.prepare(`
|
|
410
411
|
SELECT id, file_path, type, name, signature, doc_comment, parent_id, hierarchy_level
|
package/core/graph/index.js
CHANGED
|
@@ -11,6 +11,8 @@ export { default as communityDetector } from './community-detector.js';
|
|
|
11
11
|
export * from './leiden-algorithm.js';
|
|
12
12
|
export * from './repo-map.js';
|
|
13
13
|
export { default as repoMap } from './repo-map.js';
|
|
14
|
+
export * from './structural-context.js';
|
|
15
|
+
export { default as StructuralContextBuilder } from './structural-context.js';
|
|
14
16
|
export * from './hcgs-generator.js';
|
|
15
17
|
export * from './summary-manager.js';
|
|
16
18
|
export { default as summaryManager } from './summary-manager.js';
|
package/core/graph/repo-map.js
CHANGED
|
@@ -17,6 +17,16 @@ import Database from 'better-sqlite3';
|
|
|
17
17
|
import path from 'path';
|
|
18
18
|
import { DB_PATHS } from '../infrastructure/config/index.js';
|
|
19
19
|
import { applyReadPragmas } from '../infrastructure/db-utils.js';
|
|
20
|
+
import {
|
|
21
|
+
createCodeGraphVisibility,
|
|
22
|
+
entityVisibilityParams,
|
|
23
|
+
entityVisibilitySql,
|
|
24
|
+
readAdjacentManifest,
|
|
25
|
+
readAdjacentManifestEpoch,
|
|
26
|
+
relationshipVisibilityParams,
|
|
27
|
+
relationshipVisibilitySql,
|
|
28
|
+
resolveManifestCodeGraphPath,
|
|
29
|
+
} from '../infrastructure/code-graph-visibility.js';
|
|
20
30
|
|
|
21
31
|
// ---------------------------------------------------------------------------
|
|
22
32
|
// Constants
|
|
@@ -119,24 +129,32 @@ export function pageRank(outEdges, allNodes, opts = {}) {
|
|
|
119
129
|
/**
|
|
120
130
|
* Load graph data from the code-graph.db SQLite database.
|
|
121
131
|
* @param {string} [dbPath] - Path to code-graph.db
|
|
132
|
+
* @param {{ manifestEpoch?: number }} [opts]
|
|
122
133
|
* @returns {{ entities: Array, relationships: Array }}
|
|
123
134
|
*/
|
|
124
|
-
export function loadGraph(dbPath) {
|
|
125
|
-
const
|
|
135
|
+
export function loadGraph(dbPath, opts = {}) {
|
|
136
|
+
const basePath = dbPath || DB_PATHS.codeGraph;
|
|
137
|
+
const manifest = opts.manifest || readAdjacentManifest(basePath);
|
|
138
|
+
const resolvedPath = resolveManifestCodeGraphPath(basePath, manifest);
|
|
126
139
|
const db = new Database(resolvedPath, { readonly: true, timeout: 5000 });
|
|
127
140
|
applyReadPragmas(db);
|
|
128
141
|
|
|
129
142
|
try {
|
|
143
|
+
const manifestEpoch = Number.isInteger(opts.manifestEpoch)
|
|
144
|
+
? opts.manifestEpoch
|
|
145
|
+
: (Number.isInteger(manifest?.epoch) ? manifest.epoch : readAdjacentManifestEpoch(resolvedPath));
|
|
146
|
+
const visibility = createCodeGraphVisibility(db, manifestEpoch);
|
|
130
147
|
const entities = db.prepare(`
|
|
131
148
|
SELECT id, file_path, type, name, signature, start_line, end_line
|
|
132
149
|
FROM entities
|
|
133
|
-
WHERE
|
|
134
|
-
`).all();
|
|
150
|
+
WHERE ${entityVisibilitySql(visibility)}
|
|
151
|
+
`).all(...entityVisibilityParams(visibility));
|
|
135
152
|
|
|
136
153
|
const relationships = db.prepare(`
|
|
137
154
|
SELECT source_id, target_id, target_name, type, weight
|
|
138
155
|
FROM relationships
|
|
139
|
-
|
|
156
|
+
WHERE ${relationshipVisibilitySql(visibility, '')}
|
|
157
|
+
`).all(...relationshipVisibilityParams(visibility));
|
|
140
158
|
|
|
141
159
|
return { entities, relationships };
|
|
142
160
|
} finally {
|
|
@@ -335,6 +353,7 @@ function buildMapText(entries) {
|
|
|
335
353
|
* @param {string} [opts.dbPath] - Override code-graph.db path
|
|
336
354
|
* @param {string[]} [opts.focusFiles] - Boost scores for entities in these files
|
|
337
355
|
* @param {string[]} [opts.focusEntities] - Boost scores for these entity names
|
|
356
|
+
* @param {number} [opts.manifestEpoch] - Optional pinned manifest epoch for incremental readers
|
|
338
357
|
* @returns {{ text: string, entityCount: number, fileCount: number, totalEntities: number, pageRankTimeMs: number }}
|
|
339
358
|
*/
|
|
340
359
|
export function generateRepoMap(opts = {}) {
|
|
@@ -342,7 +361,10 @@ export function generateRepoMap(opts = {}) {
|
|
|
342
361
|
const start = Date.now();
|
|
343
362
|
|
|
344
363
|
// 1. Load graph from SQLite
|
|
345
|
-
const graph = loadGraph(opts.dbPath
|
|
364
|
+
const graph = loadGraph(opts.dbPath, {
|
|
365
|
+
manifest: opts.manifest,
|
|
366
|
+
manifestEpoch: opts.manifestEpoch,
|
|
367
|
+
});
|
|
346
368
|
|
|
347
369
|
if (graph.entities.length === 0) {
|
|
348
370
|
return {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { stripNonCode } from './structural-callsite-hints.js';
|
|
2
|
+
|
|
3
|
+
const STOP = new Set([
|
|
4
|
+
'const', 'let', 'var', 'return', 'function', 'export', 'import', 'from', 'null', 'true', 'false',
|
|
5
|
+
'this', 'self', 'if', 'else', 'for', 'while', 'switch', 'case', 'break', 'continue', 'typeof',
|
|
6
|
+
'string', 'number', 'boolean', 'undefined', 'object', 'class', 'struct', 'enum', 'trait', 'impl',
|
|
7
|
+
'pub', 'fn', 'func', 'mut', 'use', 'type', 'async', 'await', 'new', 'def', 'len', 'none', 'the', 'that', 'this', 'with',
|
|
8
|
+
'when', 'where', 'which', 'from', 'into', 'will', 'can', 'may', 'not', 'and', 'or',
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function terms(text) {
|
|
12
|
+
const out = new Map();
|
|
13
|
+
for (const m of String(text || '').matchAll(/[A-Za-z_$][A-Za-z0-9_$]{2,}/g)) {
|
|
14
|
+
const raw = m[0];
|
|
15
|
+
const key = raw.toLowerCase();
|
|
16
|
+
if (STOP.has(key)) continue;
|
|
17
|
+
const prev = out.get(key) || { raw, count: 0, score: 0 };
|
|
18
|
+
prev.count++;
|
|
19
|
+
prev.score += /[A-Z_]/.test(raw.slice(1)) || /^[A-Z]/.test(raw) ? 2 : 1;
|
|
20
|
+
out.set(key, prev);
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function rankedTerms(target, hint) {
|
|
26
|
+
const found = terms(`${target.signature || ''}\n${target.summary || ''}\n${stripNonCode(target.code || '')}`);
|
|
27
|
+
const hintText = String(hint || '').toLowerCase();
|
|
28
|
+
for (const [key, value] of found) {
|
|
29
|
+
if (hintText.includes(key)) value.score += 4;
|
|
30
|
+
value.score += Math.min(4, value.count);
|
|
31
|
+
}
|
|
32
|
+
return [...found.values()]
|
|
33
|
+
.sort((a, b) => (b.score - a.score) || a.raw.localeCompare(b.raw))
|
|
34
|
+
.slice(0, 24)
|
|
35
|
+
.map(x => x.raw);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function shortItem(x) {
|
|
39
|
+
const loc = x.filePath ? `${x.filePath}:${x.startLine || '?'}` : 'external';
|
|
40
|
+
const edge = x.relationship ? `${x.relationship} ` : '';
|
|
41
|
+
return `${edge}${x.name} (${loc})`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function shortPath(p) {
|
|
45
|
+
return p.path.map(x => `${x.name}${x.filePath ? `@${x.filePath}:${x.startLine || '?'}` : '@external'}`).join(' -> ');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isLowSignalPath(filePath = '') {
|
|
49
|
+
return /(^|\/)(__tests__|tests?|spec|fixtures|examples?|docs?)(\/|$)|[-_.](test|spec)\.[cm]?[jt]sx?$|_test\.go$/.test(filePath);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function preferProduction(items, isLowSignal) {
|
|
53
|
+
const primary = items.filter(item => !isLowSignal(item));
|
|
54
|
+
return primary.length ? primary : items;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function addSymbol(out, name) {
|
|
58
|
+
if (name && !out.includes(name)) out.push(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function keySymbols(target, hint, callers, callees, impactPaths) {
|
|
62
|
+
const q = String(hint || '').toLowerCase();
|
|
63
|
+
const out = [];
|
|
64
|
+
addSymbol(out, target.name);
|
|
65
|
+
const wantsCallers = /\b(caller|callers|upstream|references)\b/.test(q);
|
|
66
|
+
const wantsCallees = /\b(callee|callees|downstream|helper|helpers|conversion|next)\b/.test(q);
|
|
67
|
+
const wantsImpact = /\b(impact|changing|change|affect|break|handoff|surface)\b/.test(q);
|
|
68
|
+
if (wantsCallers || (!wantsCallees && !wantsImpact)) callers.slice(0, 3).forEach(item => addSymbol(out, item.name));
|
|
69
|
+
if (wantsCallees || (!wantsCallers && !wantsImpact)) callees.slice(0, 4).forEach(item => addSymbol(out, item.name));
|
|
70
|
+
if (wantsImpact) {
|
|
71
|
+
for (const path of impactPaths.slice(0, 4)) {
|
|
72
|
+
for (const item of path.path || []) {
|
|
73
|
+
if (item.type !== 'external') addSymbol(out, item.name);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out.slice(0, 10);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const BRANCH_ORDER = [
|
|
81
|
+
'tuple', 'dict', 'list', 'str', 'string', 'bytes', 'bytearray', 'buffer', 'stream', 'json',
|
|
82
|
+
'response', 'status', 'headers', 'params', 'body', 'querystring', 'atomicbool', 'parallel',
|
|
83
|
+
];
|
|
84
|
+
const BRANCH_WORDS = new Set(BRANCH_ORDER);
|
|
85
|
+
|
|
86
|
+
function branchTerms(targetTerms) {
|
|
87
|
+
const byLower = new Map();
|
|
88
|
+
for (const term of targetTerms) {
|
|
89
|
+
const lower = term.toLowerCase();
|
|
90
|
+
if (BRANCH_WORDS.has(lower)) {
|
|
91
|
+
byLower.set(lower, term);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
for (const branch of BRANCH_ORDER) {
|
|
95
|
+
if (lower === `${branch}s` || lower.startsWith(`${branch}schema`)) {
|
|
96
|
+
byLower.set(branch, branch);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return BRANCH_ORDER.map(term => byLower.get(term)).filter(Boolean).slice(0, 12);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function branchSnippets(target, branches) {
|
|
104
|
+
if (!branches.length || !target.code) return [];
|
|
105
|
+
const wanted = branches.map(x => x.toLowerCase());
|
|
106
|
+
const lines = String(target.code).split('\n');
|
|
107
|
+
const out = [];
|
|
108
|
+
const covered = new Set();
|
|
109
|
+
let tripleQuote = null;
|
|
110
|
+
for (let i = 0; i < lines.length && out.length < 12; i++) {
|
|
111
|
+
const line = lines[i].trim();
|
|
112
|
+
const quote = line.includes('"""') ? '"""' : line.includes("'''") ? "'''" : null;
|
|
113
|
+
if (tripleQuote) {
|
|
114
|
+
if (quote === tripleQuote) tripleQuote = null;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (quote) {
|
|
118
|
+
if ((line.match(new RegExp(quote, 'g')) || []).length % 2 === 1) tripleQuote = quote;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (!line || /^(#|\/\/|\/\*|\*|`|:)/.test(line)) continue;
|
|
122
|
+
if (!/[()[\]{}=:]|\b(if|elif|else|case|switch|const|let|var|return)\b/.test(line)) continue;
|
|
123
|
+
const lower = line.toLowerCase();
|
|
124
|
+
const hits = wanted.filter(term => lower.includes(term) || lower.includes(`${term}schema`));
|
|
125
|
+
if (!hits.length || hits.every(term => covered.has(term))) continue;
|
|
126
|
+
const lineNo = (target.startLine || 1) + i;
|
|
127
|
+
out.push(`L${lineNo}: ${line.replace(/\s+/g, ' ')}`);
|
|
128
|
+
hits.forEach(term => covered.add(term));
|
|
129
|
+
if (covered.size >= wanted.length) break;
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function relatedDefinitions(target, targetTerms, resolveTerm) {
|
|
135
|
+
if (!resolveTerm) return [];
|
|
136
|
+
const out = [];
|
|
137
|
+
const seen = new Set([target.id]);
|
|
138
|
+
for (const term of targetTerms.slice(0, 12)) {
|
|
139
|
+
const found = resolveTerm(term);
|
|
140
|
+
if (!found || seen.has(found.id) || found.filePath !== target.filePath) continue;
|
|
141
|
+
if (found.name === target.name && found.startLine === target.startLine) continue;
|
|
142
|
+
seen.add(found.id);
|
|
143
|
+
const snippet = found.summary && found.summary !== 'Same-file definition found from source text'
|
|
144
|
+
? ` - ${found.summary}`
|
|
145
|
+
: '';
|
|
146
|
+
out.push(`${found.name} [${found.type}] ${found.filePath}:${found.startLine || '?'}${snippet}`);
|
|
147
|
+
if (out.length >= 6) break;
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function buildAnswerCues({ target, hint, callers, callees, impactPaths, resolveTerm }) {
|
|
153
|
+
const targetTerms = rankedTerms(target, hint);
|
|
154
|
+
const branches = branchTerms(targetTerms);
|
|
155
|
+
const cueCallers = preferProduction(callers, item => isLowSignalPath(item.filePath));
|
|
156
|
+
const cueCallees = preferProduction(callees, item => isLowSignalPath(item.filePath));
|
|
157
|
+
const cuePaths = preferProduction(impactPaths, path => (path.path || []).some(item => isLowSignalPath(item.filePath)));
|
|
158
|
+
return {
|
|
159
|
+
targetTerms,
|
|
160
|
+
keySymbols: keySymbols(target, hint, cueCallers, cueCallees, cuePaths),
|
|
161
|
+
branchTerms: branches,
|
|
162
|
+
branchSnippets: branchSnippets(target, branches),
|
|
163
|
+
relatedDefinitions: relatedDefinitions(target, targetTerms, resolveTerm),
|
|
164
|
+
topCallers: cueCallers.slice(0, 5).map(shortItem),
|
|
165
|
+
topCallees: cueCallees.slice(0, 5).map(shortItem),
|
|
166
|
+
criticalPaths: cuePaths.slice(0, 5).map(shortPath),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const SKIP = new Set([
|
|
2
|
+
'if', 'for', 'while', 'switch', 'catch', 'function', 'func', 'return', 'defer', 'go', 'select',
|
|
3
|
+
'len', 'cap', 'make', 'new', 'append', 'copy', 'delete', 'panic', 'recover',
|
|
4
|
+
'setTimeout', 'clearTimeout', 'require', 'import', 'new', 'await', 'async',
|
|
5
|
+
]);
|
|
6
|
+
|
|
7
|
+
const MEMBER_SKIP = new Set([
|
|
8
|
+
'bind', 'call', 'apply', 'map', 'filter', 'reduce', 'forEach', 'then', 'catch',
|
|
9
|
+
'toString', 'String', 'Error', 'Println', 'Printf', 'Errorf', 'Fatalf',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
function addHint(out, known, name, member = false) {
|
|
13
|
+
if (!name || known.has(name) || out.includes(name)) return;
|
|
14
|
+
if (SKIP.has(name) || (member && MEMBER_SKIP.has(name))) return;
|
|
15
|
+
out.push(name);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function stripNonCode(text) {
|
|
19
|
+
return String(text || '')
|
|
20
|
+
.replace(/("""|''')[\s\S]*?\1/g, '')
|
|
21
|
+
.replace(/(["'`])(?:\\.|(?!\1)[\s\S])*?\1/g, '')
|
|
22
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
23
|
+
.replace(/^\s*(#|\/\/).*$/gm, '');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function callsiteHints(code, known = new Set()) {
|
|
27
|
+
const out = [];
|
|
28
|
+
const text = stripNonCode(code);
|
|
29
|
+
const free = /(?<![.\w$])([A-Za-z_$][\w$]*)\s*(?:\.(?:bind|call|apply))?\s*\(/g;
|
|
30
|
+
for (const m of text.matchAll(free)) {
|
|
31
|
+
addHint(out, known, m[1], false);
|
|
32
|
+
if (out.length >= 12) return out;
|
|
33
|
+
}
|
|
34
|
+
const member = /(?:\.|::)\s*([A-Za-z_$][\w$]*)\s*\(/g;
|
|
35
|
+
for (const m of text.matchAll(member)) {
|
|
36
|
+
addHint(out, known, m[1], true);
|
|
37
|
+
if (out.length >= 12) return out;
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function formatStructuralContext(result) {
|
|
2
|
+
if (!result.target) return `No indexed symbol found for "${result.symbol}".`;
|
|
3
|
+
const t = result.target;
|
|
4
|
+
const lines = [];
|
|
5
|
+
lines.push(`# trace ${t.name} [${t.type}] ${t.filePath}:${t.startLine}-${t.endLine}`);
|
|
6
|
+
lines.push(`fan-in=${t.fanIn} fan-out=${t.fanOut} budget=${result.tokensUsed}/${result.tokenBudget} (${result.budgetTier}:${result.budgetReason}) latency=${result.stats.latencyMs}ms`);
|
|
7
|
+
if (result.disambiguation.length) {
|
|
8
|
+
lines.push(`ambiguous: using first match; alternatives: ${result.disambiguation.slice(0, 5).map(a => `${a.name} ${a.file}:${a.startLine}`).join(', ')}`);
|
|
9
|
+
}
|
|
10
|
+
if (result.answerCues?.targetTerms?.length) {
|
|
11
|
+
if (result.answerCues.keySymbols?.length) lines.push(`answer checklist: key symbols=${result.answerCues.keySymbols.join(', ')}`);
|
|
12
|
+
if (result.answerCues.branchTerms?.length) lines.push(`answer checklist: branch terms to preserve=${result.answerCues.branchTerms.join(', ')}`);
|
|
13
|
+
if (result.answerCues.branchSnippets?.length) lines.push(`answer checklist: branch code=${result.answerCues.branchSnippets.join(' | ')}`);
|
|
14
|
+
if (result.answerCues.relatedDefinitions?.length) lines.push(`answer checklist: related definitions=${result.answerCues.relatedDefinitions.join(' | ')}`);
|
|
15
|
+
lines.push(`answer cues: target terms=${result.answerCues.targetTerms.join(', ')}`);
|
|
16
|
+
if (result.answerCues.topCallers.length) lines.push(`answer cues: top callers=${result.answerCues.topCallers.join(' | ')}`);
|
|
17
|
+
if (result.answerCues.topCallees.length) lines.push(`answer cues: top callees=${result.answerCues.topCallees.join(' | ')}`);
|
|
18
|
+
if (result.answerCues.criticalPaths.length) lines.push(`answer cues: critical paths=${result.answerCues.criticalPaths.join(' | ')}`);
|
|
19
|
+
}
|
|
20
|
+
if (t.headerContext) lines.push('\n## target imports\n```', t.headerContext, '```');
|
|
21
|
+
if (t.code) lines.push('\n## target\n```', t.code, '```');
|
|
22
|
+
if (t.callsiteHints?.length) lines.push(`target callsite hints: ${t.callsiteHints.join(', ')}`);
|
|
23
|
+
for (const [title, section] of [['callers', result.sections.callers], ['callees', result.sections.callees]]) {
|
|
24
|
+
lines.push(`\n## ${title} (${section.total})`);
|
|
25
|
+
for (const item of section.items) {
|
|
26
|
+
lines.push(`\n### ${item.name} [${item.type}] importance=${item.importance}`);
|
|
27
|
+
lines.push(item.summary);
|
|
28
|
+
if (item.code) lines.push('```', item.code, '```');
|
|
29
|
+
}
|
|
30
|
+
if (!section.items.length) lines.push('(none)');
|
|
31
|
+
}
|
|
32
|
+
lines.push(`\n## impact paths (${result.sections.impact.total}, depth <= ${result.maxDepth})`);
|
|
33
|
+
if (!result.sections.impact.paths.length) lines.push('(none)');
|
|
34
|
+
result.sections.impact.paths.forEach((p, i) => {
|
|
35
|
+
lines.push(`${i + 1}. ${p.direction} importance=${p.importance} ${p.path}`);
|
|
36
|
+
});
|
|
37
|
+
return lines.join('\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default formatStructuralContext;
|