grepmax 0.15.6 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/extract.js +20 -0
- package/dist/commands/peek.js +36 -0
- package/dist/commands/remove.js +39 -3
- package/dist/commands/search.js +175 -105
- package/dist/commands/skeleton.js +10 -20
- package/dist/lib/daemon/daemon.js +269 -16
- package/dist/lib/daemon/ipc-handler.js +36 -0
- package/dist/lib/index/batch-processor.js +18 -0
- package/dist/lib/index/chunker.js +8 -0
- package/dist/lib/store/vector-db.js +94 -27
- package/dist/lib/utils/language.js +91 -0
- package/dist/lib/workers/pool.js +42 -14
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +1 -1
package/dist/commands/extract.js
CHANGED
|
@@ -50,6 +50,7 @@ const vector_db_1 = require("../lib/store/vector-db");
|
|
|
50
50
|
const exit_1 = require("../lib/utils/exit");
|
|
51
51
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
52
52
|
const import_extractor_1 = require("../lib/utils/import-extractor");
|
|
53
|
+
const language_1 = require("../lib/utils/language");
|
|
53
54
|
const project_root_1 = require("../lib/utils/project-root");
|
|
54
55
|
const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
55
56
|
const style = {
|
|
@@ -128,6 +129,25 @@ exports.extract = new commander_1.Command("extract")
|
|
|
128
129
|
process.exitCode = 1;
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
132
|
+
// Cross-language disambiguation: when the symbol is defined in 2+
|
|
133
|
+
// languages, refuse to silently pick one. Listing all matches with a
|
|
134
|
+
// recovery hint avoids the dogfooded failure mode where peek picked
|
|
135
|
+
// Swift but listed TS callers.
|
|
136
|
+
const byLang = (0, language_1.groupByLanguage)(chunks);
|
|
137
|
+
if (byLang.size >= 2) {
|
|
138
|
+
const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
|
|
139
|
+
const lines = [
|
|
140
|
+
`Symbol '${symbol}' is defined in multiple languages:`,
|
|
141
|
+
];
|
|
142
|
+
for (const [lang, group] of byLang) {
|
|
143
|
+
const c = group[0];
|
|
144
|
+
lines.push(` ${lang.padEnd(6)} ${rel(c.path)}:${c.startLine + 1}`);
|
|
145
|
+
}
|
|
146
|
+
lines.push(`Disambiguate with --root or pin to a path: gmax extract ${symbol} --root <project-root>`);
|
|
147
|
+
console.log(lines.join("\n"));
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
131
151
|
const best = pickBestMatch(chunks, symbol);
|
|
132
152
|
const content = fs.readFileSync(best.path, "utf-8");
|
|
133
153
|
const allLines = content.split("\n");
|
package/dist/commands/peek.js
CHANGED
|
@@ -50,6 +50,7 @@ const graph_builder_1 = require("../lib/graph/graph-builder");
|
|
|
50
50
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
51
51
|
const exit_1 = require("../lib/utils/exit");
|
|
52
52
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
53
|
+
const language_1 = require("../lib/utils/language");
|
|
53
54
|
const project_root_1 = require("../lib/utils/project-root");
|
|
54
55
|
const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
55
56
|
const style = {
|
|
@@ -104,6 +105,41 @@ exports.peek = new commander_1.Command("peek")
|
|
|
104
105
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
105
106
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
106
107
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
108
|
+
// Cross-language disambiguation: when the symbol is defined in 2+
|
|
109
|
+
// languages, refuse to silently pick one. The graph builder otherwise
|
|
110
|
+
// picks one chunk arbitrarily and lists callers from a different
|
|
111
|
+
// language — verified failure mode.
|
|
112
|
+
{
|
|
113
|
+
const tableForCheck = yield vectorDb.ensureTable();
|
|
114
|
+
const prefixForCheck = projectRoot.endsWith("/")
|
|
115
|
+
? projectRoot
|
|
116
|
+
: `${projectRoot}/`;
|
|
117
|
+
const allDefs = yield tableForCheck
|
|
118
|
+
.query()
|
|
119
|
+
.select(["path", "start_line"])
|
|
120
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND path LIKE '${(0, filter_builder_1.escapeSqlString)(prefixForCheck)}%'`)
|
|
121
|
+
.limit(20)
|
|
122
|
+
.toArray();
|
|
123
|
+
const chunks = allDefs.map((row) => ({
|
|
124
|
+
path: String(row.path || ""),
|
|
125
|
+
startLine: Number(row.start_line || 0),
|
|
126
|
+
}));
|
|
127
|
+
const byLang = (0, language_1.groupByLanguage)(chunks);
|
|
128
|
+
if (byLang.size >= 2) {
|
|
129
|
+
const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
|
|
130
|
+
const lines = [
|
|
131
|
+
`Symbol '${symbol}' is defined in multiple languages:`,
|
|
132
|
+
];
|
|
133
|
+
for (const [lang, group] of byLang) {
|
|
134
|
+
const c = group[0];
|
|
135
|
+
lines.push(` ${lang.padEnd(6)} ${rel(c.path)}:${c.startLine + 1}`);
|
|
136
|
+
}
|
|
137
|
+
lines.push(`Disambiguate with --root or pin to a path: gmax peek ${symbol} --root <project-root>`);
|
|
138
|
+
console.log(lines.join("\n"));
|
|
139
|
+
process.exitCode = 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
107
143
|
const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, projectRoot);
|
|
108
144
|
const graph = yield graphBuilder.buildGraph(symbol);
|
|
109
145
|
if (!graph.center) {
|
package/dist/commands/remove.js
CHANGED
|
@@ -43,6 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.remove = void 0;
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
46
47
|
const path = __importStar(require("node:path"));
|
|
47
48
|
const readline = __importStar(require("node:readline"));
|
|
48
49
|
const commander_1 = require("commander");
|
|
@@ -68,12 +69,13 @@ function confirm(message) {
|
|
|
68
69
|
}
|
|
69
70
|
exports.remove = new commander_1.Command("remove")
|
|
70
71
|
.description("Remove a project from the gmax index")
|
|
71
|
-
.argument("[dir]", "Directory
|
|
72
|
+
.argument("[dir-or-name]", "Directory or registered project name (defaults to current directory)")
|
|
72
73
|
.option("-f, --force", "Skip confirmation prompt", false)
|
|
73
74
|
.addHelpText("after", `
|
|
74
75
|
Examples:
|
|
75
76
|
gmax remove Remove the current project
|
|
76
|
-
gmax remove ~/projects/app Remove a specific project
|
|
77
|
+
gmax remove ~/projects/app Remove a specific project by path
|
|
78
|
+
gmax remove my-app Remove a registered project by name
|
|
77
79
|
gmax remove --force Skip confirmation
|
|
78
80
|
`)
|
|
79
81
|
.action((dir, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -81,7 +83,41 @@ Examples:
|
|
|
81
83
|
let vectorDb = null;
|
|
82
84
|
let metaCache = null;
|
|
83
85
|
try {
|
|
84
|
-
|
|
86
|
+
// Resolve name → registered root when arg has no path separator and isn't a dir.
|
|
87
|
+
// Avoids the footgun where a typo'd name silently removed the cwd project.
|
|
88
|
+
let resolvedDir = dir;
|
|
89
|
+
if (dir &&
|
|
90
|
+
!dir.includes("/") &&
|
|
91
|
+
!dir.includes("\\") &&
|
|
92
|
+
!fs.existsSync(path.resolve(dir))) {
|
|
93
|
+
const matches = (0, project_registry_1.listProjects)().filter((p) => p.name === dir);
|
|
94
|
+
if (matches.length === 0) {
|
|
95
|
+
console.error(`No registered project named "${dir}".`);
|
|
96
|
+
const all = (0, project_registry_1.listProjects)();
|
|
97
|
+
if (all.length > 0) {
|
|
98
|
+
console.error("Available projects:");
|
|
99
|
+
for (const p of all) {
|
|
100
|
+
console.error(` ${p.name.padEnd(24)} ${p.root}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.error("No projects are currently registered.");
|
|
105
|
+
}
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (matches.length > 1) {
|
|
110
|
+
console.error(`Multiple registered projects named "${dir}":`);
|
|
111
|
+
for (const p of matches) {
|
|
112
|
+
console.error(` ${p.root}`);
|
|
113
|
+
}
|
|
114
|
+
console.error("Pass an absolute path to disambiguate.");
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
resolvedDir = matches[0].root;
|
|
119
|
+
}
|
|
120
|
+
const targetDir = resolvedDir ? path.resolve(resolvedDir) : process.cwd();
|
|
85
121
|
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(targetDir)) !== null && _a !== void 0 ? _a : targetDir;
|
|
86
122
|
const projectName = path.basename(projectRoot);
|
|
87
123
|
const project = (0, project_registry_1.getProject)(projectRoot);
|
package/dist/commands/search.js
CHANGED
|
@@ -256,9 +256,9 @@ function formatCompactTable(hits, projectRoot, query, opts) {
|
|
|
256
256
|
}
|
|
257
257
|
// Reuse Skeletonizer instance
|
|
258
258
|
let globalSkeletonizer = null;
|
|
259
|
-
function outputSkeletons(results, projectRoot, limit, db) {
|
|
259
|
+
function outputSkeletons(results, projectRoot, limit, db, precomputed) {
|
|
260
260
|
return __awaiter(this, void 0, void 0, function* () {
|
|
261
|
-
var _a;
|
|
261
|
+
var _a, _b;
|
|
262
262
|
const seenPaths = new Set();
|
|
263
263
|
const filesToProcess = [];
|
|
264
264
|
for (const result of results) {
|
|
@@ -288,6 +288,16 @@ function outputSkeletons(results, projectRoot, limit, db) {
|
|
|
288
288
|
const absPath = path.isAbsolute(filePath)
|
|
289
289
|
? filePath
|
|
290
290
|
: path.resolve(projectRoot, filePath);
|
|
291
|
+
// 0. Daemon-supplied (preferred — already-warm DB lookup, no cold open)
|
|
292
|
+
const fromDaemon = (_b = precomputed === null || precomputed === void 0 ? void 0 : precomputed[absPath]) !== null && _b !== void 0 ? _b : precomputed === null || precomputed === void 0 ? void 0 : precomputed[filePath];
|
|
293
|
+
if (fromDaemon) {
|
|
294
|
+
skeletonResults.push({
|
|
295
|
+
file: filePath,
|
|
296
|
+
skeleton: fromDaemon,
|
|
297
|
+
tokens: Math.ceil(fromDaemon.length / 4),
|
|
298
|
+
});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
291
301
|
// 1. Try DB cache
|
|
292
302
|
if (db) {
|
|
293
303
|
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
@@ -378,7 +388,7 @@ Examples:
|
|
|
378
388
|
gmax "handler" --name "handle.*" --exclude tests/
|
|
379
389
|
`)
|
|
380
390
|
.action((pattern, exec_path, _options, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
381
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9;
|
|
391
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10;
|
|
382
392
|
const options = cmd.optsWithGlobals();
|
|
383
393
|
const root = process.cwd();
|
|
384
394
|
const minScore = Number.isFinite(Number.parseFloat(options.minScore))
|
|
@@ -498,79 +508,10 @@ Examples:
|
|
|
498
508
|
if (project.status === "pending") {
|
|
499
509
|
console.warn("This project is still being indexed. Results may be incomplete.\n");
|
|
500
510
|
}
|
|
501
|
-
|
|
502
|
-
//
|
|
503
|
-
if (!options.agent && (0, lock_1.isLocked)(paths.dataDir)) {
|
|
504
|
-
console.warn("⚠️ Warning: Indexing in progress... search results may be incomplete.");
|
|
505
|
-
}
|
|
506
|
-
const hasRows = yield vectorDb.hasAnyRows();
|
|
507
|
-
const needsSync = options.sync || !hasRows;
|
|
508
|
-
if (needsSync) {
|
|
509
|
-
const isTTY = process.stdout.isTTY;
|
|
510
|
-
let abortController;
|
|
511
|
-
let signal;
|
|
512
|
-
if (!isTTY) {
|
|
513
|
-
abortController = new AbortController();
|
|
514
|
-
signal = abortController.signal;
|
|
515
|
-
setTimeout(() => {
|
|
516
|
-
abortController === null || abortController === void 0 ? void 0 : abortController.abort();
|
|
517
|
-
}, 60000); // 60 seconds timeout for non-TTY auto-indexing
|
|
518
|
-
}
|
|
519
|
-
const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, options.sync ? "Indexing..." : "Indexing repository (first run)...");
|
|
520
|
-
try {
|
|
521
|
-
yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
|
|
522
|
-
const result = yield (0, syncer_1.initialSync)({
|
|
523
|
-
projectRoot,
|
|
524
|
-
dryRun: options.dryRun,
|
|
525
|
-
onProgress,
|
|
526
|
-
signal,
|
|
527
|
-
});
|
|
528
|
-
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
529
|
-
spinner.warn(`Indexing timed out (${result.processed}/${result.total}). Results may be partial.`);
|
|
530
|
-
}
|
|
531
|
-
if (options.dryRun) {
|
|
532
|
-
spinner.succeed(`Dry run complete (${result.processed}/${result.total}) • would have indexed ${result.indexed}`);
|
|
533
|
-
console.log((0, sync_helpers_1.formatDryRunSummary)(result, {
|
|
534
|
-
actionDescription: "would have indexed",
|
|
535
|
-
includeTotal: true,
|
|
536
|
-
}));
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
yield vectorDb.createFTSIndex();
|
|
540
|
-
// Update registry after sync
|
|
541
|
-
const { readGlobalConfig } = yield Promise.resolve().then(() => __importStar(require("../lib/index/index-config")));
|
|
542
|
-
const { registerProject } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/project-registry")));
|
|
543
|
-
const gc = readGlobalConfig();
|
|
544
|
-
registerProject({
|
|
545
|
-
root: projectRoot,
|
|
546
|
-
name: path.basename(projectRoot),
|
|
547
|
-
vectorDim: gc.vectorDim,
|
|
548
|
-
modelTier: gc.modelTier,
|
|
549
|
-
embedMode: gc.embedMode,
|
|
550
|
-
lastIndexed: new Date().toISOString(),
|
|
551
|
-
chunkCount: result.indexed,
|
|
552
|
-
status: "indexed",
|
|
553
|
-
});
|
|
554
|
-
const failedSuffix = result.failedFiles > 0 ? ` • ${result.failedFiles} failed` : "";
|
|
555
|
-
spinner.succeed(`${options.sync ? "Indexing" : "Initial indexing"} complete (${result.processed}/${result.total}) • indexed ${result.indexed}${failedSuffix}`);
|
|
556
|
-
}
|
|
557
|
-
catch (e) {
|
|
558
|
-
spinner.fail("Indexing failed");
|
|
559
|
-
throw e;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
// Ensure a watcher is running for live reindexing
|
|
563
|
-
if (!process.env.VITEST && !((_d = process.env.NODE_ENV) === null || _d === void 0 ? void 0 : _d.includes("test"))) {
|
|
564
|
-
const { launchWatcher } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/watcher-launcher")));
|
|
565
|
-
const launched = yield launchWatcher(projectRoot);
|
|
566
|
-
if (!launched.ok && launched.reason === "spawn-failed") {
|
|
567
|
-
console.warn(`[search] ${launched.message}`);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
const searcher = new searcher_1.Searcher(vectorDb);
|
|
571
|
-
// Use --root or fall back to project root
|
|
511
|
+
// Compute effective paths + filters early — both the daemon-mediated
|
|
512
|
+
// and in-process search paths need them.
|
|
572
513
|
const effectiveRoot = options.root
|
|
573
|
-
? (
|
|
514
|
+
? (_d = (0, project_root_1.findProjectRoot)(path.resolve(options.root))) !== null && _d !== void 0 ? _d : path.resolve(options.root)
|
|
574
515
|
: projectRoot;
|
|
575
516
|
const searchPathPrefix = exec_path
|
|
576
517
|
? path.resolve(exec_path)
|
|
@@ -578,7 +519,6 @@ Examples:
|
|
|
578
519
|
const pathFilter = searchPathPrefix.endsWith("/")
|
|
579
520
|
? searchPathPrefix
|
|
580
521
|
: `${searchPathPrefix}/`;
|
|
581
|
-
// Build filters from CLI options
|
|
582
522
|
const searchFilters = {};
|
|
583
523
|
if (options.file)
|
|
584
524
|
searchFilters.file = options.file;
|
|
@@ -588,8 +528,126 @@ Examples:
|
|
|
588
528
|
searchFilters.language = options.lang;
|
|
589
529
|
if (options.role)
|
|
590
530
|
searchFilters.role = options.role;
|
|
591
|
-
|
|
592
|
-
|
|
531
|
+
// Daemon-mediated search: ships query+args over IPC, daemon runs the
|
|
532
|
+
// hybrid+rerank against its already-warm VectorDB and worker pool.
|
|
533
|
+
// Drops cold-start cost (~17s wall, 6GB RAM in the CLI) to <1s. Falls
|
|
534
|
+
// back to in-process on any failure.
|
|
535
|
+
let searchResult = null;
|
|
536
|
+
let precomputedSkeletons;
|
|
537
|
+
let precomputedGraph;
|
|
538
|
+
if (!options.sync && !options.dryRun) {
|
|
539
|
+
try {
|
|
540
|
+
const { isDaemonRunning, sendDaemonCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
541
|
+
if (yield isDaemonRunning()) {
|
|
542
|
+
const resp = yield sendDaemonCommand({
|
|
543
|
+
cmd: "search",
|
|
544
|
+
projectRoot: effectiveRoot,
|
|
545
|
+
query: pattern,
|
|
546
|
+
limit: parseInt(options.m, 10),
|
|
547
|
+
filters: Object.keys(searchFilters).length > 0
|
|
548
|
+
? searchFilters
|
|
549
|
+
: undefined,
|
|
550
|
+
pathPrefix: pathFilter,
|
|
551
|
+
rerank: true,
|
|
552
|
+
explain: options.explain,
|
|
553
|
+
includeSkeletons: options.skeleton,
|
|
554
|
+
includeGraph: options.symbol,
|
|
555
|
+
}, { timeoutMs: 60000 });
|
|
556
|
+
if (resp.ok) {
|
|
557
|
+
searchResult = {
|
|
558
|
+
data: resp.data,
|
|
559
|
+
warnings: resp.warnings,
|
|
560
|
+
};
|
|
561
|
+
precomputedSkeletons = resp.skeletons;
|
|
562
|
+
precomputedGraph = resp.graph;
|
|
563
|
+
}
|
|
564
|
+
else if (process.env.GMAX_DEBUG === "1") {
|
|
565
|
+
console.error(`[search] daemon path unavailable: ${(_e = resp.error) !== null && _e !== void 0 ? _e : "unknown"}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
catch (err) {
|
|
570
|
+
if (process.env.GMAX_DEBUG === "1") {
|
|
571
|
+
console.error("[search] daemon attempt threw:", err);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
// In-process fallback: open VectorDB, ensure index, run Searcher.
|
|
576
|
+
// Only entered when the daemon path didn't produce results.
|
|
577
|
+
if (!searchResult) {
|
|
578
|
+
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
579
|
+
// Check for active indexing lock and warn if present
|
|
580
|
+
if (!options.agent && (0, lock_1.isLocked)(paths.dataDir)) {
|
|
581
|
+
console.warn("⚠️ Warning: Indexing in progress... search results may be incomplete.");
|
|
582
|
+
}
|
|
583
|
+
const hasRows = yield vectorDb.hasAnyRows();
|
|
584
|
+
const needsSync = options.sync || !hasRows;
|
|
585
|
+
if (needsSync) {
|
|
586
|
+
const isTTY = process.stdout.isTTY;
|
|
587
|
+
let abortController;
|
|
588
|
+
let signal;
|
|
589
|
+
if (!isTTY) {
|
|
590
|
+
abortController = new AbortController();
|
|
591
|
+
signal = abortController.signal;
|
|
592
|
+
setTimeout(() => {
|
|
593
|
+
abortController === null || abortController === void 0 ? void 0 : abortController.abort();
|
|
594
|
+
}, 60000); // 60 seconds timeout for non-TTY auto-indexing
|
|
595
|
+
}
|
|
596
|
+
const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, options.sync ? "Indexing..." : "Indexing repository (first run)...");
|
|
597
|
+
try {
|
|
598
|
+
yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
|
|
599
|
+
const result = yield (0, syncer_1.initialSync)({
|
|
600
|
+
projectRoot,
|
|
601
|
+
dryRun: options.dryRun,
|
|
602
|
+
onProgress,
|
|
603
|
+
signal,
|
|
604
|
+
});
|
|
605
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
606
|
+
spinner.warn(`Indexing timed out (${result.processed}/${result.total}). Results may be partial.`);
|
|
607
|
+
}
|
|
608
|
+
if (options.dryRun) {
|
|
609
|
+
spinner.succeed(`Dry run complete (${result.processed}/${result.total}) • would have indexed ${result.indexed}`);
|
|
610
|
+
console.log((0, sync_helpers_1.formatDryRunSummary)(result, {
|
|
611
|
+
actionDescription: "would have indexed",
|
|
612
|
+
includeTotal: true,
|
|
613
|
+
}));
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
yield vectorDb.createFTSIndex();
|
|
617
|
+
// Update registry after sync
|
|
618
|
+
const { readGlobalConfig } = yield Promise.resolve().then(() => __importStar(require("../lib/index/index-config")));
|
|
619
|
+
const { registerProject } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/project-registry")));
|
|
620
|
+
const gc = readGlobalConfig();
|
|
621
|
+
registerProject({
|
|
622
|
+
root: projectRoot,
|
|
623
|
+
name: path.basename(projectRoot),
|
|
624
|
+
vectorDim: gc.vectorDim,
|
|
625
|
+
modelTier: gc.modelTier,
|
|
626
|
+
embedMode: gc.embedMode,
|
|
627
|
+
lastIndexed: new Date().toISOString(),
|
|
628
|
+
chunkCount: result.indexed,
|
|
629
|
+
status: "indexed",
|
|
630
|
+
});
|
|
631
|
+
const failedSuffix = result.failedFiles > 0 ? ` • ${result.failedFiles} failed` : "";
|
|
632
|
+
spinner.succeed(`${options.sync ? "Indexing" : "Initial indexing"} complete (${result.processed}/${result.total}) • indexed ${result.indexed}${failedSuffix}`);
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
spinner.fail("Indexing failed");
|
|
636
|
+
throw e;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Ensure a watcher is running for live reindexing
|
|
640
|
+
if (!process.env.VITEST && !((_f = process.env.NODE_ENV) === null || _f === void 0 ? void 0 : _f.includes("test"))) {
|
|
641
|
+
const { launchWatcher } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/watcher-launcher")));
|
|
642
|
+
const launched = yield launchWatcher(projectRoot);
|
|
643
|
+
if (!launched.ok && launched.reason === "spawn-failed") {
|
|
644
|
+
console.warn(`[search] ${launched.message}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const searcher = new searcher_1.Searcher(vectorDb);
|
|
648
|
+
searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true, explain: options.explain }, Object.keys(searchFilters).length > 0 ? searchFilters : undefined, pathFilter);
|
|
649
|
+
} // end if (!searchResult) — in-process fallback
|
|
650
|
+
if (!options.agent && ((_g = searchResult.warnings) === null || _g === void 0 ? void 0 : _g.length)) {
|
|
593
651
|
for (const w of searchResult.warnings) {
|
|
594
652
|
console.warn(`Warning: ${w}`);
|
|
595
653
|
}
|
|
@@ -606,7 +664,7 @@ Examples:
|
|
|
606
664
|
return defs.some((d) => regex.test(d));
|
|
607
665
|
});
|
|
608
666
|
}
|
|
609
|
-
catch (
|
|
667
|
+
catch (_11) {
|
|
610
668
|
// Invalid regex — skip
|
|
611
669
|
}
|
|
612
670
|
}
|
|
@@ -632,16 +690,16 @@ Examples:
|
|
|
632
690
|
// In agent mode, print imports header per file
|
|
633
691
|
const seenImportFiles = new Set();
|
|
634
692
|
for (const r of filteredData) {
|
|
635
|
-
const absP = (
|
|
693
|
+
const absP = (_k = (_h = r.path) !== null && _h !== void 0 ? _h : (_j = r.metadata) === null || _j === void 0 ? void 0 : _j.path) !== null && _k !== void 0 ? _k : "";
|
|
636
694
|
const relPath = absP.startsWith(effectiveRoot)
|
|
637
695
|
? absP.slice(effectiveRoot.length + 1)
|
|
638
696
|
: absP;
|
|
639
|
-
const startLine = Math.max(1, ((
|
|
697
|
+
const startLine = Math.max(1, ((_p = (_m = (_l = r.startLine) !== null && _l !== void 0 ? _l : r.start_line) !== null && _m !== void 0 ? _m : (_o = r.generated_metadata) === null || _o === void 0 ? void 0 : _o.start_line) !== null && _p !== void 0 ? _p : 0) + 1);
|
|
640
698
|
const defs = Array.isArray(r.defined_symbols)
|
|
641
699
|
? r.defined_symbols
|
|
642
700
|
: [];
|
|
643
701
|
const symbol = defs[0] || "";
|
|
644
|
-
const role = ((
|
|
702
|
+
const role = ((_q = r.role) !== null && _q !== void 0 ? _q : "")
|
|
645
703
|
.slice(0, 4)
|
|
646
704
|
.toUpperCase();
|
|
647
705
|
let hint = "";
|
|
@@ -650,7 +708,7 @@ Examples:
|
|
|
650
708
|
}
|
|
651
709
|
else {
|
|
652
710
|
// Extract first meaningful signature line from content
|
|
653
|
-
const raw = (
|
|
711
|
+
const raw = (_s = (_r = r.content) !== null && _r !== void 0 ? _r : r.text) !== null && _s !== void 0 ? _s : "";
|
|
654
712
|
const lines = raw.split("\n");
|
|
655
713
|
for (const line of lines) {
|
|
656
714
|
const trimmed = line.trim();
|
|
@@ -685,19 +743,26 @@ Examples:
|
|
|
685
743
|
}
|
|
686
744
|
const sym = symbol ? ` ${symbol}` : "";
|
|
687
745
|
const rl = role ? ` [${role}]` : "";
|
|
746
|
+
const score = r.score;
|
|
747
|
+
const scoreCol = typeof score === "number" ? `\ts=${score.toFixed(3)}` : "";
|
|
688
748
|
const explainSuffix = options.explain && r.scoreBreakdown
|
|
689
749
|
? `\texplain:rerank=${r.scoreBreakdown.rerank.toFixed(3)},fused=${r.scoreBreakdown.fused.toFixed(3)},boost=${r.scoreBreakdown.boost.toFixed(2)}x,score=${r.scoreBreakdown.normalized.toFixed(3)}`
|
|
690
750
|
: "";
|
|
691
|
-
console.log(`${relPath}:${startLine}${sym}${rl}${hint}${explainSuffix}`);
|
|
751
|
+
console.log(`${relPath}:${startLine}${scoreCol}${sym}${rl}${hint}${explainSuffix}`);
|
|
692
752
|
}
|
|
693
753
|
}
|
|
694
754
|
// Agent trace (compact)
|
|
695
|
-
if (options.symbol &&
|
|
755
|
+
if (options.symbol && filteredData.length > 0) {
|
|
696
756
|
try {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
757
|
+
let graph = precomputedGraph;
|
|
758
|
+
if (!graph) {
|
|
759
|
+
if (!vectorDb)
|
|
760
|
+
throw new Error("no graph source");
|
|
761
|
+
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
762
|
+
const builder = new GraphBuilder(vectorDb, effectiveRoot);
|
|
763
|
+
graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
764
|
+
}
|
|
765
|
+
if (graph === null || graph === void 0 ? void 0 : graph.center) {
|
|
701
766
|
console.log("---");
|
|
702
767
|
for (const t of graph.callerTree) {
|
|
703
768
|
const rel = t.node.file.startsWith(effectiveRoot)
|
|
@@ -715,12 +780,12 @@ Examples:
|
|
|
715
780
|
}
|
|
716
781
|
}
|
|
717
782
|
}
|
|
718
|
-
catch (
|
|
783
|
+
catch (_12) { }
|
|
719
784
|
}
|
|
720
785
|
return;
|
|
721
786
|
}
|
|
722
787
|
if (options.skeleton) {
|
|
723
|
-
yield outputSkeletons(filteredData, projectRoot, parseInt(options.m, 10), vectorDb);
|
|
788
|
+
yield outputSkeletons(filteredData, projectRoot, parseInt(options.m, 10), vectorDb, precomputedSkeletons);
|
|
724
789
|
return;
|
|
725
790
|
}
|
|
726
791
|
if (!filteredData.length) {
|
|
@@ -747,9 +812,9 @@ Examples:
|
|
|
747
812
|
let shown = 0;
|
|
748
813
|
console.log(resultCountHeader(filteredData, parseInt(options.m, 10)));
|
|
749
814
|
for (const r of filteredData) {
|
|
750
|
-
const absP = (
|
|
751
|
-
const startLine = (
|
|
752
|
-
const endLine = (
|
|
815
|
+
const absP = (_v = (_t = r.path) !== null && _t !== void 0 ? _t : (_u = r.metadata) === null || _u === void 0 ? void 0 : _u.path) !== null && _v !== void 0 ? _v : "";
|
|
816
|
+
const startLine = (_z = (_x = (_w = r.startLine) !== null && _w !== void 0 ? _w : r.start_line) !== null && _x !== void 0 ? _x : (_y = r.generated_metadata) === null || _y === void 0 ? void 0 : _y.start_line) !== null && _z !== void 0 ? _z : 0;
|
|
817
|
+
const endLine = (_3 = (_1 = (_0 = r.endLine) !== null && _0 !== void 0 ? _0 : r.end_line) !== null && _1 !== void 0 ? _1 : (_2 = r.generated_metadata) === null || _2 === void 0 ? void 0 : _2.end_line) !== null && _3 !== void 0 ? _3 : startLine;
|
|
753
818
|
const relPath = absP.startsWith(projectRoot)
|
|
754
819
|
? absP.slice(projectRoot.length + 1)
|
|
755
820
|
: absP;
|
|
@@ -782,7 +847,7 @@ Examples:
|
|
|
782
847
|
tokensUsed += blobTokens;
|
|
783
848
|
shown++;
|
|
784
849
|
}
|
|
785
|
-
catch (
|
|
850
|
+
catch (_13) {
|
|
786
851
|
console.log(`\n--- ${relPath} (file not readable) ---`);
|
|
787
852
|
shown++;
|
|
788
853
|
}
|
|
@@ -799,7 +864,7 @@ Examples:
|
|
|
799
864
|
if (options.imports) {
|
|
800
865
|
const seenFiles = new Set();
|
|
801
866
|
for (const r of filteredData) {
|
|
802
|
-
const absP = (
|
|
867
|
+
const absP = (_6 = (_4 = r.path) !== null && _4 !== void 0 ? _4 : (_5 = r.metadata) === null || _5 === void 0 ? void 0 : _5.path) !== null && _6 !== void 0 ? _6 : "";
|
|
803
868
|
if (absP && !seenFiles.has(absP)) {
|
|
804
869
|
seenFiles.add(absP);
|
|
805
870
|
const imports = getImportsForFile(absP);
|
|
@@ -826,7 +891,7 @@ Examples:
|
|
|
826
891
|
for (const r of filteredData) {
|
|
827
892
|
const b = r.scoreBreakdown;
|
|
828
893
|
if (b) {
|
|
829
|
-
const absP = (
|
|
894
|
+
const absP = (_9 = (_7 = r.path) !== null && _7 !== void 0 ? _7 : (_8 = r.metadata) === null || _8 === void 0 ? void 0 : _8.path) !== null && _9 !== void 0 ? _9 : "";
|
|
830
895
|
const relPath = absP.startsWith(projectRoot)
|
|
831
896
|
? absP.slice(projectRoot.length + 1)
|
|
832
897
|
: absP;
|
|
@@ -845,12 +910,17 @@ Examples:
|
|
|
845
910
|
console.log(output);
|
|
846
911
|
}
|
|
847
912
|
// Symbol mode: append call graph
|
|
848
|
-
if (options.symbol
|
|
913
|
+
if (options.symbol) {
|
|
849
914
|
try {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
915
|
+
let graph = precomputedGraph;
|
|
916
|
+
if (!graph) {
|
|
917
|
+
if (!vectorDb)
|
|
918
|
+
throw new Error("no graph source");
|
|
919
|
+
const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../lib/graph/graph-builder")));
|
|
920
|
+
const builder = new GraphBuilder(vectorDb, effectiveRoot);
|
|
921
|
+
graph = yield builder.buildGraphMultiHop(pattern, 1);
|
|
922
|
+
}
|
|
923
|
+
if (graph === null || graph === void 0 ? void 0 : graph.center) {
|
|
854
924
|
const lines = ["\n--- Call graph ---"];
|
|
855
925
|
const centerRel = path.relative(effectiveRoot, graph.center.file);
|
|
856
926
|
lines.push(`${graph.center.symbol} [${graph.center.role}] ${centerRel}:${graph.center.line + 1}`);
|
|
@@ -883,7 +953,7 @@ Examples:
|
|
|
883
953
|
console.log(lines.join("\n"));
|
|
884
954
|
}
|
|
885
955
|
}
|
|
886
|
-
catch (
|
|
956
|
+
catch (_14) {
|
|
887
957
|
// Trace failed — skip silently
|
|
888
958
|
}
|
|
889
959
|
}
|
|
@@ -903,13 +973,13 @@ Examples:
|
|
|
903
973
|
source: "cli",
|
|
904
974
|
tool: "search",
|
|
905
975
|
query: pattern,
|
|
906
|
-
project: (
|
|
976
|
+
project: (_10 = (0, project_root_1.findProjectRoot)(root)) !== null && _10 !== void 0 ? _10 : root,
|
|
907
977
|
results: _searchResultCount,
|
|
908
978
|
ms: Date.now() - _searchStartMs,
|
|
909
979
|
error: _searchError,
|
|
910
980
|
});
|
|
911
981
|
}
|
|
912
|
-
catch (
|
|
982
|
+
catch (_15) { }
|
|
913
983
|
if (vectorDb) {
|
|
914
984
|
try {
|
|
915
985
|
yield vectorDb.close();
|
|
@@ -61,7 +61,6 @@ const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
|
61
61
|
const retriever_1 = require("../lib/skeleton/retriever");
|
|
62
62
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
63
63
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
64
|
-
const file_utils_1 = require("../lib/utils/file-utils");
|
|
65
64
|
const exit_1 = require("../lib/utils/exit");
|
|
66
65
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
67
66
|
const project_root_1 = require("../lib/utils/project-root");
|
|
@@ -156,27 +155,18 @@ Examples:
|
|
|
156
155
|
};
|
|
157
156
|
// Determine mode based on target
|
|
158
157
|
const resolvedTarget = path.resolve(target);
|
|
159
|
-
// Directory mode
|
|
158
|
+
// Directory mode is unsupported. Auto-picking files from a directory
|
|
159
|
+
// was confusingly magical (and on '.' it fell through to the resolver
|
|
160
|
+
// path and skeletonized .gitignore). Refuse and point at the file form.
|
|
160
161
|
if (fs.existsSync(resolvedTarget) &&
|
|
161
162
|
fs.statSync(resolvedTarget).isDirectory()) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.slice(0, Number.parseInt(options.limit, 10));
|
|
170
|
-
if (files.length === 0) {
|
|
171
|
-
console.error(`No indexable files in ${target}`);
|
|
172
|
-
process.exitCode = 1;
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
for (const filePath of files) {
|
|
176
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
177
|
-
const result = yield skeletonizer.skeletonizeFile(filePath, content, skeletonOpts);
|
|
178
|
-
outputResult(result, options);
|
|
179
|
-
}
|
|
163
|
+
console.error([
|
|
164
|
+
"skeleton expects a file or symbol, not a directory.",
|
|
165
|
+
"Try:",
|
|
166
|
+
" gmax skeleton src/foo.ts # one file's structure",
|
|
167
|
+
' gmax search "<topic>" --agent # find relevant files first',
|
|
168
|
+
].join("\n"));
|
|
169
|
+
process.exitCode = 1;
|
|
180
170
|
return;
|
|
181
171
|
}
|
|
182
172
|
// Batch mode (comma-separated)
|