grepmax 0.23.0 → 0.24.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 +1 -1
- package/README.md +14 -0
- package/dist/commands/impact.js +33 -1
- package/dist/commands/mcp.js +234 -37
- package/dist/commands/surprises.js +150 -0
- package/dist/eval-surprising-connections.js +191 -0
- package/dist/index.js +2 -0
- package/dist/lib/analysis/surprising-connections.js +600 -0
- package/dist/lib/graph/impact-rollup.js +202 -0
- package/dist/lib/graph/impact.js +15 -1
- package/dist/lib/help/agent-cheatsheet.js +1 -0
- package/dist/lib/skeleton/skeletonizer.js +118 -0
- package/dist/lib/skeleton/summary-formatter.js +8 -1
- package/dist/lib/workers/pool.js +4 -1
- package/package.json +3 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.relativeToProject = relativeToProject;
|
|
37
|
+
exports.impactPackageBucket = impactPackageBucket;
|
|
38
|
+
exports.buildImpactRollup = buildImpactRollup;
|
|
39
|
+
exports.formatImpactRollupHuman = formatImpactRollupHuman;
|
|
40
|
+
exports.formatImpactRollupAgent = formatImpactRollupAgent;
|
|
41
|
+
const path = __importStar(require("node:path"));
|
|
42
|
+
const impact_1 = require("./impact");
|
|
43
|
+
const test_hits_1 = require("./test-hits");
|
|
44
|
+
function relativeToProject(projectRoot, filePath) {
|
|
45
|
+
return filePath.startsWith(`${projectRoot}/`)
|
|
46
|
+
? filePath.slice(projectRoot.length + 1)
|
|
47
|
+
: filePath;
|
|
48
|
+
}
|
|
49
|
+
function impactPackageBucket(projectRoot, filePath) {
|
|
50
|
+
const rel = relativeToProject(projectRoot, filePath).replace(/\\/g, "/");
|
|
51
|
+
const parts = rel.split("/").filter(Boolean);
|
|
52
|
+
if (parts[0] === "packages" && parts[1])
|
|
53
|
+
return `packages/${parts[1]}`;
|
|
54
|
+
if (parts[0])
|
|
55
|
+
return parts[0];
|
|
56
|
+
return path.basename(filePath) || ".";
|
|
57
|
+
}
|
|
58
|
+
function sortDetailedDependents(dependents) {
|
|
59
|
+
return [...dependents].sort((a, b) => b.sharedSymbols - a.sharedSymbols || a.file.localeCompare(b.file));
|
|
60
|
+
}
|
|
61
|
+
function buildImpactRollup(opts) {
|
|
62
|
+
var _a, _b, _c;
|
|
63
|
+
const top = Math.max(1, (_a = opts.top) !== null && _a !== void 0 ? _a : 10);
|
|
64
|
+
const productionDependents = sortDetailedDependents(opts.dependents.filter((d) => !(0, impact_1.isTestPath)(d.file)));
|
|
65
|
+
const tests = (0, test_hits_1.groupTestHitsByFile)((_b = opts.tests) !== null && _b !== void 0 ? _b : []);
|
|
66
|
+
const exports = opts.targetSymbols
|
|
67
|
+
.map((symbol) => {
|
|
68
|
+
const matches = productionDependents.filter((d) => d.symbols.includes(symbol));
|
|
69
|
+
return {
|
|
70
|
+
symbol,
|
|
71
|
+
dependentCount: matches.length,
|
|
72
|
+
topDependents: matches.slice(0, top),
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
.sort((a, b) => b.dependentCount - a.dependentCount || a.symbol.localeCompare(b.symbol));
|
|
76
|
+
const byPackage = new Map();
|
|
77
|
+
for (const dep of productionDependents) {
|
|
78
|
+
const bucket = impactPackageBucket(opts.projectRoot, dep.file);
|
|
79
|
+
const entry = (_c = byPackage.get(bucket)) !== null && _c !== void 0 ? _c : {
|
|
80
|
+
dependents: [],
|
|
81
|
+
symbols: new Set(),
|
|
82
|
+
};
|
|
83
|
+
entry.dependents.push(dep);
|
|
84
|
+
for (const sym of dep.symbols)
|
|
85
|
+
entry.symbols.add(sym);
|
|
86
|
+
byPackage.set(bucket, entry);
|
|
87
|
+
}
|
|
88
|
+
const packages = [...byPackage.entries()]
|
|
89
|
+
.map(([name, entry]) => ({
|
|
90
|
+
name,
|
|
91
|
+
dependentCount: entry.dependents.length,
|
|
92
|
+
symbols: [...entry.symbols].sort(),
|
|
93
|
+
topDependents: sortDetailedDependents(entry.dependents).slice(0, top),
|
|
94
|
+
}))
|
|
95
|
+
.sort((a, b) => b.dependentCount - a.dependentCount || a.name.localeCompare(b.name));
|
|
96
|
+
return {
|
|
97
|
+
targetSymbols: [...opts.targetSymbols],
|
|
98
|
+
productionDependents,
|
|
99
|
+
packages,
|
|
100
|
+
exports,
|
|
101
|
+
tests,
|
|
102
|
+
topDependents: productionDependents.slice(0, top),
|
|
103
|
+
topTests: tests.slice(0, top),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function symbolsLabel(symbols) {
|
|
107
|
+
if (symbols.length <= 3)
|
|
108
|
+
return symbols.join(",");
|
|
109
|
+
return `${symbols.slice(0, 3).join(",")}(+${symbols.length - 3})`;
|
|
110
|
+
}
|
|
111
|
+
function formatImpactRollupHuman(rollup, opts) {
|
|
112
|
+
const lines = [`Impact rollup for ${opts.target}:`, ""];
|
|
113
|
+
lines.push("Summary:");
|
|
114
|
+
lines.push(` Exports: ${rollup.targetSymbols.length}`);
|
|
115
|
+
lines.push(` Production dependents: ${rollup.productionDependents.length}`);
|
|
116
|
+
lines.push(` Packages: ${rollup.packages.length}`);
|
|
117
|
+
if (opts.includeTests)
|
|
118
|
+
lines.push(` Affected tests: ${rollup.tests.length}`);
|
|
119
|
+
lines.push("", `Exports (${rollup.exports.length}):`);
|
|
120
|
+
for (const ex of rollup.exports) {
|
|
121
|
+
lines.push(` ${ex.symbol} ${ex.dependentCount} dependent${ex.dependentCount === 1 ? "" : "s"}`);
|
|
122
|
+
for (const dep of ex.topDependents) {
|
|
123
|
+
lines.push(` ${relativeToProject(opts.projectRoot, dep.file)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
lines.push("", `Packages (${rollup.packages.length}):`);
|
|
127
|
+
if (rollup.packages.length === 0) {
|
|
128
|
+
lines.push(" none");
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
for (const pkg of rollup.packages) {
|
|
132
|
+
lines.push(` ${pkg.name} ${pkg.dependentCount} file${pkg.dependentCount === 1 ? "" : "s"} symbols:${symbolsLabel(pkg.symbols)}`);
|
|
133
|
+
for (const dep of pkg.topDependents) {
|
|
134
|
+
lines.push(` ${relativeToProject(opts.projectRoot, dep.file)} (${symbolsLabel(dep.symbols)})`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
lines.push("", `Top dependents (${rollup.topDependents.length}):`);
|
|
139
|
+
if (rollup.topDependents.length === 0) {
|
|
140
|
+
lines.push(" none");
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
for (const dep of rollup.topDependents) {
|
|
144
|
+
lines.push(` ${relativeToProject(opts.projectRoot, dep.file)} (${symbolsLabel(dep.symbols)})`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (opts.includeTests) {
|
|
148
|
+
lines.push("", `Affected tests (${rollup.tests.length}):`);
|
|
149
|
+
if (rollup.topTests.length === 0) {
|
|
150
|
+
lines.push(" none");
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
for (const test of rollup.topTests) {
|
|
154
|
+
lines.push(` ${relativeToProject(opts.projectRoot, test.file)}:${test.line + 1} (${(0, test_hits_1.hopLabelHuman)(test.hops)}${(0, test_hits_1.formatViaHuman)(test.via)})`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return lines.join("\n");
|
|
159
|
+
}
|
|
160
|
+
function formatImpactRollupAgent(rollup, opts) {
|
|
161
|
+
const lines = [
|
|
162
|
+
[
|
|
163
|
+
"summary",
|
|
164
|
+
`target=${opts.target}`,
|
|
165
|
+
`exports=${rollup.targetSymbols.length}`,
|
|
166
|
+
`deps=${rollup.productionDependents.length}`,
|
|
167
|
+
`packages=${rollup.packages.length}`,
|
|
168
|
+
`tests=${opts.includeTests ? rollup.tests.length : "skipped"}`,
|
|
169
|
+
].join("\t"),
|
|
170
|
+
];
|
|
171
|
+
for (const ex of rollup.exports) {
|
|
172
|
+
lines.push(["export", ex.symbol, `deps=${ex.dependentCount}`].join("\t"));
|
|
173
|
+
}
|
|
174
|
+
for (const pkg of rollup.packages) {
|
|
175
|
+
lines.push([
|
|
176
|
+
"pkg",
|
|
177
|
+
pkg.name,
|
|
178
|
+
`deps=${pkg.dependentCount}`,
|
|
179
|
+
`symbols=${symbolsLabel(pkg.symbols)}`,
|
|
180
|
+
].join("\t"));
|
|
181
|
+
}
|
|
182
|
+
for (const dep of rollup.topDependents) {
|
|
183
|
+
lines.push([
|
|
184
|
+
"dep",
|
|
185
|
+
relativeToProject(opts.projectRoot, dep.file),
|
|
186
|
+
`symbols=${symbolsLabel(dep.symbols)}`,
|
|
187
|
+
].join("\t"));
|
|
188
|
+
}
|
|
189
|
+
if (opts.includeTests) {
|
|
190
|
+
for (const test of rollup.topTests) {
|
|
191
|
+
lines.push([
|
|
192
|
+
"test",
|
|
193
|
+
`${relativeToProject(opts.projectRoot, test.file)}:${test.line + 1}`,
|
|
194
|
+
(0, test_hits_1.hopLabelAgent)(test.hops),
|
|
195
|
+
(0, test_hits_1.formatViaAgent)(test.via).trim(),
|
|
196
|
+
]
|
|
197
|
+
.filter(Boolean)
|
|
198
|
+
.join("\t"));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return lines.join("\n");
|
|
202
|
+
}
|
package/dist/lib/graph/impact.js
CHANGED
|
@@ -13,6 +13,7 @@ exports.isTestPath = isTestPath;
|
|
|
13
13
|
exports.resolveTargetSymbols = resolveTargetSymbols;
|
|
14
14
|
exports.findTests = findTests;
|
|
15
15
|
exports.findDependents = findDependents;
|
|
16
|
+
exports.findDependentsDetailed = findDependentsDetailed;
|
|
16
17
|
const languages_1 = require("../core/languages");
|
|
17
18
|
const filter_builder_1 = require("../utils/filter-builder");
|
|
18
19
|
const query_timeout_1 = require("../utils/query-timeout");
|
|
@@ -244,6 +245,15 @@ function walkCallers(symbol, graphBuilder, testHits, currentHop, maxDepth, visit
|
|
|
244
245
|
* Returns files sorted by number of shared symbols (descending).
|
|
245
246
|
*/
|
|
246
247
|
function findDependents(symbols_1, vectorDb_1, projectRoot_1, excludePaths_1) {
|
|
248
|
+
return __awaiter(this, arguments, void 0, function* (symbols, vectorDb, projectRoot, excludePaths, limit = 10, excludePrefixes, symbolFamilies) {
|
|
249
|
+
return (yield findDependentsDetailed(symbols, vectorDb, projectRoot, excludePaths, limit, excludePrefixes, symbolFamilies)).map(({ file, sharedSymbols }) => ({ file, sharedSymbols }));
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Find dependent files and retain which target symbols each file matched.
|
|
254
|
+
* The legacy `findDependents` projects this down to `{file, sharedSymbols}`.
|
|
255
|
+
*/
|
|
256
|
+
function findDependentsDetailed(symbols_1, vectorDb_1, projectRoot_1, excludePaths_1) {
|
|
247
257
|
return __awaiter(this, arguments, void 0, function* (symbols, vectorDb, projectRoot, excludePaths, limit = 10, excludePrefixes, symbolFamilies) {
|
|
248
258
|
var _a, _b;
|
|
249
259
|
const table = yield vectorDb.ensureTable();
|
|
@@ -279,6 +289,10 @@ function findDependents(symbols_1, vectorDb_1, projectRoot_1, excludePaths_1) {
|
|
|
279
289
|
return Array.from(symbolsByFile.entries())
|
|
280
290
|
.sort((a, b) => b[1].size - a[1].size)
|
|
281
291
|
.slice(0, limit)
|
|
282
|
-
.map(([file, symbols]) => ({
|
|
292
|
+
.map(([file, symbols]) => ({
|
|
293
|
+
file,
|
|
294
|
+
sharedSymbols: symbols.size,
|
|
295
|
+
symbols: [...symbols].sort(),
|
|
296
|
+
}));
|
|
283
297
|
});
|
|
284
298
|
}
|
|
@@ -31,6 +31,7 @@ Understand:
|
|
|
31
31
|
Survey:
|
|
32
32
|
gmax project codebase overview (langs, structure, key symbols)
|
|
33
33
|
gmax skeleton <file> file structure (file path, NOT a directory)
|
|
34
|
+
gmax surprises --experimental similar but graph-disconnected file pairs
|
|
34
35
|
gmax context "topic-or-path" --budget 4000 topic summary or deterministic file/dir context
|
|
35
36
|
gmax log <path-or-symbol> git commits (replaces recent/diff)
|
|
36
37
|
gmax status indexed projects
|
|
@@ -69,6 +69,8 @@ const DEFAULT_OPTIONS = {
|
|
|
69
69
|
includeSummary: true,
|
|
70
70
|
maxCallsInSummary: 4,
|
|
71
71
|
};
|
|
72
|
+
const SQL_TEMPLATE_LANGS = new Set(["typescript", "tsx", "javascript"]);
|
|
73
|
+
const SQL_IDENTIFIER_PATTERN = '(?:"[^"]+"|\\[[^\\]]+\\]|`[^`]+`|[A-Za-z_][\\w$]*)(?:\\s*\\.\\s*(?:"[^"]+"|\\[[^\\]]+\\]|`[^`]+`|[A-Za-z_][\\w$]*)){0,2}';
|
|
72
74
|
// WASM locator (same as chunker.ts)
|
|
73
75
|
function resolveTreeSitterWasmLocator() {
|
|
74
76
|
try {
|
|
@@ -238,10 +240,12 @@ class Skeletonizer {
|
|
|
238
240
|
}
|
|
239
241
|
// Extract metadata from the body
|
|
240
242
|
const referencedSymbols = this.extractReferencedSymbols(bodyNode);
|
|
243
|
+
const sqlTemplates = this.extractSqlTemplateSummaries(bodyNode, langId);
|
|
241
244
|
const complexity = this.calculateComplexity(bodyNode);
|
|
242
245
|
const role = this.classifyRole(complexity, referencedSymbols.length);
|
|
243
246
|
const metadata = {
|
|
244
247
|
referencedSymbols,
|
|
248
|
+
sqlTemplates,
|
|
245
249
|
complexity,
|
|
246
250
|
role,
|
|
247
251
|
};
|
|
@@ -450,6 +454,120 @@ class Skeletonizer {
|
|
|
450
454
|
extract(node);
|
|
451
455
|
return refs;
|
|
452
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* Surface SQL tagged templates hidden inside elided JS/TS function bodies.
|
|
459
|
+
* This is intentionally a lightweight skeleton hint, not a SQL parser.
|
|
460
|
+
*/
|
|
461
|
+
extractSqlTemplateSummaries(node, langId) {
|
|
462
|
+
if (!SQL_TEMPLATE_LANGS.has(langId))
|
|
463
|
+
return [];
|
|
464
|
+
const summaries = [];
|
|
465
|
+
const seen = new Set();
|
|
466
|
+
const extract = (n) => {
|
|
467
|
+
const template = (n.namedChildren || []).find((child) => child.type === "template_string");
|
|
468
|
+
if (template && this.isSqlTaggedTemplate(n, template)) {
|
|
469
|
+
const summary = this.summarizeSqlTemplate(template);
|
|
470
|
+
if (summary && !seen.has(summary)) {
|
|
471
|
+
seen.add(summary);
|
|
472
|
+
summaries.push(summary);
|
|
473
|
+
}
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
for (const child of n.namedChildren || []) {
|
|
477
|
+
extract(child);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
extract(node);
|
|
481
|
+
return summaries;
|
|
482
|
+
}
|
|
483
|
+
isSqlTaggedTemplate(node, template) {
|
|
484
|
+
const tagText = node.text
|
|
485
|
+
.slice(0, template.startIndex - node.startIndex)
|
|
486
|
+
.trim();
|
|
487
|
+
return /(?:^|[.\s])sql(?:$|[.<(])/.test(tagText);
|
|
488
|
+
}
|
|
489
|
+
summarizeSqlTemplate(template) {
|
|
490
|
+
var _a;
|
|
491
|
+
const sqlText = this.extractTemplateText(template)
|
|
492
|
+
.replace(/--[^\n]*/g, " ")
|
|
493
|
+
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
|
494
|
+
.replace(/\s+/g, " ")
|
|
495
|
+
.trim();
|
|
496
|
+
if (!sqlText)
|
|
497
|
+
return null;
|
|
498
|
+
const op = (_a = sqlText.match(/\b(select|insert|update|delete|merge|create|alter|drop|truncate)\b/i)) === null || _a === void 0 ? void 0 : _a[1];
|
|
499
|
+
if (!op)
|
|
500
|
+
return "SQL";
|
|
501
|
+
const operation = op.toUpperCase();
|
|
502
|
+
const tables = this.extractSqlTables(sqlText, operation);
|
|
503
|
+
if (tables.length === 0)
|
|
504
|
+
return operation;
|
|
505
|
+
const tableList = tables.slice(0, 2);
|
|
506
|
+
if (tables.length > tableList.length)
|
|
507
|
+
tableList.push("...");
|
|
508
|
+
return `${operation} ${tableList.join(", ")}`;
|
|
509
|
+
}
|
|
510
|
+
extractTemplateText(template) {
|
|
511
|
+
const parts = [];
|
|
512
|
+
for (const child of template.namedChildren || []) {
|
|
513
|
+
if (child.type === "string_fragment") {
|
|
514
|
+
parts.push(child.text);
|
|
515
|
+
}
|
|
516
|
+
else if (child.type === "template_substitution") {
|
|
517
|
+
parts.push(" ? ");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (parts.length > 0)
|
|
521
|
+
return parts.join("");
|
|
522
|
+
return template.text.slice(1, -1).replace(/\$\{[^}]*\}/g, " ? ");
|
|
523
|
+
}
|
|
524
|
+
extractSqlTables(sqlText, operation) {
|
|
525
|
+
const tables = [];
|
|
526
|
+
const seen = new Set();
|
|
527
|
+
const addMatches = (pattern) => {
|
|
528
|
+
for (const match of sqlText.matchAll(pattern)) {
|
|
529
|
+
const table = this.normalizeSqlIdentifier(match[1]);
|
|
530
|
+
if (table && !seen.has(table)) {
|
|
531
|
+
seen.add(table);
|
|
532
|
+
tables.push(table);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
switch (operation) {
|
|
537
|
+
case "SELECT":
|
|
538
|
+
addMatches(new RegExp(`\\b(?:from|join)\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
539
|
+
break;
|
|
540
|
+
case "INSERT":
|
|
541
|
+
addMatches(new RegExp(`\\binsert\\s+into\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
542
|
+
break;
|
|
543
|
+
case "UPDATE":
|
|
544
|
+
addMatches(new RegExp(`\\bupdate\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
545
|
+
break;
|
|
546
|
+
case "DELETE":
|
|
547
|
+
addMatches(new RegExp(`\\bdelete\\s+from\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
548
|
+
break;
|
|
549
|
+
case "MERGE":
|
|
550
|
+
addMatches(new RegExp(`\\bmerge\\s+into\\s+(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
551
|
+
break;
|
|
552
|
+
case "CREATE":
|
|
553
|
+
addMatches(new RegExp(`\\bcreate\\s+(?:or\\s+replace\\s+)?(?:table|view|materialized\\s+view|index)\\s+(?:if\\s+not\\s+exists\\s+)?(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
554
|
+
break;
|
|
555
|
+
case "ALTER":
|
|
556
|
+
case "DROP":
|
|
557
|
+
addMatches(new RegExp(`\\b${operation.toLowerCase()}\\s+(?:table|view|materialized\\s+view|index)\\s+(?:if\\s+(?:exists|not\\s+exists)\\s+)?(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
558
|
+
break;
|
|
559
|
+
case "TRUNCATE":
|
|
560
|
+
addMatches(new RegExp(`\\btruncate\\s+(?:table\\s+)?(${SQL_IDENTIFIER_PATTERN})`, "gi"));
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
return tables;
|
|
564
|
+
}
|
|
565
|
+
normalizeSqlIdentifier(identifier) {
|
|
566
|
+
const normalized = identifier === null || identifier === void 0 ? void 0 : identifier.replace(/\s*\.\s*/g, ".").replace(/["`[\]]/g, "").trim();
|
|
567
|
+
if (!normalized || normalized === "?")
|
|
568
|
+
return null;
|
|
569
|
+
return normalized;
|
|
570
|
+
}
|
|
453
571
|
/**
|
|
454
572
|
* Calculate cyclomatic complexity of a node.
|
|
455
573
|
*/
|
|
@@ -25,7 +25,7 @@ const DEFAULT_OPTIONS = {
|
|
|
25
25
|
* // Output: "// → findByEmail, compare, sign | C:8 | ORCH"
|
|
26
26
|
*/
|
|
27
27
|
function formatSummary(metadata, options = {}) {
|
|
28
|
-
var _a;
|
|
28
|
+
var _a, _b;
|
|
29
29
|
const opts = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
|
|
30
30
|
const parts = [];
|
|
31
31
|
// Calls (referenced symbols)
|
|
@@ -36,6 +36,13 @@ function formatSummary(metadata, options = {}) {
|
|
|
36
36
|
}
|
|
37
37
|
parts.push(`→ ${calls.join(", ")}`);
|
|
38
38
|
}
|
|
39
|
+
if ((_b = metadata.sqlTemplates) === null || _b === void 0 ? void 0 : _b.length) {
|
|
40
|
+
const templates = metadata.sqlTemplates.slice(0, 3);
|
|
41
|
+
if (metadata.sqlTemplates.length > templates.length) {
|
|
42
|
+
templates.push("...");
|
|
43
|
+
}
|
|
44
|
+
parts.push(`SQL: ${templates.join("; ")}`);
|
|
45
|
+
}
|
|
39
46
|
// Complexity (only if > 1, trivial functions don't need it)
|
|
40
47
|
if (opts.showComplexity && metadata.complexity && metadata.complexity > 1) {
|
|
41
48
|
parts.push(`C:${metadata.complexity}`);
|
package/dist/lib/workers/pool.js
CHANGED
|
@@ -160,7 +160,10 @@ function resolveProcessWorker() {
|
|
|
160
160
|
return { filename: jsWorker, execArgv: [] };
|
|
161
161
|
}
|
|
162
162
|
if (fs.existsSync(tsWorker)) {
|
|
163
|
-
return {
|
|
163
|
+
return {
|
|
164
|
+
filename: tsWorker,
|
|
165
|
+
execArgv: ["--import", require.resolve("tsx")],
|
|
166
|
+
};
|
|
164
167
|
}
|
|
165
168
|
throw new Error("Process worker file not found");
|
|
166
169
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"author": "Robert Owens <78518764+reowens@users.noreply.github.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"bench:oss:json": "GMAX_EVAL_JSON=1 npx tsx src/eval-oss.ts all",
|
|
32
32
|
"bench:tokens": "npx tsx src/eval-tokens.ts",
|
|
33
33
|
"bench:tokens:json": "GMAX_EVAL_JSON=1 npx tsx src/eval-tokens.ts",
|
|
34
|
+
"bench:surprises": "npx tsx src/eval-surprising-connections.ts",
|
|
35
|
+
"bench:surprises:json": "GMAX_EVAL_JSON=1 npx tsx src/eval-surprising-connections.ts",
|
|
34
36
|
"format": "biome format --write .",
|
|
35
37
|
"format:check": "biome check .",
|
|
36
38
|
"lint": "biome lint .",
|