grepmax 0.9.4 → 0.9.5
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/mcp.js +5 -5
- package/dist/commands/project.js +40 -21
- package/dist/commands/recent.js +20 -7
- package/dist/commands/related.js +40 -21
- package/dist/commands/search.js +42 -15
- package/dist/commands/status.js +35 -6
- package/dist/commands/symbols.js +29 -2
- package/dist/commands/trace.js +33 -1
- package/dist/lib/output/formatter.js +16 -2
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +1 -1
- package/plugins/grepmax/skills/grepmax/SKILL.md +13 -3
package/dist/commands/mcp.js
CHANGED
|
@@ -441,7 +441,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
441
441
|
}
|
|
442
442
|
const result = yield searcher.search(query, limit, { rerank: true }, Object.keys(filters).length > 0 ? filters : undefined, pathPrefix);
|
|
443
443
|
if (!result.data || result.data.length === 0) {
|
|
444
|
-
return ok("No matches found.");
|
|
444
|
+
return ok("No matches found. Try broadening your query, using fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
445
445
|
}
|
|
446
446
|
const minScore = typeof args.min_score === "number" ? args.min_score : 0;
|
|
447
447
|
const maxPerFile = typeof args.max_per_file === "number" ? args.max_per_file : 0;
|
|
@@ -743,7 +743,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
743
743
|
const depth = Math.min(Math.max(Number(args.depth) || 1, 1), 3);
|
|
744
744
|
const graph = yield builder.buildGraphMultiHop(symbol, depth);
|
|
745
745
|
if (!graph.center) {
|
|
746
|
-
return ok(`Symbol '${symbol}' not found in the index.`);
|
|
746
|
+
return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`);
|
|
747
747
|
}
|
|
748
748
|
const lines = [];
|
|
749
749
|
// Center
|
|
@@ -870,7 +870,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
870
870
|
})
|
|
871
871
|
.slice(0, limit);
|
|
872
872
|
if (entries.length === 0) {
|
|
873
|
-
return ok("No symbols found. Run
|
|
873
|
+
return ok("No symbols found. Run `gmax status` to verify the project is indexed, or `gmax index` to rebuild.");
|
|
874
874
|
}
|
|
875
875
|
const lines = entries.map((e) => {
|
|
876
876
|
const rel = e.path.startsWith(projectRoot)
|
|
@@ -1118,7 +1118,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1118
1118
|
.where(`path = '${(0, filter_builder_1.escapeSqlString)(absPath)}'`)
|
|
1119
1119
|
.toArray();
|
|
1120
1120
|
if (fileChunks.length === 0) {
|
|
1121
|
-
return ok(`File not found in index: ${file}
|
|
1121
|
+
return ok(`File not found in index: ${file}. Check that the path is relative to the project root. Run \`gmax status\` to see indexed projects.`);
|
|
1122
1122
|
}
|
|
1123
1123
|
const definedHere = new Set();
|
|
1124
1124
|
const referencedHere = new Set();
|
|
@@ -1236,7 +1236,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1236
1236
|
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1237
1237
|
const top = files.slice(0, limit);
|
|
1238
1238
|
if (top.length === 0) {
|
|
1239
|
-
return ok(`No indexed files found for ${root}
|
|
1239
|
+
return ok(`No indexed files found for ${root}. Run \`gmax add\` to register and index this project.`);
|
|
1240
1240
|
}
|
|
1241
1241
|
const now = Date.now();
|
|
1242
1242
|
const lines = [
|
package/dist/commands/project.js
CHANGED
|
@@ -59,8 +59,9 @@ function toArr(val) {
|
|
|
59
59
|
exports.project = new commander_1.Command("project")
|
|
60
60
|
.description("Show project overview — languages, structure, key symbols")
|
|
61
61
|
.option("--root <dir>", "Project root (defaults to current directory)")
|
|
62
|
+
.option("--agent", "Compact output for AI agents", false)
|
|
62
63
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
|
-
var _a, _b, _c;
|
|
64
|
+
var _a, _b, _c, _d;
|
|
64
65
|
let vectorDb = null;
|
|
65
66
|
try {
|
|
66
67
|
const root = opts.root
|
|
@@ -86,6 +87,7 @@ exports.project = new commander_1.Command("project")
|
|
|
86
87
|
.toArray();
|
|
87
88
|
if (rows.length === 0) {
|
|
88
89
|
console.log(`No indexed data found for ${root}. Run: gmax index --path ${root}`);
|
|
90
|
+
process.exitCode = 1;
|
|
89
91
|
return;
|
|
90
92
|
}
|
|
91
93
|
const files = new Set();
|
|
@@ -128,33 +130,50 @@ exports.project = new commander_1.Command("project")
|
|
|
128
130
|
}
|
|
129
131
|
const projects = (0, project_registry_1.listProjects)();
|
|
130
132
|
const proj = projects.find((p) => p.root === root);
|
|
131
|
-
console.log(`Project: ${projectName} (${root})`);
|
|
132
|
-
console.log(`Last indexed: ${(_c = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _c !== void 0 ? _c : "unknown"} • ${rows.length} chunks • ${files.size} files\n`);
|
|
133
133
|
const extEntries = Array.from(extCounts.entries())
|
|
134
134
|
.sort((a, b) => b[1] - a[1])
|
|
135
135
|
.slice(0, 8);
|
|
136
|
-
console.log(`Languages: ${extEntries.map(([ext, count]) => `${ext} (${Math.round((count / rows.length) * 100)}%)`).join(", ")}\n`);
|
|
137
|
-
console.log("Directory structure:");
|
|
138
|
-
for (const [dir, data] of Array.from(dirCounts.entries())
|
|
139
|
-
.sort((a, b) => b[1].chunks - a[1].chunks)
|
|
140
|
-
.slice(0, 12)) {
|
|
141
|
-
console.log(` ${dir.padEnd(25)} (${data.files.size} files, ${data.chunks} chunks)`);
|
|
142
|
-
}
|
|
143
|
-
const roleEntries = Array.from(roleCounts.entries()).sort((a, b) => b[1] - a[1]);
|
|
144
|
-
console.log(`\nRoles: ${roleEntries.map(([r, c]) => `${Math.round((c / rows.length) * 100)}% ${r}`).join(", ")}\n`);
|
|
145
136
|
const topSymbols = Array.from(symbolRefs.entries())
|
|
146
137
|
.sort((a, b) => b[1] - a[1])
|
|
147
138
|
.slice(0, 8);
|
|
148
|
-
if (
|
|
149
|
-
console.log(
|
|
150
|
-
|
|
151
|
-
|
|
139
|
+
if (opts.agent) {
|
|
140
|
+
console.log(`name\t${projectName}`);
|
|
141
|
+
console.log(`root\t${root}`);
|
|
142
|
+
console.log(`chunks\t${rows.length}`);
|
|
143
|
+
console.log(`files\t${files.size}`);
|
|
144
|
+
console.log(`last_indexed\t${(_c = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _c !== void 0 ? _c : "unknown"}`);
|
|
145
|
+
console.log(`languages\t${extEntries.map(([ext]) => ext).join(",")}`);
|
|
146
|
+
console.log(`top_dirs\t${Array.from(dirCounts.entries()).sort((a, b) => b[1].chunks - a[1].chunks).slice(0, 8).map(([d]) => d).join(",")}`);
|
|
147
|
+
if (topSymbols.length > 0) {
|
|
148
|
+
console.log(`key_symbols\t${topSymbols.map(([s]) => s).join(",")}`);
|
|
149
|
+
}
|
|
150
|
+
if (entryPoints.length > 0) {
|
|
151
|
+
console.log(`entry_points\t${entryPoints.slice(0, 10).map((e) => e.symbol).join(",")}`);
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
console.log(
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
else {
|
|
155
|
+
console.log(`Project: ${projectName} (${root})`);
|
|
156
|
+
console.log(`Last indexed: ${(_d = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _d !== void 0 ? _d : "unknown"} • ${rows.length} chunks • ${files.size} files\n`);
|
|
157
|
+
console.log(`Languages: ${extEntries.map(([ext, count]) => `${ext} (${Math.round((count / rows.length) * 100)}%)`).join(", ")}\n`);
|
|
158
|
+
console.log("Directory structure:");
|
|
159
|
+
for (const [dir, data] of Array.from(dirCounts.entries())
|
|
160
|
+
.sort((a, b) => b[1].chunks - a[1].chunks)
|
|
161
|
+
.slice(0, 12)) {
|
|
162
|
+
console.log(` ${dir.padEnd(25)} (${data.files.size} files, ${data.chunks} chunks)`);
|
|
163
|
+
}
|
|
164
|
+
const roleEntries = Array.from(roleCounts.entries()).sort((a, b) => b[1] - a[1]);
|
|
165
|
+
console.log(`\nRoles: ${roleEntries.map(([r, c]) => `${Math.round((c / rows.length) * 100)}% ${r}`).join(", ")}\n`);
|
|
166
|
+
if (topSymbols.length > 0) {
|
|
167
|
+
console.log("Key symbols (by reference count):");
|
|
168
|
+
for (const [sym, count] of topSymbols) {
|
|
169
|
+
console.log(` ${sym.padEnd(25)} (referenced ${count}x)`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (entryPoints.length > 0) {
|
|
173
|
+
console.log("\nEntry points (exported orchestration):");
|
|
174
|
+
for (const ep of entryPoints.slice(0, 10)) {
|
|
175
|
+
console.log(` ${ep.symbol.padEnd(25)} ${ep.path}`);
|
|
176
|
+
}
|
|
158
177
|
}
|
|
159
178
|
}
|
|
160
179
|
}
|
|
@@ -168,7 +187,7 @@ exports.project = new commander_1.Command("project")
|
|
|
168
187
|
try {
|
|
169
188
|
yield vectorDb.close();
|
|
170
189
|
}
|
|
171
|
-
catch (
|
|
190
|
+
catch (_e) { }
|
|
172
191
|
}
|
|
173
192
|
yield (0, exit_1.gracefulExit)();
|
|
174
193
|
}
|
package/dist/commands/recent.js
CHANGED
|
@@ -61,6 +61,7 @@ exports.recent = new commander_1.Command("recent")
|
|
|
61
61
|
.description("Show recently modified indexed files")
|
|
62
62
|
.option("-l, --limit <n>", "Max files (default 20)", "20")
|
|
63
63
|
.option("--root <dir>", "Project root (defaults to current directory)")
|
|
64
|
+
.option("--agent", "Compact output for AI agents", false)
|
|
64
65
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
66
|
var _a, e_1, _b, _c;
|
|
66
67
|
var _d, _e;
|
|
@@ -94,16 +95,28 @@ exports.recent = new commander_1.Command("recent")
|
|
|
94
95
|
const top = files.slice(0, limit);
|
|
95
96
|
if (top.length === 0) {
|
|
96
97
|
console.log(`No indexed files found for ${root}`);
|
|
98
|
+
console.log("\nTry: `gmax add` to register and index this project, or `gmax status` to see what's indexed.");
|
|
99
|
+
process.exitCode = 1;
|
|
97
100
|
return;
|
|
98
101
|
}
|
|
99
102
|
const now = Date.now();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
if (opts.agent) {
|
|
104
|
+
for (const f of top) {
|
|
105
|
+
const rel = f.path.startsWith(prefix)
|
|
106
|
+
? f.path.slice(prefix.length)
|
|
107
|
+
: f.path;
|
|
108
|
+
console.log(`${rel}\t${(0, format_helpers_1.formatTimeAgo)(now - f.mtimeMs)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(`Recent changes in ${path.basename(root)} (${top.length} most recent):\n`);
|
|
113
|
+
for (const f of top) {
|
|
114
|
+
const rel = f.path.startsWith(prefix)
|
|
115
|
+
? f.path.slice(prefix.length)
|
|
116
|
+
: f.path;
|
|
117
|
+
const ago = (0, format_helpers_1.formatTimeAgo)(now - f.mtimeMs);
|
|
118
|
+
console.log(` ${ago.padEnd(10)} ${rel}`);
|
|
119
|
+
}
|
|
107
120
|
}
|
|
108
121
|
}
|
|
109
122
|
finally {
|
package/dist/commands/related.js
CHANGED
|
@@ -60,6 +60,7 @@ exports.related = new commander_1.Command("related")
|
|
|
60
60
|
.argument("<file>", "File path relative to project root")
|
|
61
61
|
.option("-l, --limit <n>", "Max results per direction (default 10)", "10")
|
|
62
62
|
.option("--root <dir>", "Project root directory")
|
|
63
|
+
.option("--agent", "Compact output for AI agents", false)
|
|
63
64
|
.action((file, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
64
65
|
var _a;
|
|
65
66
|
const limit = Math.min(Math.max(Number.parseInt(opts.limit || "10", 10), 1), 25);
|
|
@@ -79,6 +80,8 @@ exports.related = new commander_1.Command("related")
|
|
|
79
80
|
.toArray();
|
|
80
81
|
if (fileChunks.length === 0) {
|
|
81
82
|
console.log(`File not found in index: ${file}`);
|
|
83
|
+
console.log("\nCheck that the path is relative to the project root. Run `gmax status` to see indexed projects.");
|
|
84
|
+
process.exitCode = 1;
|
|
82
85
|
return;
|
|
83
86
|
}
|
|
84
87
|
const definedHere = new Set();
|
|
@@ -123,37 +126,53 @@ exports.related = new commander_1.Command("related")
|
|
|
123
126
|
revCounts.set(p, (revCounts.get(p) || 0) + 1);
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
|
-
console.log(`Related files for ${file}:\n`);
|
|
127
129
|
const topDeps = Array.from(depCounts.entries())
|
|
128
130
|
.sort((a, b) => b[1] - a[1])
|
|
129
131
|
.slice(0, limit);
|
|
130
|
-
if (topDeps.length > 0) {
|
|
131
|
-
console.log("Dependencies (files this imports/calls):");
|
|
132
|
-
for (const [p, count] of topDeps) {
|
|
133
|
-
const rel = p.startsWith(`${projectRoot}/`)
|
|
134
|
-
? p.slice(projectRoot.length + 1)
|
|
135
|
-
: p;
|
|
136
|
-
console.log(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
console.log("Dependencies: none found");
|
|
141
|
-
}
|
|
142
|
-
console.log("");
|
|
143
132
|
const topRevs = Array.from(revCounts.entries())
|
|
144
133
|
.sort((a, b) => b[1] - a[1])
|
|
145
134
|
.slice(0, limit);
|
|
146
|
-
if (
|
|
147
|
-
|
|
135
|
+
if (opts.agent) {
|
|
136
|
+
const rel = (p) => p.startsWith(`${projectRoot}/`)
|
|
137
|
+
? p.slice(projectRoot.length + 1)
|
|
138
|
+
: p;
|
|
139
|
+
for (const [p, count] of topDeps) {
|
|
140
|
+
console.log(`dep: ${rel(p)}\t${count}`);
|
|
141
|
+
}
|
|
148
142
|
for (const [p, count] of topRevs) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
console.log(
|
|
143
|
+
console.log(`rev: ${rel(p)}\t${count}`);
|
|
144
|
+
}
|
|
145
|
+
if (!topDeps.length && !topRevs.length) {
|
|
146
|
+
console.log("(none)");
|
|
153
147
|
}
|
|
154
148
|
}
|
|
155
149
|
else {
|
|
156
|
-
console.log(
|
|
150
|
+
console.log(`Related files for ${file}:\n`);
|
|
151
|
+
if (topDeps.length > 0) {
|
|
152
|
+
console.log("Dependencies (files this imports/calls):");
|
|
153
|
+
for (const [p, count] of topDeps) {
|
|
154
|
+
const rel = p.startsWith(`${projectRoot}/`)
|
|
155
|
+
? p.slice(projectRoot.length + 1)
|
|
156
|
+
: p;
|
|
157
|
+
console.log(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log("Dependencies: none found");
|
|
162
|
+
}
|
|
163
|
+
console.log("");
|
|
164
|
+
if (topRevs.length > 0) {
|
|
165
|
+
console.log("Dependents (files that call this):");
|
|
166
|
+
for (const [p, count] of topRevs) {
|
|
167
|
+
const rel = p.startsWith(`${projectRoot}/`)
|
|
168
|
+
? p.slice(projectRoot.length + 1)
|
|
169
|
+
: p;
|
|
170
|
+
console.log(` ${rel.padEnd(40)} (${count} shared symbol${count > 1 ? "s" : ""})`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
console.log("Dependents: none found");
|
|
175
|
+
}
|
|
157
176
|
}
|
|
158
177
|
}
|
|
159
178
|
catch (error) {
|
package/dist/commands/search.js
CHANGED
|
@@ -272,6 +272,8 @@ function outputSkeletons(results, projectRoot, limit, db) {
|
|
|
272
272
|
}
|
|
273
273
|
if (filesToProcess.length === 0) {
|
|
274
274
|
console.log("No skeleton matches found.");
|
|
275
|
+
console.log("\nTry: broaden your query, or use `gmax skeleton <path>` to view a specific file's structure.");
|
|
276
|
+
process.exitCode = 1;
|
|
275
277
|
return;
|
|
276
278
|
}
|
|
277
279
|
// Reuse or init skeletonizer for fallbacks
|
|
@@ -326,6 +328,19 @@ function outputSkeletons(results, projectRoot, limit, db) {
|
|
|
326
328
|
}
|
|
327
329
|
});
|
|
328
330
|
}
|
|
331
|
+
function resultCountHeader(results, maxCount) {
|
|
332
|
+
var _a, _b, _c;
|
|
333
|
+
const files = new Set();
|
|
334
|
+
for (const r of results) {
|
|
335
|
+
const p = (_c = (_a = r.path) !== null && _a !== void 0 ? _a : (_b = r.metadata) === null || _b === void 0 ? void 0 : _b.path) !== null && _c !== void 0 ? _c : "";
|
|
336
|
+
if (p)
|
|
337
|
+
files.add(p);
|
|
338
|
+
}
|
|
339
|
+
const showing = results.length < maxCount
|
|
340
|
+
? `${results.length}`
|
|
341
|
+
: `top ${results.length}`;
|
|
342
|
+
return `Found ${results.length} match${results.length === 1 ? "" : "es"} (showing ${showing}) across ${files.size} file${files.size === 1 ? "" : "s"}`;
|
|
343
|
+
}
|
|
329
344
|
exports.search = new commander_1.Command("search")
|
|
330
345
|
.description("Search code by meaning (default command)")
|
|
331
346
|
.option("-m <max_count>, --max-count <max_count>", "The maximum number of results to return (total)", "5")
|
|
@@ -403,21 +418,31 @@ Examples:
|
|
|
403
418
|
? toCompactHits(filteredData)
|
|
404
419
|
: [];
|
|
405
420
|
if (options.compact) {
|
|
406
|
-
|
|
407
|
-
|
|
421
|
+
if (!compactHits.length) {
|
|
422
|
+
console.log("No matches found.");
|
|
423
|
+
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
424
|
+
process.exitCode = 1;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
console.log(formatCompactTable(compactHits, projectRootForServer, pattern, {
|
|
408
428
|
isTTY: !!process.stdout.isTTY,
|
|
409
429
|
plain: !!options.plain,
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
console.log(compactText);
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
413
432
|
return; // EXIT
|
|
414
433
|
}
|
|
415
434
|
if (!filteredData.length) {
|
|
416
435
|
console.log("No matches found.");
|
|
436
|
+
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
437
|
+
process.exitCode = 1;
|
|
417
438
|
return; // EXIT
|
|
418
439
|
}
|
|
419
440
|
const isTTY = process.stdout.isTTY;
|
|
420
441
|
const shouldBePlain = options.plain || !isTTY;
|
|
442
|
+
if (!options.agent && !options.compact) {
|
|
443
|
+
console.log(resultCountHeader(filteredData, parseInt(options.m, 10)));
|
|
444
|
+
console.log();
|
|
445
|
+
}
|
|
421
446
|
if (shouldBePlain) {
|
|
422
447
|
const mappedResults = toTextResults(filteredData);
|
|
423
448
|
const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRootForServer, {
|
|
@@ -592,6 +617,7 @@ Examples:
|
|
|
592
617
|
if (options.agent) {
|
|
593
618
|
if (!filteredData.length) {
|
|
594
619
|
console.log("(none)");
|
|
620
|
+
process.exitCode = 1;
|
|
595
621
|
}
|
|
596
622
|
else {
|
|
597
623
|
// In agent mode, print imports header per file
|
|
@@ -685,25 +711,26 @@ Examples:
|
|
|
685
711
|
yield outputSkeletons(filteredData, projectRoot, parseInt(options.m, 10), vectorDb);
|
|
686
712
|
return;
|
|
687
713
|
}
|
|
688
|
-
const compactHits = options.compact ? toCompactHits(filteredData) : [];
|
|
689
|
-
const compactText = options.compact && compactHits.length
|
|
690
|
-
? formatCompactTable(compactHits, projectRoot, pattern, {
|
|
691
|
-
isTTY: !!process.stdout.isTTY,
|
|
692
|
-
plain: !!options.plain,
|
|
693
|
-
})
|
|
694
|
-
: options.compact
|
|
695
|
-
? "No matches found."
|
|
696
|
-
: "";
|
|
697
714
|
if (!filteredData.length) {
|
|
698
715
|
console.log("No matches found.");
|
|
716
|
+
console.log("\nTry: broaden your query, use fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
717
|
+
process.exitCode = 1;
|
|
699
718
|
return;
|
|
700
719
|
}
|
|
701
720
|
if (options.compact) {
|
|
702
|
-
|
|
721
|
+
const compactHits = toCompactHits(filteredData);
|
|
722
|
+
console.log(formatCompactTable(compactHits, projectRoot, pattern, {
|
|
723
|
+
isTTY: !!process.stdout.isTTY,
|
|
724
|
+
plain: !!options.plain,
|
|
725
|
+
}));
|
|
703
726
|
return;
|
|
704
727
|
}
|
|
705
728
|
const isTTY = process.stdout.isTTY;
|
|
706
729
|
const shouldBePlain = options.plain || !isTTY;
|
|
730
|
+
if (!options.agent && !options.compact) {
|
|
731
|
+
console.log(resultCountHeader(filteredData, parseInt(options.m, 10)));
|
|
732
|
+
console.log();
|
|
733
|
+
}
|
|
707
734
|
// Print imports per unique file before results when --imports is used
|
|
708
735
|
if (options.imports) {
|
|
709
736
|
const seenFiles = new Set();
|
package/dist/commands/status.js
CHANGED
|
@@ -91,21 +91,50 @@ function formatChunks(n) {
|
|
|
91
91
|
}
|
|
92
92
|
exports.status = new commander_1.Command("status")
|
|
93
93
|
.description("Show gmax index status for all projects")
|
|
94
|
+
.option("--agent", "Compact output for AI agents", false)
|
|
94
95
|
.addHelpText("after", `
|
|
95
96
|
Examples:
|
|
96
97
|
gmax status Show status of all indexed projects
|
|
97
98
|
`)
|
|
98
|
-
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
99
|
-
var _a;
|
|
99
|
+
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
100
|
+
var _a, _b;
|
|
100
101
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
101
102
|
const projects = (0, project_registry_1.listProjects)();
|
|
102
103
|
(0, watcher_store_1.listWatchers)(); // cleans stale entries as side effect
|
|
103
104
|
const indexing = (0, lock_1.isLocked)(config_1.PATHS.globalRoot);
|
|
104
105
|
const currentRoot = (0, project_root_1.findProjectRoot)(process.cwd());
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
if (!opts.agent) {
|
|
107
|
+
// Header
|
|
108
|
+
console.log(`\n${style.bold("gmax")} · ${globalConfig.modelTier} (${globalConfig.vectorDim}d, ${globalConfig.embedMode})${indexing ? style.yellow(" · indexing...") : ""}`);
|
|
109
|
+
}
|
|
107
110
|
if (projects.length === 0) {
|
|
108
|
-
|
|
111
|
+
if (opts.agent) {
|
|
112
|
+
console.log("(none)");
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.log(`\nNo projects added yet. Run ${style.cyan("gmax add")} to get started.\n`);
|
|
116
|
+
}
|
|
117
|
+
yield (0, exit_1.gracefulExit)();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (opts.agent) {
|
|
121
|
+
for (const project of projects) {
|
|
122
|
+
const watcher = (0, watcher_store_1.getWatcherForProject)(project.root);
|
|
123
|
+
const projectStatus = (_a = project.status) !== null && _a !== void 0 ? _a : "indexed";
|
|
124
|
+
let st;
|
|
125
|
+
if (projectStatus === "pending")
|
|
126
|
+
st = "pending";
|
|
127
|
+
else if (projectStatus === "error")
|
|
128
|
+
st = "error";
|
|
129
|
+
else if ((watcher === null || watcher === void 0 ? void 0 : watcher.status) === "syncing")
|
|
130
|
+
st = "indexing";
|
|
131
|
+
else if (watcher)
|
|
132
|
+
st = "watching";
|
|
133
|
+
else
|
|
134
|
+
st = "idle";
|
|
135
|
+
const isCurrent = project.root === currentRoot;
|
|
136
|
+
console.log(`${project.name}\t${formatChunks(project.chunkCount)}\t${formatAge(project.lastIndexed)}\t${st}${isCurrent ? "\tcurrent" : ""}`);
|
|
137
|
+
}
|
|
109
138
|
yield (0, exit_1.gracefulExit)();
|
|
110
139
|
return;
|
|
111
140
|
}
|
|
@@ -117,7 +146,7 @@ Examples:
|
|
|
117
146
|
const watcher = (0, watcher_store_1.getWatcherForProject)(project.root);
|
|
118
147
|
// Status column
|
|
119
148
|
let statusStr;
|
|
120
|
-
const projectStatus = (
|
|
149
|
+
const projectStatus = (_b = project.status) !== null && _b !== void 0 ? _b : "indexed";
|
|
121
150
|
if (projectStatus === "pending") {
|
|
122
151
|
statusStr = style.yellow("pending");
|
|
123
152
|
}
|
package/dist/commands/symbols.js
CHANGED
|
@@ -123,7 +123,13 @@ function collectSymbols(options) {
|
|
|
123
123
|
}
|
|
124
124
|
function formatTable(entries) {
|
|
125
125
|
if (entries.length === 0) {
|
|
126
|
-
return
|
|
126
|
+
return [
|
|
127
|
+
"No symbols found.",
|
|
128
|
+
"",
|
|
129
|
+
"Try:",
|
|
130
|
+
" gmax status — verify the project is indexed",
|
|
131
|
+
" gmax index — rebuild the index",
|
|
132
|
+
].join("\n");
|
|
127
133
|
}
|
|
128
134
|
const rows = entries.map((e) => ({
|
|
129
135
|
symbol: e.symbol,
|
|
@@ -147,12 +153,25 @@ function formatTable(entries) {
|
|
|
147
153
|
];
|
|
148
154
|
return lines.join("\n");
|
|
149
155
|
}
|
|
156
|
+
function formatAgent(entries, projectRoot) {
|
|
157
|
+
if (entries.length === 0)
|
|
158
|
+
return "(none)";
|
|
159
|
+
return entries
|
|
160
|
+
.map((e) => {
|
|
161
|
+
const rel = e.path.startsWith(projectRoot)
|
|
162
|
+
? e.path.slice(projectRoot.length + 1)
|
|
163
|
+
: e.path;
|
|
164
|
+
return `${e.symbol}\t${rel}:${Math.max(1, e.line + 1)}\t${e.count}`;
|
|
165
|
+
})
|
|
166
|
+
.join("\n");
|
|
167
|
+
}
|
|
150
168
|
exports.symbols = new commander_1.Command("symbols")
|
|
151
169
|
.description("List indexed symbols and where they are defined")
|
|
152
170
|
.argument("[pattern]", "Optional pattern to filter symbols by name")
|
|
153
171
|
.option("-l, --limit <number>", "Max symbols to list (default 20)", "20")
|
|
154
172
|
.option("-p, --path <prefix>", "Only include symbols under this path prefix")
|
|
155
173
|
.option("--root <dir>", "Project root directory")
|
|
174
|
+
.option("--agent", "Compact output for AI agents", false)
|
|
156
175
|
.action((pattern, cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
176
|
var _a, _b;
|
|
158
177
|
const root = cmd.root ? path.resolve(cmd.root) : process.cwd();
|
|
@@ -166,6 +185,14 @@ exports.symbols = new commander_1.Command("symbols")
|
|
|
166
185
|
pathPrefix,
|
|
167
186
|
pattern: pattern,
|
|
168
187
|
});
|
|
169
|
-
|
|
188
|
+
if (cmd.agent) {
|
|
189
|
+
console.log(formatAgent(entries, projectRoot));
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.log(`${style.bold("Project")}: ${style.green(projectRoot)}\n${formatTable(entries)}`);
|
|
193
|
+
}
|
|
194
|
+
if (entries.length === 0) {
|
|
195
|
+
process.exitCode = 1;
|
|
196
|
+
}
|
|
170
197
|
yield (0, exit_1.gracefulExit)();
|
|
171
198
|
}));
|
package/dist/commands/trace.js
CHANGED
|
@@ -50,11 +50,35 @@ const formatter_1 = require("../lib/output/formatter");
|
|
|
50
50
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
51
51
|
const exit_1 = require("../lib/utils/exit");
|
|
52
52
|
const project_root_1 = require("../lib/utils/project-root");
|
|
53
|
+
function formatTraceAgent(graph, projectRoot) {
|
|
54
|
+
if (!graph.center)
|
|
55
|
+
return "(not found)";
|
|
56
|
+
const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
|
|
57
|
+
const lines = [];
|
|
58
|
+
lines.push(`${graph.center.symbol}\t${rel(graph.center.file)}:${graph.center.line}\t${graph.center.role}`);
|
|
59
|
+
function walkCallers(tree, depth) {
|
|
60
|
+
for (const t of tree) {
|
|
61
|
+
lines.push(`${" ".repeat(depth)}<- ${t.node.symbol}\t${rel(t.node.file)}:${t.node.line}`);
|
|
62
|
+
walkCallers(t.callers, depth + 1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
walkCallers(graph.callerTree, 0);
|
|
66
|
+
for (const c of graph.callees) {
|
|
67
|
+
if (c.file) {
|
|
68
|
+
lines.push(`-> ${c.symbol}\t${rel(c.file)}:${c.line}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
lines.push(`-> ${c.symbol}\t(not indexed)`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
53
76
|
exports.trace = new commander_1.Command("trace")
|
|
54
77
|
.description("Trace the call graph for a symbol")
|
|
55
78
|
.argument("<symbol>", "The symbol to trace")
|
|
56
79
|
.option("-d, --depth <n>", "Caller traversal depth (default 1, max 3)", "1")
|
|
57
80
|
.option("--root <dir>", "Project root directory")
|
|
81
|
+
.option("--agent", "Compact output for AI agents", false)
|
|
58
82
|
.action((symbol, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
83
|
var _a;
|
|
60
84
|
const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
|
|
@@ -66,7 +90,15 @@ exports.trace = new commander_1.Command("trace")
|
|
|
66
90
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
67
91
|
const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, projectRoot);
|
|
68
92
|
const graph = yield graphBuilder.buildGraphMultiHop(symbol, depth);
|
|
69
|
-
|
|
93
|
+
if (opts.agent) {
|
|
94
|
+
console.log(formatTraceAgent(graph, projectRoot));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log((0, formatter_1.formatTrace)(graph, { symbol }));
|
|
98
|
+
}
|
|
99
|
+
if (!graph.center) {
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
}
|
|
70
102
|
}
|
|
71
103
|
catch (error) {
|
|
72
104
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -125,9 +125,23 @@ function formatResults(results, root, options = {}) {
|
|
|
125
125
|
return "No results found.";
|
|
126
126
|
return results.map((r) => formatResult(r, root, options)).join("\n\n");
|
|
127
127
|
}
|
|
128
|
-
function formatTrace(graph) {
|
|
128
|
+
function formatTrace(graph, options) {
|
|
129
|
+
var _a;
|
|
129
130
|
if (!graph.center) {
|
|
130
|
-
|
|
131
|
+
const name = (_a = options === null || options === void 0 ? void 0 : options.symbol) !== null && _a !== void 0 ? _a : "unknown";
|
|
132
|
+
const lines = [
|
|
133
|
+
`Symbol not found: ${style.bold(name)}`,
|
|
134
|
+
"",
|
|
135
|
+
style.dim("Possible reasons:"),
|
|
136
|
+
style.dim(" • The symbol doesn't exist in any indexed project"),
|
|
137
|
+
style.dim(" • The containing file hasn't been indexed yet"),
|
|
138
|
+
style.dim(" • The name is spelled differently in the source"),
|
|
139
|
+
"",
|
|
140
|
+
style.dim("Try:"),
|
|
141
|
+
style.dim(" gmax status — see which projects are indexed"),
|
|
142
|
+
style.dim(" gmax search <name> — fuzzy search for similar symbols"),
|
|
143
|
+
];
|
|
144
|
+
return lines.join("\n");
|
|
131
145
|
}
|
|
132
146
|
const lines = [];
|
|
133
147
|
// 1. Importers
|
package/package.json
CHANGED
|
@@ -121,7 +121,7 @@ async function main() {
|
|
|
121
121
|
hookSpecificOutput: {
|
|
122
122
|
hookEventName: "SessionStart",
|
|
123
123
|
additionalContext:
|
|
124
|
-
'gmax ready. PREFER CLI over MCP — use Bash(gmax "query" --agent) for search (one line per result, 89% fewer tokens). Bash(gmax trace <symbol>) for call graphs. Bash(gmax skeleton <path>) for structure. Bash(gmax status) to check indexed projects.
|
|
124
|
+
'gmax ready. PREFER CLI over MCP — use Bash(gmax "query" --agent) for search (one line per result, 89% fewer tokens). Bash(gmax trace <symbol>) for call graphs. Bash(gmax skeleton <path>) for structure. Bash(gmax status) to check indexed projects. --agent flag works on search, trace, symbols, related, recent, status, project. If search says "not added yet", run Bash(gmax add).',
|
|
125
125
|
},
|
|
126
126
|
};
|
|
127
127
|
process.stdout.write(JSON.stringify(response));
|
|
@@ -40,7 +40,7 @@ If search returns "This project hasn't been added to gmax yet", run `Bash(gmax a
|
|
|
40
40
|
|
|
41
41
|
### Search — `gmax "query" --agent`
|
|
42
42
|
|
|
43
|
-
The `--agent` flag
|
|
43
|
+
The `--agent` flag produces compact, token-efficient output for AI agents. It is supported on: `search`, `trace`, `symbols`, `related`, `recent`, `status`, and `project`.
|
|
44
44
|
|
|
45
45
|
```
|
|
46
46
|
gmax "where do we handle authentication" --agent
|
|
@@ -70,6 +70,7 @@ This shows function signatures, what each calls, and complexity — enough to de
|
|
|
70
70
|
gmax trace handleAuth # 1-hop: callers + callees
|
|
71
71
|
gmax trace handleAuth -d 2 # 2-hop: callers-of-callers
|
|
72
72
|
gmax trace handleAuth --root ~/project # trace in a different project
|
|
73
|
+
gmax trace handleAuth --agent # compact: symbol\tpath:line, <- callers, -> callees
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
### Skeleton — `gmax skeleton <target>`
|
|
@@ -97,11 +98,20 @@ gmax related src/lib/index/syncer.ts --root ~/project
|
|
|
97
98
|
gmax recent # recently modified files
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
###
|
|
101
|
+
### Symbols — `gmax symbols`
|
|
101
102
|
```
|
|
102
103
|
gmax symbols # list indexed symbols
|
|
103
104
|
gmax symbols auth -p src/ --root ~/proj # filter by name, path, project
|
|
105
|
+
gmax symbols --agent # compact: symbol\tpath:line\tcount
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Other
|
|
109
|
+
```
|
|
104
110
|
gmax status # show all indexed projects
|
|
111
|
+
gmax status --agent # compact: name\tchunks\tage\tstatus
|
|
112
|
+
gmax recent --agent # compact: path\tage
|
|
113
|
+
gmax related src/file.ts --agent # compact: dep:/rev: path\tcount
|
|
114
|
+
gmax project --agent # compact: key\tvalue pairs
|
|
105
115
|
gmax index # reindex current directory
|
|
106
116
|
gmax config # view/change settings
|
|
107
117
|
gmax doctor # health check
|
|
@@ -124,7 +134,7 @@ Use MCP only for `index_status` and `summarize_directory`. Use CLI for everythin
|
|
|
124
134
|
|
|
125
135
|
## Tips
|
|
126
136
|
|
|
127
|
-
- **Use `--agent`
|
|
137
|
+
- **Use `--agent` for compact output** — supported on search, trace, symbols, related, recent, status, project.
|
|
128
138
|
- **Be specific.** 5+ words. "auth" returns noise. "where does the server validate JWT tokens" is specific.
|
|
129
139
|
- **Use `--role ORCHESTRATION`** to skip type definitions and find the actual logic.
|
|
130
140
|
- **Use `--symbol`** when the query is a function/class name — gets search + trace in one call.
|