grepmax 0.23.0 → 0.25.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 +25 -6
- package/dist/commands/add.js +38 -31
- package/dist/commands/config.js +16 -15
- package/dist/commands/context.js +38 -13
- package/dist/commands/doctor.js +76 -83
- package/dist/commands/extract.js +12 -1
- package/dist/commands/impact.js +33 -1
- package/dist/commands/index.js +23 -24
- package/dist/commands/list.js +23 -14
- package/dist/commands/mcp.js +494 -162
- package/dist/commands/peek.js +16 -5
- package/dist/commands/repair.js +54 -120
- package/dist/commands/search-output.js +30 -9
- package/dist/commands/search-run.js +75 -47
- package/dist/commands/search-skeletons.js +28 -18
- package/dist/commands/search.js +45 -49
- package/dist/commands/serve.js +415 -373
- package/dist/commands/setup.js +2 -2
- package/dist/commands/similar.js +5 -5
- package/dist/commands/skeleton.js +67 -41
- package/dist/commands/status.js +5 -2
- package/dist/commands/summarize.js +6 -0
- package/dist/commands/surprises.js +150 -0
- package/dist/commands/watch.js +102 -38
- package/dist/config.js +3 -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/daemon/daemon.js +1101 -379
- package/dist/lib/daemon/ipc-handler.js +122 -13
- package/dist/lib/daemon/mlx-server-manager.js +268 -125
- package/dist/lib/daemon/process-manager.js +7 -4
- package/dist/lib/daemon/search-handler.js +23 -9
- package/dist/lib/daemon/watcher-manager.js +353 -110
- 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/index/batch-processor.js +231 -67
- package/dist/lib/index/embedding-generation.js +109 -0
- package/dist/lib/index/embedding-status.js +23 -0
- package/dist/lib/index/file-policy.js +273 -0
- package/dist/lib/index/ignore-patterns.js +4 -0
- package/dist/lib/index/index-config.js +18 -4
- package/dist/lib/index/syncer.js +256 -79
- package/dist/lib/index/walker.js +66 -86
- package/dist/lib/index/watcher-batch.js +9 -0
- package/dist/lib/index/watcher.js +129 -3
- package/dist/lib/llm/server.js +6 -0
- package/dist/lib/search/searcher.js +30 -19
- package/dist/lib/skeleton/skeletonizer.js +118 -0
- package/dist/lib/skeleton/summary-formatter.js +8 -1
- package/dist/lib/store/store-lease.js +473 -0
- package/dist/lib/store/vector-db.js +302 -57
- package/dist/lib/utils/blocked-roots.js +63 -0
- package/dist/lib/utils/cross-project.js +7 -3
- package/dist/lib/utils/daemon-client.js +24 -1
- package/dist/lib/utils/daemon-launcher.js +38 -13
- package/dist/lib/utils/doctor-status.js +76 -0
- package/dist/lib/utils/file-utils.js +74 -4
- package/dist/lib/utils/keyed-mutex.js +101 -0
- package/dist/lib/utils/logger.js +57 -1
- package/dist/lib/utils/mlx-hf-cache.js +114 -0
- package/dist/lib/utils/operation-coordinator.js +146 -0
- package/dist/lib/utils/path-containment.js +106 -0
- package/dist/lib/utils/process.js +44 -0
- package/dist/lib/utils/project-registry.js +351 -3
- package/dist/lib/utils/scope-filter.js +3 -9
- package/dist/lib/utils/stale-hint.js +12 -19
- package/dist/lib/utils/watcher-launcher.js +5 -1
- package/dist/lib/utils/watcher-store.js +2 -2
- package/dist/lib/workers/colbert-math.js +15 -12
- package/dist/lib/workers/embeddings/colbert.js +3 -2
- package/dist/lib/workers/embeddings/granite.js +4 -3
- package/dist/lib/workers/embeddings/mlx-client.js +59 -16
- package/dist/lib/workers/orchestrator.js +39 -8
- package/dist/lib/workers/pool.js +150 -83
- package/dist/lib/workers/process-child.js +11 -2
- package/dist/lib/workers/serialized-handler.js +10 -0
- package/dist/lib/workers/worker.js +13 -4
- package/mlx-embed-server/server.py +21 -3
- package/mlx-embed-server/summarizer.py +8 -0
- package/package.json +6 -3
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +3 -170
package/dist/commands/watch.js
CHANGED
|
@@ -48,7 +48,6 @@ const fs = __importStar(require("node:fs"));
|
|
|
48
48
|
const path = __importStar(require("node:path"));
|
|
49
49
|
const commander_1 = require("commander");
|
|
50
50
|
const config_1 = require("../config");
|
|
51
|
-
const index_config_1 = require("../lib/index/index-config");
|
|
52
51
|
const syncer_1 = require("../lib/index/syncer");
|
|
53
52
|
const watcher_1 = require("../lib/index/watcher");
|
|
54
53
|
const meta_cache_1 = require("../lib/store/meta-cache");
|
|
@@ -122,19 +121,51 @@ exports.watch = new commander_1.Command("watch")
|
|
|
122
121
|
}
|
|
123
122
|
const logFile = path.join(config_1.PATHS.logsDir, "daemon.log");
|
|
124
123
|
const out = (0, log_rotate_1.openRotatedLog)(logFile);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
let child;
|
|
125
|
+
try {
|
|
126
|
+
child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "watch", "--daemon"], {
|
|
127
|
+
detached: true,
|
|
128
|
+
stdio: ["ignore", out, out],
|
|
129
|
+
cwd: process.cwd(),
|
|
130
|
+
env: Object.assign(Object.assign({}, process.env), { GMAX_BACKGROUND: "true" }),
|
|
131
|
+
});
|
|
132
|
+
yield new Promise((resolve, reject) => {
|
|
133
|
+
child.once("spawn", resolve);
|
|
134
|
+
child.once("error", reject);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
139
|
+
console.error(`Failed to start daemon: ${message}`);
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
try {
|
|
145
|
+
fs.closeSync(out);
|
|
146
|
+
}
|
|
147
|
+
catch (_c) { }
|
|
148
|
+
}
|
|
131
149
|
child.unref();
|
|
132
150
|
console.log(`Daemon started (PID: ${child.pid}, log: ${logFile})`);
|
|
133
151
|
process.exit(0);
|
|
134
152
|
}
|
|
135
153
|
// Daemon foreground
|
|
154
|
+
// Stamp every daemon.log line so incidents can be correlated with the
|
|
155
|
+
// timestamped MLX/LLM server logs. Workers inherit via env flag.
|
|
156
|
+
const { installTimestampedOutput } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/logger")));
|
|
157
|
+
installTimestampedOutput();
|
|
136
158
|
const { Daemon } = yield Promise.resolve().then(() => __importStar(require("../lib/daemon/daemon")));
|
|
137
159
|
const daemon = new Daemon();
|
|
160
|
+
process.on("SIGINT", () => daemon.shutdown().then(() => (0, exit_1.gracefulExit)()));
|
|
161
|
+
process.on("SIGTERM", () => daemon.shutdown().then(() => (0, exit_1.gracefulExit)()));
|
|
162
|
+
process.on("uncaughtException", (err) => {
|
|
163
|
+
console.error("[daemon] uncaughtException:", err);
|
|
164
|
+
daemon.shutdown().then(() => (0, exit_1.gracefulExit)(1));
|
|
165
|
+
});
|
|
166
|
+
process.on("unhandledRejection", (reason) => {
|
|
167
|
+
console.error("[daemon] unhandledRejection:", reason);
|
|
168
|
+
});
|
|
138
169
|
try {
|
|
139
170
|
yield daemon.start();
|
|
140
171
|
}
|
|
@@ -151,19 +182,10 @@ exports.watch = new commander_1.Command("watch")
|
|
|
151
182
|
try {
|
|
152
183
|
yield daemon.shutdown();
|
|
153
184
|
}
|
|
154
|
-
catch (
|
|
185
|
+
catch (_d) { }
|
|
155
186
|
process.exitCode = 1;
|
|
156
187
|
return;
|
|
157
188
|
}
|
|
158
|
-
process.on("SIGINT", () => daemon.shutdown().then(() => (0, exit_1.gracefulExit)()));
|
|
159
|
-
process.on("SIGTERM", () => daemon.shutdown().then(() => (0, exit_1.gracefulExit)()));
|
|
160
|
-
process.on("uncaughtException", (err) => {
|
|
161
|
-
console.error("[daemon] uncaughtException:", err);
|
|
162
|
-
daemon.shutdown().then(() => (0, exit_1.gracefulExit)(1));
|
|
163
|
-
});
|
|
164
|
-
process.on("unhandledRejection", (reason) => {
|
|
165
|
-
console.error("[daemon] unhandledRejection:", reason);
|
|
166
|
-
});
|
|
167
189
|
return;
|
|
168
190
|
}
|
|
169
191
|
// --- Per-project mode ---
|
|
@@ -185,12 +207,31 @@ exports.watch = new commander_1.Command("watch")
|
|
|
185
207
|
const safeName = projectName.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
186
208
|
const logFile = path.join(config_1.PATHS.logsDir, `watch-${safeName}.log`);
|
|
187
209
|
const out = (0, log_rotate_1.openRotatedLog)(logFile);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
210
|
+
let child;
|
|
211
|
+
try {
|
|
212
|
+
child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], ...args], {
|
|
213
|
+
detached: true,
|
|
214
|
+
stdio: ["ignore", out, out],
|
|
215
|
+
cwd: process.cwd(),
|
|
216
|
+
env: Object.assign(Object.assign({}, process.env), { GMAX_BACKGROUND: "true" }),
|
|
217
|
+
});
|
|
218
|
+
yield new Promise((resolve, reject) => {
|
|
219
|
+
child.once("spawn", resolve);
|
|
220
|
+
child.once("error", reject);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
225
|
+
console.error(`Failed to start watcher for ${projectName}: ${message}`);
|
|
226
|
+
process.exitCode = 1;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
try {
|
|
231
|
+
fs.closeSync(out);
|
|
232
|
+
}
|
|
233
|
+
catch (_e) { }
|
|
234
|
+
}
|
|
194
235
|
child.unref();
|
|
195
236
|
console.log(`Watcher started for ${projectName} (PID: ${child.pid}, log: ${logFile})`);
|
|
196
237
|
process.exit(0);
|
|
@@ -225,24 +266,36 @@ exports.watch = new commander_1.Command("watch")
|
|
|
225
266
|
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
226
267
|
.limit(1)
|
|
227
268
|
.toArray();
|
|
269
|
+
let degraded = false;
|
|
270
|
+
let degradedErrors = 0;
|
|
271
|
+
let initialScanErrors = 0;
|
|
272
|
+
let initialFailedFiles = 0;
|
|
228
273
|
if (indexed.length === 0) {
|
|
229
274
|
console.log(`[watch:${projectName}] No index found for ${projectRoot}, running initial sync...`);
|
|
230
275
|
const syncResult = yield (0, syncer_1.initialSync)({ projectRoot });
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
276
|
+
if (syncResult.degraded) {
|
|
277
|
+
degraded = true;
|
|
278
|
+
degradedErrors = syncResult.scanErrors + syncResult.failedFiles;
|
|
279
|
+
initialScanErrors = syncResult.scanErrors;
|
|
280
|
+
initialFailedFiles = syncResult.failedFiles;
|
|
281
|
+
console.warn(`[watch:${projectName}] Initial sync incomplete: ${syncResult.scanErrors} scan error(s), ${syncResult.failedFiles} file failure(s). Preserving pending registry state.`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
const chunkCount = yield vectorDb.countRowsForPath(prefix);
|
|
285
|
+
(0, project_registry_1.stampProjectFullSync)({
|
|
286
|
+
root: projectRoot,
|
|
287
|
+
name: projectName,
|
|
288
|
+
generation: syncResult.generation,
|
|
289
|
+
embedMode: syncResult.embedMode,
|
|
290
|
+
chunkCount,
|
|
291
|
+
chunkerVersion: config_1.CONFIG.CHUNKER_VERSION,
|
|
292
|
+
expectedFingerprint: syncResult.registryExpectation.embeddingFingerprint,
|
|
293
|
+
expectedRebuildId: syncResult.registryExpectation.rebuildId,
|
|
294
|
+
});
|
|
295
|
+
console.log(`[watch:${projectName}] Initial sync complete.`);
|
|
296
|
+
}
|
|
244
297
|
}
|
|
245
|
-
(0, watcher_store_1.updateWatcherStatus)(process.pid, "watching");
|
|
298
|
+
(0, watcher_store_1.updateWatcherStatus)(process.pid, degraded ? "degraded" : "watching", undefined, degraded ? `${degradedErrors} incomplete scan path(s)` : undefined);
|
|
246
299
|
// Open resources for watcher
|
|
247
300
|
const metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
248
301
|
// Start watching
|
|
@@ -251,10 +304,17 @@ exports.watch = new commander_1.Command("watch")
|
|
|
251
304
|
vectorDb,
|
|
252
305
|
metaCache,
|
|
253
306
|
dataDir: paths.dataDir,
|
|
307
|
+
initialScanErrors,
|
|
308
|
+
initialFailedFiles,
|
|
254
309
|
onReindex: (files, ms) => {
|
|
255
310
|
console.log(`[watch:${projectName}] Reindexed ${files} file${files !== 1 ? "s" : ""} (${(ms / 1000).toFixed(1)}s)`);
|
|
256
311
|
lastActivity = Date.now();
|
|
257
|
-
(0, watcher_store_1.updateWatcherStatus)(process.pid, "watching", Date.now());
|
|
312
|
+
(0, watcher_store_1.updateWatcherStatus)(process.pid, degraded ? "degraded" : "watching", Date.now(), degraded ? `${degradedErrors} incomplete scan path(s)` : undefined);
|
|
313
|
+
},
|
|
314
|
+
onHealthChange: (complete, errors) => {
|
|
315
|
+
degraded = !complete;
|
|
316
|
+
degradedErrors = errors;
|
|
317
|
+
(0, watcher_store_1.updateWatcherStatus)(process.pid, complete ? "watching" : "degraded", undefined, complete ? undefined : `${errors} incomplete scan path(s)`);
|
|
258
318
|
},
|
|
259
319
|
});
|
|
260
320
|
console.log(`[watch:${projectName}] File watcher active`);
|
|
@@ -300,6 +360,10 @@ exports.watch
|
|
|
300
360
|
const projects = resp.projects;
|
|
301
361
|
const uptime = Math.floor(resp.uptime / 60);
|
|
302
362
|
console.log(`Daemon (PID: ${resp.pid}, uptime: ${uptime}m):`);
|
|
363
|
+
const mlx = resp.mlx;
|
|
364
|
+
if (mlx) {
|
|
365
|
+
console.log(` MLX: ${mlx.state} (${mlx.model}${mlx.pid ? `, PID ${mlx.pid}` : ""})${mlx.error ? ` — ${mlx.error}` : ""}`);
|
|
366
|
+
}
|
|
303
367
|
for (const p of projects) {
|
|
304
368
|
console.log(` - ${p.root} [${p.status}]`);
|
|
305
369
|
}
|
package/dist/config.js
CHANGED
|
@@ -217,6 +217,9 @@ exports.PATHS = {
|
|
|
217
217
|
globalRoot: GLOBAL_ROOT,
|
|
218
218
|
models: path.join(GLOBAL_ROOT, "models"),
|
|
219
219
|
grammars: path.join(GLOBAL_ROOT, "grammars"),
|
|
220
|
+
// HF cache for the MLX embed server, pinned to internal disk so the server
|
|
221
|
+
// survives an unmounted external volume behind the user's HF_HOME.
|
|
222
|
+
hfDir: path.join(GLOBAL_ROOT, "hf"),
|
|
220
223
|
logsDir: path.join(GLOBAL_ROOT, "logs"),
|
|
221
224
|
daemonSocket: path.join(GLOBAL_ROOT, "daemon.sock"),
|
|
222
225
|
daemonPidFile: path.join(GLOBAL_ROOT, "daemon.pid"),
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Measure-first prototype for Graphify Phase 3 "surprising connections".
|
|
4
|
+
*
|
|
5
|
+
* It samples indexed code chunks, asks LanceDB for each chunk's nearest vector
|
|
6
|
+
* neighbors, then filters out pairs that are already obvious. Output is grouped
|
|
7
|
+
* by file pair and scored for actionability. This is deliberately an eval
|
|
8
|
+
* harness; the product surface is `gmax surprises --experimental`.
|
|
9
|
+
*
|
|
10
|
+
* Run:
|
|
11
|
+
* pnpm bench:surprises
|
|
12
|
+
* pnpm bench:surprises -- --sample 300 --neighbors 25 --top 30
|
|
13
|
+
* pnpm bench:surprises:json
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
49
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
50
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
51
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
52
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
53
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
54
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
const path = __importStar(require("node:path"));
|
|
59
|
+
const surprising_connections_1 = require("./lib/analysis/surprising-connections");
|
|
60
|
+
const vector_db_1 = require("./lib/store/vector-db");
|
|
61
|
+
const exit_1 = require("./lib/utils/exit");
|
|
62
|
+
const project_root_1 = require("./lib/utils/project-root");
|
|
63
|
+
function argValue(name) {
|
|
64
|
+
const prefix = `${name}=`;
|
|
65
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
66
|
+
const arg = process.argv[i];
|
|
67
|
+
if (arg === name)
|
|
68
|
+
return process.argv[i + 1];
|
|
69
|
+
if (arg.startsWith(prefix))
|
|
70
|
+
return arg.slice(prefix.length);
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
function argValues(name) {
|
|
75
|
+
const values = [];
|
|
76
|
+
const prefix = `${name}=`;
|
|
77
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
78
|
+
const arg = process.argv[i];
|
|
79
|
+
if (arg === name && process.argv[i + 1]) {
|
|
80
|
+
values.push(process.argv[i + 1]);
|
|
81
|
+
i++;
|
|
82
|
+
}
|
|
83
|
+
else if (arg.startsWith(prefix)) {
|
|
84
|
+
values.push(arg.slice(prefix.length));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return values.length > 0 ? values : undefined;
|
|
88
|
+
}
|
|
89
|
+
function hasFlag(name) {
|
|
90
|
+
return process.argv.includes(name);
|
|
91
|
+
}
|
|
92
|
+
function intOpt(name, envName, fallback) {
|
|
93
|
+
var _a;
|
|
94
|
+
const raw = (_a = argValue(name)) !== null && _a !== void 0 ? _a : process.env[envName];
|
|
95
|
+
const parsed = Number.parseInt(raw !== null && raw !== void 0 ? raw : "", 10);
|
|
96
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
97
|
+
}
|
|
98
|
+
function floatOpt(name, envName, fallback) {
|
|
99
|
+
var _a;
|
|
100
|
+
const raw = (_a = argValue(name)) !== null && _a !== void 0 ? _a : process.env[envName];
|
|
101
|
+
const parsed = Number.parseFloat(raw !== null && raw !== void 0 ? raw : "");
|
|
102
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
103
|
+
}
|
|
104
|
+
function run() {
|
|
105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
const root = path.resolve((_a = argValue("--root")) !== null && _a !== void 0 ? _a : process.cwd());
|
|
108
|
+
const projectRoot = (_b = (0, project_root_1.findProjectRoot)(root)) !== null && _b !== void 0 ? _b : root;
|
|
109
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
110
|
+
const vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
111
|
+
const table = yield vectorDb.ensureTable();
|
|
112
|
+
const top = intOpt("--top", "GMAX_SURPRISE_TOP", 25);
|
|
113
|
+
const json = hasFlag("--json") || process.env.GMAX_EVAL_JSON === "1";
|
|
114
|
+
const log = json ? console.error : console.log;
|
|
115
|
+
const result = yield (0, surprising_connections_1.analyzeSurprisingConnections)(table, projectRoot, {
|
|
116
|
+
sample: intOpt("--sample", "GMAX_SURPRISE_SAMPLE", surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.sample),
|
|
117
|
+
neighbors: intOpt("--neighbors", "GMAX_SURPRISE_NEIGHBORS", surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.neighbors),
|
|
118
|
+
dirDepth: intOpt("--dir-depth", "GMAX_SURPRISE_DIR_DEPTH", surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.dirDepth),
|
|
119
|
+
minSimilarity: floatOpt("--min-sim", "GMAX_SURPRISE_MIN_SIM", surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.minSimilarity),
|
|
120
|
+
maxRows: intOpt("--max-rows", "GMAX_SURPRISE_MAX_ROWS", surprising_connections_1.DEFAULT_SURPRISE_OPTIONS.maxRows),
|
|
121
|
+
includeTests: hasFlag("--include-tests"),
|
|
122
|
+
includeEval: hasFlag("--include-eval"),
|
|
123
|
+
in: argValues("--in"),
|
|
124
|
+
exclude: argValues("--exclude"),
|
|
125
|
+
});
|
|
126
|
+
const { summary, findings } = result;
|
|
127
|
+
const topFindings = findings.slice(0, top);
|
|
128
|
+
log(`Surprising-connections prototype: ${summary.sampledAnchors} sampled chunks from ${summary.codeRows} code chunks (${summary.rows} indexed rows)`);
|
|
129
|
+
if (json) {
|
|
130
|
+
process.stdout.write(`${JSON.stringify({
|
|
131
|
+
summary,
|
|
132
|
+
findings: topFindings.map((finding) => ({
|
|
133
|
+
score: finding.score,
|
|
134
|
+
maxSimilarity: finding.maxSimilarity,
|
|
135
|
+
medianSimilarity: finding.medianSimilarity,
|
|
136
|
+
pairCount: finding.pairCount,
|
|
137
|
+
files: [finding.fileA, finding.fileB],
|
|
138
|
+
reasons: finding.reasons,
|
|
139
|
+
topSimilarities: finding.topSimilarities,
|
|
140
|
+
representative: {
|
|
141
|
+
similarity: Number(finding.representative.similarity.toFixed(3)),
|
|
142
|
+
distance: Number(finding.representative.distance.toFixed(3)),
|
|
143
|
+
scoreParts: finding.representative.scoreParts,
|
|
144
|
+
source: {
|
|
145
|
+
file: finding.representative.source.relPath,
|
|
146
|
+
line: finding.representative.source.startLine + 1,
|
|
147
|
+
symbols: finding.representative.source.definedSymbols.slice(0, 4),
|
|
148
|
+
role: finding.representative.source.role,
|
|
149
|
+
},
|
|
150
|
+
target: {
|
|
151
|
+
file: finding.representative.target.relPath,
|
|
152
|
+
line: finding.representative.target.startLine + 1,
|
|
153
|
+
symbols: finding.representative.target.definedSymbols.slice(0, 4),
|
|
154
|
+
role: finding.representative.target.role,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
})),
|
|
158
|
+
}, null, 2)}\n`);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log("\nSummary");
|
|
162
|
+
console.log(` project: ${projectRoot}`);
|
|
163
|
+
console.log(` rows/code rows: ${summary.rows}/${summary.codeRows}`);
|
|
164
|
+
console.log(` sampled anchors: ${summary.sampledAnchors}`);
|
|
165
|
+
console.log(` graph file edges: ${summary.graphFileEdges}`);
|
|
166
|
+
console.log(` accepted pairs/file-pairs: ${summary.acceptedPairs}/${summary.acceptedFilePairs}`);
|
|
167
|
+
console.log(` similarity p50/p90/max: ${summary.similarity.p50}/${summary.similarity.p90}/${summary.similarity.max}`);
|
|
168
|
+
console.log(` score p50/p90/max: ${summary.actionabilityScore.p50}/${summary.actionabilityScore.p90}/${summary.actionabilityScore.max}`);
|
|
169
|
+
console.log(` filtered: same-file ${summary.filters.sameFile}, same-dir ${summary.filters.sameDirBucket}, graph-edge ${summary.filters.graphEdge}, tests ${summary.filters.tests}`);
|
|
170
|
+
console.log("\nTop grouped surprising connections");
|
|
171
|
+
if (topFindings.length === 0) {
|
|
172
|
+
console.log(" none");
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
for (const finding of topFindings) {
|
|
176
|
+
const pair = finding.representative;
|
|
177
|
+
console.log(` score=${finding.score.toFixed(3)} sim=${finding.maxSimilarity.toFixed(3)} pairs=${finding.pairCount} ${finding.fileA}`);
|
|
178
|
+
console.log(` <-> ${finding.fileB}`);
|
|
179
|
+
console.log(` best: ${(0, surprising_connections_1.lineLabel)(pair.source)} <-> ${(0, surprising_connections_1.lineLabel)(pair.target)}`);
|
|
180
|
+
console.log(` reasons: ${finding.reasons.join(", ") || "none"}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
yield vectorDb.close();
|
|
185
|
+
yield (0, exit_1.gracefulExit)(0);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
run().catch((error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
189
|
+
console.error(error instanceof Error ? error.stack || error.message : error);
|
|
190
|
+
yield (0, exit_1.gracefulExit)(1);
|
|
191
|
+
}));
|
package/dist/index.js
CHANGED
|
@@ -73,6 +73,7 @@ const similar_1 = require("./commands/similar");
|
|
|
73
73
|
const skeleton_1 = require("./commands/skeleton");
|
|
74
74
|
const status_1 = require("./commands/status");
|
|
75
75
|
const summarize_1 = require("./commands/summarize");
|
|
76
|
+
const surprises_1 = require("./commands/surprises");
|
|
76
77
|
const symbols_1 = require("./commands/symbols");
|
|
77
78
|
const test_find_1 = require("./commands/test-find");
|
|
78
79
|
const trace_1 = require("./commands/trace");
|
|
@@ -129,6 +130,7 @@ commander_1.program.addCommand(diff_1.diff);
|
|
|
129
130
|
commander_1.program.addCommand(test_find_1.testFind);
|
|
130
131
|
commander_1.program.addCommand(impact_1.impact);
|
|
131
132
|
commander_1.program.addCommand(similar_1.similar);
|
|
133
|
+
commander_1.program.addCommand(surprises_1.surprises);
|
|
132
134
|
commander_1.program.addCommand(context_1.context);
|
|
133
135
|
// Services
|
|
134
136
|
commander_1.program.addCommand(serve_1.serve);
|