grepmax 0.18.1 → 0.20.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/.claude-plugin/marketplace.json +23 -0
- package/README.md +28 -3
- package/dist/commands/add.js +4 -2
- package/dist/commands/claude-code.js +1 -1
- package/dist/commands/codex.js +3 -1
- package/dist/commands/context.js +1 -1
- package/dist/commands/dead.js +2 -6
- package/dist/commands/doctor.js +26 -7
- package/dist/commands/extract.js +3 -5
- package/dist/commands/impact.js +6 -4
- package/dist/commands/index.js +7 -2
- package/dist/commands/log.js +4 -2
- package/dist/commands/mcp.js +594 -722
- package/dist/commands/peek.js +15 -9
- package/dist/commands/plugin.js +36 -22
- package/dist/commands/project.js +16 -6
- package/dist/commands/related.js +8 -9
- package/dist/commands/remove.js +9 -2
- package/dist/commands/review.js +1 -1
- package/dist/commands/setup.js +4 -2
- package/dist/commands/similar.js +4 -4
- package/dist/commands/status.js +6 -4
- package/dist/commands/summarize.js +6 -4
- package/dist/commands/symbols.js +1 -1
- package/dist/commands/test-find.js +2 -2
- package/dist/commands/trace.js +12 -5
- package/dist/commands/watch.js +33 -9
- package/dist/eval-graph-nav.js +4 -1
- package/dist/eval-graph-recovery-probe.js +56 -14
- package/dist/eval-graph-spotcheck.js +10 -2
- package/dist/eval-graph-totals.js +5 -2
- package/dist/eval-oss.js +212 -37
- package/dist/eval-seed.js +13 -4
- package/dist/index.js +9 -9
- package/dist/lib/daemon/daemon.js +32 -13
- package/dist/lib/daemon/ipc-handler.js +24 -5
- package/dist/lib/daemon/mlx-server-manager.js +11 -3
- package/dist/lib/daemon/process-manager.js +1 -2
- package/dist/lib/daemon/watcher-manager.js +22 -6
- package/dist/lib/graph/callsites.js +151 -25
- package/dist/lib/graph/graph-builder.js +2 -2
- package/dist/lib/graph/impact.js +6 -6
- package/dist/lib/index/batch-processor.js +16 -6
- package/dist/lib/index/chunker.js +2 -0
- package/dist/lib/index/syncer.js +22 -10
- package/dist/lib/index/watcher-batch.js +10 -1
- package/dist/lib/llm/config.js +2 -1
- package/dist/lib/llm/diff.js +56 -11
- package/dist/lib/llm/investigate.js +52 -11
- package/dist/lib/llm/review.js +21 -8
- package/dist/lib/llm/server.js +25 -9
- package/dist/lib/llm/tools.js +1 -1
- package/dist/lib/output/agent-search-formatter.js +25 -3
- package/dist/lib/output/index-state-footer.js +1 -1
- package/dist/lib/review/risk.js +2 -4
- package/dist/lib/search/pagerank.js +1 -1
- package/dist/lib/search/searcher.js +43 -17
- package/dist/lib/search/seed-weight.js +4 -1
- package/dist/lib/skeleton/symbol-extractor.js +2 -1
- package/dist/lib/store/vector-db.js +21 -10
- package/dist/lib/utils/cross-project.js +5 -1
- package/dist/lib/utils/daemon-client.js +39 -1
- package/dist/lib/utils/filter-builder.js +22 -0
- package/dist/lib/utils/git.js +10 -2
- package/dist/lib/utils/project-registry.js +3 -2
- package/dist/lib/utils/scope-filter.js +6 -6
- package/dist/lib/utils/watcher-launcher.js +4 -1
- package/dist/lib/utils/watcher-store.js +2 -1
- package/dist/lib/workers/embeddings/granite.js +4 -1
- package/dist/lib/workers/embeddings/mlx-client.js +7 -2
- package/dist/lib/workers/pool.js +30 -7
- package/dist/lib/workers/process-child.js +1 -1
- package/package.json +23 -19
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/cwd-changed.js +4 -1
- package/plugins/grepmax/hooks/pre-grep.js +2 -1
- package/plugins/grepmax/hooks/start.js +45 -10
- package/plugins/grepmax/hooks/subagent-start.js +1 -4
package/dist/lib/llm/diff.js
CHANGED
|
@@ -11,14 +11,59 @@ const node_child_process_1 = require("node:child_process");
|
|
|
11
11
|
exports.DIFF_MAX_LINES = 500;
|
|
12
12
|
exports.SYMBOL_MAX = 10;
|
|
13
13
|
const KEYWORD_SKIP = new Set([
|
|
14
|
-
"public",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
14
|
+
"public",
|
|
15
|
+
"private",
|
|
16
|
+
"internal",
|
|
17
|
+
"protected",
|
|
18
|
+
"open",
|
|
19
|
+
"final",
|
|
20
|
+
"static",
|
|
21
|
+
"override",
|
|
22
|
+
"class",
|
|
23
|
+
"struct",
|
|
24
|
+
"enum",
|
|
25
|
+
"func",
|
|
26
|
+
"function",
|
|
27
|
+
"def",
|
|
28
|
+
"const",
|
|
29
|
+
"let",
|
|
30
|
+
"var",
|
|
31
|
+
"export",
|
|
32
|
+
"async",
|
|
33
|
+
"await",
|
|
34
|
+
"import",
|
|
35
|
+
"return",
|
|
36
|
+
"if",
|
|
37
|
+
"else",
|
|
38
|
+
"for",
|
|
39
|
+
"while",
|
|
40
|
+
"switch",
|
|
41
|
+
"case",
|
|
42
|
+
"guard",
|
|
43
|
+
"interface",
|
|
44
|
+
"abstract",
|
|
45
|
+
"sealed",
|
|
46
|
+
"data",
|
|
47
|
+
"suspend",
|
|
48
|
+
"inline",
|
|
49
|
+
"typealias",
|
|
50
|
+
"extension",
|
|
51
|
+
"protocol",
|
|
52
|
+
"throws",
|
|
53
|
+
"mutating",
|
|
54
|
+
"nonmutating",
|
|
55
|
+
"convenience",
|
|
56
|
+
"required",
|
|
57
|
+
"weak",
|
|
58
|
+
"unowned",
|
|
59
|
+
"lazy",
|
|
60
|
+
"dynamic",
|
|
61
|
+
"optional",
|
|
62
|
+
"objc",
|
|
63
|
+
"nonisolated",
|
|
64
|
+
"isolated",
|
|
65
|
+
"consuming",
|
|
66
|
+
"borrowing",
|
|
22
67
|
]);
|
|
23
68
|
const DECL_RE = /(?:function|class|struct|enum|interface|func|def)\s+([a-zA-Z_][a-zA-Z0-9_]*)/g;
|
|
24
69
|
const IDENT_RE = /[a-zA-Z_][a-zA-Z0-9_]*/g;
|
|
@@ -122,6 +167,7 @@ function extractSymbols(diff) {
|
|
|
122
167
|
const idents = [];
|
|
123
168
|
let m;
|
|
124
169
|
IDENT_RE.lastIndex = 0;
|
|
170
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex exec loop
|
|
125
171
|
while ((m = IDENT_RE.exec(ctx)) !== null) {
|
|
126
172
|
if (!KEYWORD_SKIP.has(m[0]))
|
|
127
173
|
idents.push(m[0]);
|
|
@@ -134,15 +180,14 @@ function extractSymbols(diff) {
|
|
|
134
180
|
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
135
181
|
DECL_RE.lastIndex = 0;
|
|
136
182
|
let m;
|
|
183
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex exec loop
|
|
137
184
|
while ((m = DECL_RE.exec(line)) !== null) {
|
|
138
185
|
symbols.add(m[1]);
|
|
139
186
|
}
|
|
140
187
|
}
|
|
141
188
|
}
|
|
142
189
|
// Filter short identifiers and cap
|
|
143
|
-
return [...symbols]
|
|
144
|
-
.filter((s) => s.length >= 2)
|
|
145
|
-
.slice(0, exports.SYMBOL_MAX);
|
|
190
|
+
return [...symbols].filter((s) => s.length >= 2).slice(0, exports.SYMBOL_MAX);
|
|
146
191
|
}
|
|
147
192
|
/**
|
|
148
193
|
* Detect languages from file extensions.
|
|
@@ -45,6 +45,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
45
45
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.looksLikeRawToolCall = looksLikeRawToolCall;
|
|
49
|
+
exports.toolCallLeakHint = toolCallLeakHint;
|
|
50
|
+
exports.finalizeAnswer = finalizeAnswer;
|
|
48
51
|
exports.investigate = investigate;
|
|
49
52
|
const path = __importStar(require("node:path"));
|
|
50
53
|
const openai_1 = __importDefault(require("openai"));
|
|
@@ -56,9 +59,38 @@ const config_1 = require("./config");
|
|
|
56
59
|
const prompts_1 = require("./prompts");
|
|
57
60
|
const tools_1 = require("./tools");
|
|
58
61
|
function stripThinkTags(text) {
|
|
59
|
-
return text
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
return text.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/g, "").trim();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Detect raw model tool-call markup leaking into `message.content`.
|
|
66
|
+
*
|
|
67
|
+
* Some local models (notably Qwen3.5-35B-A3B, which emits Qwen-XML tool calls)
|
|
68
|
+
* trip a known llama.cpp `peg-native` parser bug: the call is never extracted
|
|
69
|
+
* into structured `tool_calls` and instead leaks into `content` as raw markup
|
|
70
|
+
* like `<tool_call>…`, `<function=peek>…`, or `<parameter=…>`. Returning that
|
|
71
|
+
* verbatim as the "answer" is what this guards against.
|
|
72
|
+
*/
|
|
73
|
+
function looksLikeRawToolCall(text) {
|
|
74
|
+
return /<tool_call\b|<\/?function[=\s>]|<parameter[=\s>]/i.test(text);
|
|
75
|
+
}
|
|
76
|
+
/** Guidance shown when a non-tool-calling model leaks raw tool-call markup. */
|
|
77
|
+
function toolCallLeakHint(modelName) {
|
|
78
|
+
return (`The investigate tool needs a model that emits structured tool calls, but ` +
|
|
79
|
+
`"${modelName}" returned raw tool-call markup instead. This is a known ` +
|
|
80
|
+
`llama.cpp limitation for Qwen-XML tool-calling models — switch GMAX_LLM_MODEL ` +
|
|
81
|
+
`to a Hermes-JSON tool-calling model (e.g. Qwen3-30B-A3B-Instruct-2507), or ` +
|
|
82
|
+
`use review_commit / the gmax CLI search tools for this question.`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Turn a model message's content into the final answer: detect a tool-call leak
|
|
86
|
+
* (return a clear hint), otherwise strip reasoning tags.
|
|
87
|
+
*/
|
|
88
|
+
function finalizeAnswer(content, modelName) {
|
|
89
|
+
if (content && looksLikeRawToolCall(content)) {
|
|
90
|
+
return toolCallLeakHint(modelName);
|
|
91
|
+
}
|
|
92
|
+
const stripped = content ? stripThinkTags(content) : "";
|
|
93
|
+
return stripped || "(no response)";
|
|
62
94
|
}
|
|
63
95
|
function investigate(opts) {
|
|
64
96
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -83,7 +115,12 @@ function investigate(opts) {
|
|
|
83
115
|
const vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
84
116
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
85
117
|
const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, projectRoot);
|
|
86
|
-
const ctx = {
|
|
118
|
+
const ctx = {
|
|
119
|
+
vectorDb,
|
|
120
|
+
searcher,
|
|
121
|
+
graphBuilder,
|
|
122
|
+
projectRoot,
|
|
123
|
+
};
|
|
87
124
|
const messages = [
|
|
88
125
|
{ role: "system", content: prompts_1.SYSTEM_PROMPT },
|
|
89
126
|
{ role: "user", content: question },
|
|
@@ -113,9 +150,7 @@ function investigate(opts) {
|
|
|
113
150
|
if (verbose) {
|
|
114
151
|
process.stderr.write(`[R${round}] Final answer (${roundMs}ms)\n`);
|
|
115
152
|
}
|
|
116
|
-
const answer = message.content
|
|
117
|
-
? stripThinkTags(message.content)
|
|
118
|
-
: "(no response)";
|
|
153
|
+
const answer = finalizeAnswer(message.content, modelName);
|
|
119
154
|
return {
|
|
120
155
|
answer,
|
|
121
156
|
rounds,
|
|
@@ -160,7 +195,11 @@ function investigate(opts) {
|
|
|
160
195
|
if (verbose) {
|
|
161
196
|
process.stderr.write(` ${fn.name}() — BLOCKED (limit)\n`);
|
|
162
197
|
}
|
|
163
|
-
messages.push({
|
|
198
|
+
messages.push({
|
|
199
|
+
role: "tool",
|
|
200
|
+
tool_call_id: tc.id,
|
|
201
|
+
content: result,
|
|
202
|
+
});
|
|
164
203
|
continue;
|
|
165
204
|
}
|
|
166
205
|
}
|
|
@@ -187,6 +226,10 @@ function investigate(opts) {
|
|
|
187
226
|
process.stderr.write(`[R${maxRounds}] Round cap reached, forcing final answer\n`);
|
|
188
227
|
}
|
|
189
228
|
messages.push({ role: "user", content: prompts_1.FORCE_FINAL_MESSAGE });
|
|
229
|
+
// No tools on the synthesis call. Note: tool_choice:"none" does NOT stop this
|
|
230
|
+
// model from emitting a stray tool call here (verified 2026-06-28 — it still
|
|
231
|
+
// leaks raw markup into content), so the finalizeAnswer guard is what protects
|
|
232
|
+
// this path rather than a request-shape tweak.
|
|
190
233
|
const response = yield client.chat.completions.create({
|
|
191
234
|
model: modelName,
|
|
192
235
|
messages,
|
|
@@ -195,9 +238,7 @@ function investigate(opts) {
|
|
|
195
238
|
if (!((_c = response.choices) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
196
239
|
throw new Error("LLM returned empty response");
|
|
197
240
|
}
|
|
198
|
-
const answer = response.choices[0].message.content
|
|
199
|
-
? stripThinkTags(response.choices[0].message.content)
|
|
200
|
-
: "(no response)";
|
|
241
|
+
const answer = finalizeAnswer(response.choices[0].message.content, modelName);
|
|
201
242
|
return {
|
|
202
243
|
answer,
|
|
203
244
|
rounds: rounds + 1,
|
package/dist/lib/llm/review.js
CHANGED
|
@@ -49,6 +49,7 @@ exports.reviewCommit = reviewCommit;
|
|
|
49
49
|
const path = __importStar(require("node:path"));
|
|
50
50
|
const openai_1 = __importDefault(require("openai"));
|
|
51
51
|
const graph_builder_1 = require("../graph/graph-builder");
|
|
52
|
+
const risk_1 = require("../review/risk");
|
|
52
53
|
const searcher_1 = require("../search/searcher");
|
|
53
54
|
const vector_db_1 = require("../store/vector-db");
|
|
54
55
|
const project_root_1 = require("../utils/project-root");
|
|
@@ -56,7 +57,6 @@ const config_1 = require("./config");
|
|
|
56
57
|
const diff_1 = require("./diff");
|
|
57
58
|
const report_1 = require("./report");
|
|
58
59
|
const tools_1 = require("./tools");
|
|
59
|
-
const risk_1 = require("../review/risk");
|
|
60
60
|
function reviewCommit(opts) {
|
|
61
61
|
return __awaiter(this, void 0, void 0, function* () {
|
|
62
62
|
var _a, _b, _c, _d;
|
|
@@ -88,13 +88,21 @@ function reviewCommit(opts) {
|
|
|
88
88
|
try {
|
|
89
89
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
90
90
|
const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, projectRoot);
|
|
91
|
-
const ctx = {
|
|
91
|
+
const ctx = {
|
|
92
|
+
vectorDb,
|
|
93
|
+
searcher,
|
|
94
|
+
graphBuilder,
|
|
95
|
+
projectRoot,
|
|
96
|
+
};
|
|
92
97
|
// Deterministic risk ranking (Phase 8) — gives the LLM an explicit
|
|
93
98
|
// blast-radius × tests × churn ordering to anchor its judgement, rather
|
|
94
99
|
// than inferring importance from prose alone.
|
|
95
100
|
const [context, riskInputs] = yield Promise.all([
|
|
96
101
|
gatherContext(symbols, changedFiles, ctx, verbose),
|
|
97
|
-
(0, risk_1.gatherRiskInputs)(commitRef, projectRoot, {
|
|
102
|
+
(0, risk_1.gatherRiskInputs)(commitRef, projectRoot, {
|
|
103
|
+
vectorDb,
|
|
104
|
+
graphBuilder,
|
|
105
|
+
}).catch(() => []),
|
|
98
106
|
]);
|
|
99
107
|
contextStr = context;
|
|
100
108
|
const riskRows = (0, risk_1.computeRiskTable)(riskInputs);
|
|
@@ -202,7 +210,11 @@ function gatherContext(symbols, changedFiles, ctx, verbose) {
|
|
|
202
210
|
if (s.status !== "fulfilled")
|
|
203
211
|
continue;
|
|
204
212
|
const { type, result } = s.value;
|
|
205
|
-
if (result.startsWith("(") &&
|
|
213
|
+
if (result.startsWith("(") &&
|
|
214
|
+
(result.includes("not found") ||
|
|
215
|
+
result.includes("error") ||
|
|
216
|
+
result.includes("not indexed") ||
|
|
217
|
+
result.includes("none")))
|
|
206
218
|
continue;
|
|
207
219
|
if (type === "peek")
|
|
208
220
|
peekResults.push(result);
|
|
@@ -356,12 +368,13 @@ ${diff}
|
|
|
356
368
|
// Response parsing
|
|
357
369
|
// ---------------------------------------------------------------------------
|
|
358
370
|
function stripThinkTags(text) {
|
|
359
|
-
return text
|
|
360
|
-
.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/g, "")
|
|
361
|
-
.trim();
|
|
371
|
+
return text.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/g, "").trim();
|
|
362
372
|
}
|
|
363
373
|
function parseFindings(content) {
|
|
364
|
-
const empty = {
|
|
374
|
+
const empty = {
|
|
375
|
+
findings: [],
|
|
376
|
+
summary: "Parse error — could not extract findings from model output.",
|
|
377
|
+
};
|
|
365
378
|
if (!content)
|
|
366
379
|
return empty;
|
|
367
380
|
// Strip think tags and markdown fences
|
package/dist/lib/llm/server.js
CHANGED
|
@@ -85,7 +85,9 @@ class LlmServer {
|
|
|
85
85
|
const runningModel = (_b = (_a = body === null || body === void 0 ? void 0 : body.data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.id;
|
|
86
86
|
if (runningModel) {
|
|
87
87
|
const configBasename = path.basename(this.config.model);
|
|
88
|
-
if (runningModel !== configBasename &&
|
|
88
|
+
if (runningModel !== configBasename &&
|
|
89
|
+
!configBasename.includes(runningModel) &&
|
|
90
|
+
!runningModel.includes(configBasename)) {
|
|
89
91
|
console.log(`[llm] Model mismatch: running "${runningModel}" but config expects "${configBasename}" — will restart`);
|
|
90
92
|
resolve(false);
|
|
91
93
|
return;
|
|
@@ -141,13 +143,25 @@ class LlmServer {
|
|
|
141
143
|
throw new Error(`Model file not found: "${this.config.model}". Set GMAX_LLM_MODEL to a valid .gguf path`);
|
|
142
144
|
}
|
|
143
145
|
const logFd = (0, log_rotate_1.openRotatedLog)(config_1.PATHS.llmLogFile);
|
|
144
|
-
const
|
|
145
|
-
"-m",
|
|
146
|
-
|
|
147
|
-
"--
|
|
148
|
-
|
|
149
|
-
"--
|
|
150
|
-
|
|
146
|
+
const args = [
|
|
147
|
+
"-m",
|
|
148
|
+
this.config.model,
|
|
149
|
+
"--host",
|
|
150
|
+
this.config.host,
|
|
151
|
+
"--port",
|
|
152
|
+
String(this.config.port),
|
|
153
|
+
"-ngl",
|
|
154
|
+
String(this.config.ngl),
|
|
155
|
+
"--ctx-size",
|
|
156
|
+
String(this.config.ctxSize),
|
|
157
|
+
];
|
|
158
|
+
if (this.config.reasoningFormat) {
|
|
159
|
+
args.push("--reasoning-format", this.config.reasoningFormat);
|
|
160
|
+
}
|
|
161
|
+
const child = (0, node_child_process_1.spawn)(binary, args, {
|
|
162
|
+
detached: true,
|
|
163
|
+
stdio: ["ignore", logFd, logFd],
|
|
164
|
+
});
|
|
151
165
|
child.unref();
|
|
152
166
|
fs.closeSync(logFd);
|
|
153
167
|
const pid = child.pid;
|
|
@@ -256,7 +270,9 @@ class LlmServer {
|
|
|
256
270
|
pid: alive ? pid : null,
|
|
257
271
|
port: this.config.port,
|
|
258
272
|
model: this.config.model,
|
|
259
|
-
uptime: alive && this.startTime
|
|
273
|
+
uptime: alive && this.startTime
|
|
274
|
+
? Math.floor((Date.now() - this.startTime) / 1000)
|
|
275
|
+
: 0,
|
|
260
276
|
};
|
|
261
277
|
}
|
|
262
278
|
startIdleWatchdog() {
|
package/dist/lib/llm/tools.js
CHANGED
|
@@ -244,7 +244,7 @@ function executePeek(args, ctx) {
|
|
|
244
244
|
const metaRows = yield table
|
|
245
245
|
.query()
|
|
246
246
|
.select(["is_exported", "start_line", "end_line"])
|
|
247
|
-
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND
|
|
247
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND ${(0, filter_builder_1.pathStartsWith)(prefix)}`)
|
|
248
248
|
.limit(1)
|
|
249
249
|
.toArray();
|
|
250
250
|
const exported = metaRows.length > 0 && Boolean(metaRows[0].is_exported);
|
|
@@ -102,9 +102,31 @@ function firstSignatureLine(chunk) {
|
|
|
102
102
|
return "";
|
|
103
103
|
}
|
|
104
104
|
const QUERY_STOPWORDS = new Set([
|
|
105
|
-
"the",
|
|
106
|
-
"
|
|
107
|
-
"
|
|
105
|
+
"the",
|
|
106
|
+
"and",
|
|
107
|
+
"for",
|
|
108
|
+
"with",
|
|
109
|
+
"that",
|
|
110
|
+
"this",
|
|
111
|
+
"from",
|
|
112
|
+
"into",
|
|
113
|
+
"where",
|
|
114
|
+
"when",
|
|
115
|
+
"how",
|
|
116
|
+
"what",
|
|
117
|
+
"does",
|
|
118
|
+
"not",
|
|
119
|
+
"are",
|
|
120
|
+
"was",
|
|
121
|
+
"has",
|
|
122
|
+
"have",
|
|
123
|
+
"its",
|
|
124
|
+
"all",
|
|
125
|
+
"can",
|
|
126
|
+
"use",
|
|
127
|
+
"uses",
|
|
128
|
+
"using",
|
|
129
|
+
"code",
|
|
108
130
|
]);
|
|
109
131
|
function extractQueryTerms(query) {
|
|
110
132
|
if (!query)
|
|
@@ -14,7 +14,7 @@ exports.formatIndexStateFooter = formatIndexStateFooter;
|
|
|
14
14
|
* while results may actually be incomplete.
|
|
15
15
|
*/
|
|
16
16
|
function formatIndexStateFooter(state, opts) {
|
|
17
|
-
if (!state ||
|
|
17
|
+
if (!(state === null || state === void 0 ? void 0 : state.indexing))
|
|
18
18
|
return null;
|
|
19
19
|
const count = state.pendingFiles > 0 ? `~${state.pendingFiles} files pending` : null;
|
|
20
20
|
if (opts.agent) {
|
package/dist/lib/review/risk.js
CHANGED
|
@@ -40,9 +40,7 @@ function computeRiskTable(inputs) {
|
|
|
40
40
|
/** Render the ranking — TSV-ish for agents, an aligned table for humans. */
|
|
41
41
|
function formatRiskTable(rows, opts) {
|
|
42
42
|
if (rows.length === 0) {
|
|
43
|
-
return opts.agent
|
|
44
|
-
? "(no changed symbols)"
|
|
45
|
-
: "No changed symbols to rank.";
|
|
43
|
+
return opts.agent ? "(no changed symbols)" : "No changed symbols to rank.";
|
|
46
44
|
}
|
|
47
45
|
if (opts.agent) {
|
|
48
46
|
return rows
|
|
@@ -70,7 +68,7 @@ function gatherRiskInputs(ref, projectRoot, deps) {
|
|
|
70
68
|
if (symbols.length === 0)
|
|
71
69
|
return [];
|
|
72
70
|
const root = projectRoot.endsWith("/") ? projectRoot : `${projectRoot}/`;
|
|
73
|
-
const relativize = (f) =>
|
|
71
|
+
const relativize = (f) => f.startsWith(root) ? f.slice(root.length) : f;
|
|
74
72
|
const inputs = yield Promise.all(symbols.map((symbol) => __awaiter(this, void 0, void 0, function* () {
|
|
75
73
|
var _a, _b;
|
|
76
74
|
const [callers, loc, tests] = yield Promise.all([
|
|
@@ -147,7 +147,7 @@ function buildGraphFromDb(db, pathPrefix) {
|
|
|
147
147
|
const rows = yield table
|
|
148
148
|
.query()
|
|
149
149
|
.select(["defined_symbols", "referenced_symbols"])
|
|
150
|
-
.where(
|
|
150
|
+
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
151
151
|
.toArray();
|
|
152
152
|
const nodes = new Set();
|
|
153
153
|
const edges = new Map();
|
|
@@ -29,7 +29,9 @@ function readSymbolArray(val) {
|
|
|
29
29
|
if (typeof maybe.toArray === "function") {
|
|
30
30
|
try {
|
|
31
31
|
const a = maybe.toArray();
|
|
32
|
-
return Array.isArray(a)
|
|
32
|
+
return Array.isArray(a)
|
|
33
|
+
? a.filter((v) => typeof v === "string")
|
|
34
|
+
: [];
|
|
33
35
|
}
|
|
34
36
|
catch (_a) {
|
|
35
37
|
return [];
|
|
@@ -49,7 +51,7 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
49
51
|
var _a;
|
|
50
52
|
const parts = [];
|
|
51
53
|
if (pathPrefix) {
|
|
52
|
-
parts.push(
|
|
54
|
+
parts.push((0, filter_builder_1.pathStartsWith)((0, filter_builder_1.normalizePath)(pathPrefix)));
|
|
53
55
|
}
|
|
54
56
|
const fileFilter = filters === null || filters === void 0 ? void 0 : filters.file;
|
|
55
57
|
if (typeof fileFilter === "string" && fileFilter) {
|
|
@@ -60,7 +62,7 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
60
62
|
const absExclude = pathPrefix
|
|
61
63
|
? (0, filter_builder_1.normalizePath)(pathPrefix + excludeFilter)
|
|
62
64
|
: excludeFilter;
|
|
63
|
-
parts.push(
|
|
65
|
+
parts.push((0, filter_builder_1.pathNotStartsWith)(absExclude));
|
|
64
66
|
}
|
|
65
67
|
// New array-shape: pre-resolved absolute exclude prefixes from
|
|
66
68
|
// resolveScope (or daemon IPC). Each becomes its own NOT LIKE clause.
|
|
@@ -69,7 +71,7 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
69
71
|
for (const p of excludePrefixes) {
|
|
70
72
|
if (typeof p === "string" && p) {
|
|
71
73
|
const norm = (0, filter_builder_1.normalizePath)(p);
|
|
72
|
-
parts.push(
|
|
74
|
+
parts.push((0, filter_builder_1.pathNotStartsWith)(norm));
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -82,7 +84,7 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
82
84
|
for (const p of inPrefixes) {
|
|
83
85
|
if (typeof p === "string" && p) {
|
|
84
86
|
const norm = (0, filter_builder_1.normalizePath)(p);
|
|
85
|
-
clauses.push(
|
|
87
|
+
clauses.push((0, filter_builder_1.pathStartsWith)(norm));
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
if (clauses.length === 1)
|
|
@@ -104,7 +106,7 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
104
106
|
const roots = projectRoots.split(",");
|
|
105
107
|
const clauses = roots.map((r) => {
|
|
106
108
|
const prefix = r.endsWith("/") ? r : `${r}/`;
|
|
107
|
-
return
|
|
109
|
+
return (0, filter_builder_1.pathStartsWith)(prefix);
|
|
108
110
|
});
|
|
109
111
|
parts.push(`(${clauses.join(" OR ")})`);
|
|
110
112
|
}
|
|
@@ -112,7 +114,7 @@ function buildWhereClause(pathPrefix, filters, searchIntent) {
|
|
|
112
114
|
if (typeof excludeRoots === "string" && excludeRoots) {
|
|
113
115
|
for (const r of excludeRoots.split(",")) {
|
|
114
116
|
const prefix = r.endsWith("/") ? r : `${r}/`;
|
|
115
|
-
parts.push(
|
|
117
|
+
parts.push((0, filter_builder_1.pathNotStartsWith)(prefix));
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
const defFilter = filters === null || filters === void 0 ? void 0 : filters.def;
|
|
@@ -441,9 +443,21 @@ class Searcher {
|
|
|
441
443
|
// loaded), so pull it into the lightweight path when symbols were seeded.
|
|
442
444
|
const needDefinedSymbols = pagerankEnabled || symbolQuery !== null || seedCtx.symbols.size > 0;
|
|
443
445
|
const LIGHTWEIGHT_COLUMNS = [
|
|
444
|
-
"id",
|
|
445
|
-
"
|
|
446
|
-
"
|
|
446
|
+
"id",
|
|
447
|
+
"path",
|
|
448
|
+
"hash",
|
|
449
|
+
"chunk_index",
|
|
450
|
+
"start_line",
|
|
451
|
+
"end_line",
|
|
452
|
+
"is_anchor",
|
|
453
|
+
"chunk_type",
|
|
454
|
+
"role",
|
|
455
|
+
"complexity",
|
|
456
|
+
"is_exported",
|
|
457
|
+
"content",
|
|
458
|
+
"parent_symbol",
|
|
459
|
+
"referenced_symbols",
|
|
460
|
+
"pooled_colbert_48d",
|
|
447
461
|
...(needDefinedSymbols ? ["defined_symbols"] : []),
|
|
448
462
|
];
|
|
449
463
|
// _distance is auto-added by vectorSearch, _score by FTS — include each
|
|
@@ -457,7 +471,7 @@ class Searcher {
|
|
|
457
471
|
if (whereClause) {
|
|
458
472
|
vectorQuery = vectorQuery.where(whereClause);
|
|
459
473
|
}
|
|
460
|
-
|
|
474
|
+
const vectorResults = (yield vectorQuery.toArray()).map((r) => (Object.assign({}, r)));
|
|
461
475
|
let ftsResults = [];
|
|
462
476
|
let ftsSearchFailed = false;
|
|
463
477
|
if (this.ftsAvailable) {
|
|
@@ -566,7 +580,7 @@ class Searcher {
|
|
|
566
580
|
var _a, _b;
|
|
567
581
|
const ka = a.id || `${a.path}:${a.chunk_index}`;
|
|
568
582
|
const kb = b.id || `${b.path}:${b.chunk_index}`;
|
|
569
|
-
return ((_a = candidateScores.get(kb)) !== null && _a !== void 0 ? _a : 0) - ((_b = candidateScores.get(ka)) !== null && _b !== void 0 ? _b : 0);
|
|
583
|
+
return (((_a = candidateScores.get(kb)) !== null && _a !== void 0 ? _a : 0) - ((_b = candidateScores.get(ka)) !== null && _b !== void 0 ? _b : 0));
|
|
570
584
|
});
|
|
571
585
|
}
|
|
572
586
|
}
|
|
@@ -751,7 +765,12 @@ class Searcher {
|
|
|
751
765
|
record: doc,
|
|
752
766
|
score: boosted,
|
|
753
767
|
breakdown: explain
|
|
754
|
-
? {
|
|
768
|
+
? {
|
|
769
|
+
rerank: base,
|
|
770
|
+
fused: fusedScore,
|
|
771
|
+
boost: blended > 0 ? boosted / blended : 1,
|
|
772
|
+
normalized: 0,
|
|
773
|
+
}
|
|
755
774
|
: undefined,
|
|
756
775
|
};
|
|
757
776
|
});
|
|
@@ -766,12 +785,14 @@ class Searcher {
|
|
|
766
785
|
const envWeight = Number.parseFloat((_o = process.env.GMAX_PR_WEIGHT) !== null && _o !== void 0 ? _o : "");
|
|
767
786
|
const PR_WEIGHT = Number.isFinite(envWeight) && envWeight >= 0 ? envWeight : 0.05;
|
|
768
787
|
for (const item of scored) {
|
|
769
|
-
const raw = item.record
|
|
788
|
+
const raw = item.record
|
|
789
|
+
.defined_symbols;
|
|
770
790
|
let defs = [];
|
|
771
791
|
if (Array.isArray(raw)) {
|
|
772
792
|
defs = raw.filter((v) => typeof v === "string");
|
|
773
793
|
}
|
|
774
|
-
else if (raw &&
|
|
794
|
+
else if (raw &&
|
|
795
|
+
typeof raw.toArray === "function") {
|
|
775
796
|
try {
|
|
776
797
|
const arr = raw.toArray();
|
|
777
798
|
if (Array.isArray(arr)) {
|
|
@@ -817,8 +838,13 @@ class Searcher {
|
|
|
817
838
|
const displayRows = yield table
|
|
818
839
|
.query()
|
|
819
840
|
.select([
|
|
820
|
-
"id",
|
|
821
|
-
"
|
|
841
|
+
"id",
|
|
842
|
+
"defined_symbols",
|
|
843
|
+
"parent_symbol",
|
|
844
|
+
"imports",
|
|
845
|
+
"exports",
|
|
846
|
+
"summary",
|
|
847
|
+
"file_skeleton",
|
|
822
848
|
])
|
|
823
849
|
.where(`id IN (${finalIds.join(",")})`)
|
|
824
850
|
.limit(finalIds.length)
|
|
@@ -64,7 +64,10 @@ function seedParamsFromEnv(env = process.env) {
|
|
|
64
64
|
function buildSeedContext(spec) {
|
|
65
65
|
var _a, _b;
|
|
66
66
|
const fileSuffixes = ((_a = spec === null || spec === void 0 ? void 0 : spec.files) !== null && _a !== void 0 ? _a : [])
|
|
67
|
-
.map((f) => f
|
|
67
|
+
.map((f) => f
|
|
68
|
+
.trim()
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/^\.?\//, ""))
|
|
68
71
|
.filter((f) => f.length > 0);
|
|
69
72
|
const symbols = new Set(((_b = spec === null || spec === void 0 ? void 0 : spec.symbols) !== null && _b !== void 0 ? _b : []).map((s) => s.trim()).filter((s) => s.length > 0));
|
|
70
73
|
return {
|
|
@@ -13,7 +13,8 @@ function extractSymbolsFromSkeleton(annotatedSkeleton) {
|
|
|
13
13
|
const line = Number.parseInt(m[1], 10);
|
|
14
14
|
const sig = m[2].trim();
|
|
15
15
|
const exported = sig.includes("export ");
|
|
16
|
-
const type = ((_a = sig.match(/\b(class|interface|type|function|def|fn|func)\b/)) === null || _a === void 0 ? void 0 : _a[1]) ||
|
|
16
|
+
const type = ((_a = sig.match(/\b(class|interface|type|function|def|fn|func)\b/)) === null || _a === void 0 ? void 0 : _a[1]) ||
|
|
17
|
+
"other";
|
|
17
18
|
const name = ((_b = sig.match(/(?:function|class|interface|type|def|fn|func)\s+(\w+)/)) === null || _b === void 0 ? void 0 : _b[1]) ||
|
|
18
19
|
((_c = sig.match(/^(?:async\s+)?(\w+)\s*[(<]/)) === null || _c === void 0 ? void 0 : _c[1]) ||
|
|
19
20
|
"unknown";
|