grepmax 0.1.0 → 0.2.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/LICENSE +1 -1
- package/NOTICE +2 -2
- package/README.md +72 -72
- package/dist/commands/claude-code.js +6 -6
- package/dist/commands/codex.js +17 -17
- package/dist/commands/doctor.js +6 -5
- package/dist/commands/droid.js +22 -22
- package/dist/commands/index.js +1 -1
- package/dist/commands/list.js +82 -19
- package/dist/commands/mcp.js +177 -158
- package/dist/commands/opencode.js +26 -26
- package/dist/commands/search.js +23 -13
- package/dist/commands/serve.js +30 -30
- package/dist/commands/setup.js +51 -40
- package/dist/commands/skeleton.js +19 -13
- package/dist/commands/symbols.js +40 -2
- package/dist/commands/verify.js +1 -1
- package/dist/commands/watch.js +206 -0
- package/dist/config.js +37 -7
- package/dist/eval.js +14 -14
- package/dist/index.js +11 -7
- package/dist/lib/core/languages.js +28 -0
- package/dist/lib/index/chunker.js +6 -3
- package/dist/lib/index/grammar-loader.js +2 -2
- package/dist/lib/index/ignore-patterns.js +1 -1
- package/dist/lib/index/index-config.js +50 -10
- package/dist/lib/index/sync-helpers.js +1 -1
- package/dist/lib/index/syncer.js +67 -45
- package/dist/lib/index/walker.js +3 -3
- package/dist/lib/index/watcher.js +4 -4
- package/dist/lib/output/formatter.js +1 -1
- package/dist/lib/search/searcher.js +9 -9
- package/dist/lib/setup/model-loader.js +3 -3
- package/dist/lib/setup/setup-helpers.js +2 -4
- package/dist/lib/skeleton/body-fields.js +20 -0
- package/dist/lib/skeleton/retriever.js +1 -1
- package/dist/lib/skeleton/skeletonizer.js +8 -2
- package/dist/lib/skeleton/summary-formatter.js +1 -4
- package/dist/lib/store/meta-cache.js +28 -3
- package/dist/lib/store/vector-db.js +17 -9
- package/dist/lib/utils/formatter.js +3 -3
- package/dist/lib/utils/lock.js +1 -1
- package/dist/lib/utils/project-registry.js +83 -0
- package/dist/lib/utils/project-root.js +32 -57
- package/dist/lib/utils/watcher-registry.js +100 -0
- package/dist/lib/workers/colbert-math.js +2 -2
- package/dist/lib/workers/download-worker.js +2 -2
- package/dist/lib/workers/embeddings/colbert.js +2 -2
- package/dist/lib/workers/embeddings/granite.js +4 -4
- package/dist/lib/workers/embeddings/mlx-client.js +1 -1
- package/dist/lib/workers/orchestrator.js +8 -8
- package/dist/lib/workers/pool.js +1 -1
- package/dist/lib/workers/worker.js +4 -1
- package/package.json +20 -21
- package/plugins/{osgrep → grepmax}/.claude-plugin/plugin.json +4 -4
- package/plugins/grepmax/hooks/start.js +63 -0
- package/plugins/grepmax/hooks/stop.js +3 -0
- package/plugins/{osgrep/skills/osgrep → grepmax/skills/gmax}/SKILL.md +11 -11
- package/plugins/osgrep/hooks/start.js +0 -90
- package/plugins/osgrep/hooks/stop.js +0 -3
- /package/plugins/{osgrep → grepmax}/hooks.json +0 -0
package/dist/commands/mcp.js
CHANGED
|
@@ -46,27 +46,30 @@ exports.mcp = void 0;
|
|
|
46
46
|
exports.toStringArray = toStringArray;
|
|
47
47
|
exports.ok = ok;
|
|
48
48
|
exports.err = err;
|
|
49
|
-
const node_child_process_1 = require("node:child_process");
|
|
50
49
|
const fs = __importStar(require("node:fs"));
|
|
51
50
|
const path = __importStar(require("node:path"));
|
|
52
51
|
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
53
52
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
54
53
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
55
54
|
const commander_1 = require("commander");
|
|
55
|
+
const config_1 = require("../config");
|
|
56
56
|
const graph_builder_1 = require("../lib/graph/graph-builder");
|
|
57
|
+
const index_config_1 = require("../lib/index/index-config");
|
|
58
|
+
const syncer_1 = require("../lib/index/syncer");
|
|
59
|
+
const searcher_1 = require("../lib/search/searcher");
|
|
57
60
|
const retriever_1 = require("../lib/skeleton/retriever");
|
|
58
61
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
59
62
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
60
63
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
64
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
61
65
|
const project_root_1 = require("../lib/utils/project-root");
|
|
62
|
-
const server_registry_1 = require("../lib/utils/server-registry");
|
|
63
66
|
// ---------------------------------------------------------------------------
|
|
64
67
|
// Tool definitions
|
|
65
68
|
// ---------------------------------------------------------------------------
|
|
66
69
|
const TOOLS = [
|
|
67
70
|
{
|
|
68
71
|
name: "semantic_search",
|
|
69
|
-
description: "Search code by meaning. Use natural language queries like 'where do we validate permissions'
|
|
72
|
+
description: "Search code by meaning within a directory. Use natural language queries like 'where do we validate permissions'. Searches the current project by default. Use `root` to search a different directory's index (e.g. a parent directory).",
|
|
70
73
|
inputSchema: {
|
|
71
74
|
type: "object",
|
|
72
75
|
properties: {
|
|
@@ -78,9 +81,13 @@ const TOOLS = [
|
|
|
78
81
|
type: "number",
|
|
79
82
|
description: "Max results to return (default 10, max 50)",
|
|
80
83
|
},
|
|
84
|
+
root: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Directory to search (absolute or relative path). Defaults to the current project root. Use to search a parent or sibling directory's indexed code.",
|
|
87
|
+
},
|
|
81
88
|
path: {
|
|
82
89
|
type: "string",
|
|
83
|
-
description: "Restrict search to files under this path prefix (e.g. 'src/auth/')",
|
|
90
|
+
description: "Restrict search to files under this path prefix (e.g. 'src/auth/'). Relative to the search root.",
|
|
84
91
|
},
|
|
85
92
|
min_score: {
|
|
86
93
|
type: "number",
|
|
@@ -94,6 +101,32 @@ const TOOLS = [
|
|
|
94
101
|
required: ["query"],
|
|
95
102
|
},
|
|
96
103
|
},
|
|
104
|
+
{
|
|
105
|
+
name: "search_all",
|
|
106
|
+
description: "Search ALL indexed code across every directory. Use when you need to find code that could be anywhere. Returns results with full absolute paths so you know which project each result is from.",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: "object",
|
|
109
|
+
properties: {
|
|
110
|
+
query: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "Natural language search query.",
|
|
113
|
+
},
|
|
114
|
+
limit: {
|
|
115
|
+
type: "number",
|
|
116
|
+
description: "Max results to return (default 10, max 50)",
|
|
117
|
+
},
|
|
118
|
+
min_score: {
|
|
119
|
+
type: "number",
|
|
120
|
+
description: "Minimum relevance score (0-1). Default: 0",
|
|
121
|
+
},
|
|
122
|
+
max_per_file: {
|
|
123
|
+
type: "number",
|
|
124
|
+
description: "Max results per file (default: no cap).",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
required: ["query"],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
97
130
|
{
|
|
98
131
|
name: "code_skeleton",
|
|
99
132
|
description: "Show the structure of a source file — all function/class/method signatures with bodies collapsed. Useful for understanding large files without reading every line. Returns ~4x fewer tokens than the full file.",
|
|
@@ -110,7 +143,7 @@ const TOOLS = [
|
|
|
110
143
|
},
|
|
111
144
|
{
|
|
112
145
|
name: "trace_calls",
|
|
113
|
-
description: "Trace the call graph for a symbol — who calls it (callers) and what it calls (callees).
|
|
146
|
+
description: "Trace the call graph for a symbol — who calls it (callers) and what it calls (callees). Searches across ALL indexed code to follow calls across project boundaries.",
|
|
114
147
|
inputSchema: {
|
|
115
148
|
type: "object",
|
|
116
149
|
properties: {
|
|
@@ -145,7 +178,7 @@ const TOOLS = [
|
|
|
145
178
|
},
|
|
146
179
|
{
|
|
147
180
|
name: "index_status",
|
|
148
|
-
description: "Check the status of the
|
|
181
|
+
description: "Check the status of the gmax index. Returns indexed directories, chunk counts, embed mode, index age, and watcher status.",
|
|
149
182
|
inputSchema: {
|
|
150
183
|
type: "object",
|
|
151
184
|
properties: {},
|
|
@@ -153,37 +186,6 @@ const TOOLS = [
|
|
|
153
186
|
},
|
|
154
187
|
];
|
|
155
188
|
// ---------------------------------------------------------------------------
|
|
156
|
-
// Daemon lifecycle
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
let _daemonReady = null;
|
|
159
|
-
function ensureDaemon(projectRoot) {
|
|
160
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
-
const existing = (0, server_registry_1.getServerForProject)(projectRoot);
|
|
162
|
-
if (existing && (0, server_registry_1.isProcessRunning)(existing.pid)) {
|
|
163
|
-
console.log(`[MCP] Serve daemon already running (PID: ${existing.pid}, Port: ${existing.port})`);
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
console.log("[MCP] Starting serve daemon...");
|
|
167
|
-
const child = (0, node_child_process_1.spawn)("osgrep", ["serve", "-b"], {
|
|
168
|
-
cwd: projectRoot,
|
|
169
|
-
detached: true,
|
|
170
|
-
stdio: "ignore",
|
|
171
|
-
});
|
|
172
|
-
child.unref();
|
|
173
|
-
// Poll for readiness — daemon registers in ~/.osgrep/servers.json once listening
|
|
174
|
-
for (let i = 0; i < 30; i++) {
|
|
175
|
-
yield new Promise((r) => setTimeout(r, 2000));
|
|
176
|
-
const server = (0, server_registry_1.getServerForProject)(projectRoot);
|
|
177
|
-
if (server && (0, server_registry_1.isProcessRunning)(server.pid)) {
|
|
178
|
-
console.log(`[MCP] Daemon ready (PID: ${server.pid}, Port: ${server.port})`);
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
console.error("[MCP] Daemon failed to become ready within 60s");
|
|
183
|
-
return false;
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
189
|
// Helpers
|
|
188
190
|
// ---------------------------------------------------------------------------
|
|
189
191
|
function toStringArray(val) {
|
|
@@ -210,12 +212,13 @@ function err(text) {
|
|
|
210
212
|
// Command
|
|
211
213
|
// ---------------------------------------------------------------------------
|
|
212
214
|
exports.mcp = new commander_1.Command("mcp")
|
|
213
|
-
.description("Start MCP server for
|
|
215
|
+
.description("Start MCP server for gmax")
|
|
214
216
|
.action((_optsArg, _cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
215
217
|
// --- Lifecycle ---
|
|
216
|
-
var _a;
|
|
217
218
|
let _vectorDb = null;
|
|
219
|
+
let _searcher = null;
|
|
218
220
|
let _skeletonizer = null;
|
|
221
|
+
let _indexReady = false;
|
|
219
222
|
const cleanup = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
223
|
if (_vectorDb) {
|
|
221
224
|
try {
|
|
@@ -223,6 +226,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
223
226
|
}
|
|
224
227
|
catch (_a) { }
|
|
225
228
|
_vectorDb = null;
|
|
229
|
+
_searcher = null;
|
|
226
230
|
}
|
|
227
231
|
});
|
|
228
232
|
const exit = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -246,15 +250,20 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
246
250
|
};
|
|
247
251
|
console.debug = (..._args) => { };
|
|
248
252
|
// --- Project context ---
|
|
249
|
-
const projectRoot = (
|
|
253
|
+
const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
|
|
250
254
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
251
|
-
//
|
|
255
|
+
// Propagate project root to worker processes
|
|
256
|
+
process.env.GMAX_PROJECT_ROOT = paths.root;
|
|
257
|
+
// Lazy resource accessors — all use centralized store
|
|
252
258
|
function getVectorDb() {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
259
|
+
if (!_vectorDb)
|
|
260
|
+
_vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
261
|
+
return _vectorDb;
|
|
262
|
+
}
|
|
263
|
+
function getSearcher() {
|
|
264
|
+
if (!_searcher)
|
|
265
|
+
_searcher = new searcher_1.Searcher(getVectorDb());
|
|
266
|
+
return _searcher;
|
|
258
267
|
}
|
|
259
268
|
function getSkeletonizer() {
|
|
260
269
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -265,66 +274,88 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
265
274
|
return _skeletonizer;
|
|
266
275
|
});
|
|
267
276
|
}
|
|
268
|
-
// ---
|
|
269
|
-
function
|
|
277
|
+
// --- Index sync ---
|
|
278
|
+
function ensureIndexReady() {
|
|
270
279
|
return __awaiter(this, void 0, void 0, function* () {
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
280
|
+
if (_indexReady)
|
|
281
|
+
return;
|
|
282
|
+
try {
|
|
283
|
+
const db = getVectorDb();
|
|
284
|
+
const hasIndex = yield db.hasAnyRows();
|
|
285
|
+
if (!hasIndex) {
|
|
286
|
+
console.log("[MCP] No index found, running initial sync...");
|
|
287
|
+
yield (0, syncer_1.initialSync)({ projectRoot });
|
|
288
|
+
console.log("[MCP] Initial sync complete.");
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
console.log("[MCP] Index exists, ready.");
|
|
292
|
+
}
|
|
293
|
+
_indexReady = true;
|
|
294
|
+
}
|
|
295
|
+
catch (e) {
|
|
296
|
+
console.error("[MCP] Index sync failed:", e);
|
|
275
297
|
}
|
|
276
|
-
const server = (0, server_registry_1.getServerForProject)(projectRoot);
|
|
277
|
-
if (server && (0, server_registry_1.isProcessRunning)(server.pid))
|
|
278
|
-
return true;
|
|
279
|
-
// Daemon died — restart it
|
|
280
|
-
console.log("[MCP] Daemon not running, restarting...");
|
|
281
|
-
_daemonReady = ensureDaemon(projectRoot);
|
|
282
|
-
return _daemonReady;
|
|
283
298
|
});
|
|
284
299
|
}
|
|
285
|
-
|
|
286
|
-
|
|
300
|
+
// --- Tool handlers ---
|
|
301
|
+
function handleSemanticSearch(args_1) {
|
|
302
|
+
return __awaiter(this, arguments, void 0, function* (args, searchAll = false) {
|
|
287
303
|
const query = String(args.query || "");
|
|
288
304
|
if (!query)
|
|
289
305
|
return err("Missing required parameter: query");
|
|
290
306
|
const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50);
|
|
291
|
-
|
|
292
|
-
if (!(yield ensureDaemonRunning())) {
|
|
293
|
-
return err("Search daemon failed to start. Run 'osgrep serve -b' manually.");
|
|
294
|
-
}
|
|
295
|
-
const server = (0, server_registry_1.getServerForProject)(projectRoot);
|
|
296
|
-
if (!server || !(0, server_registry_1.isProcessRunning)(server.pid)) {
|
|
297
|
-
return err("Search daemon not running. Run 'osgrep serve -b' manually.");
|
|
298
|
-
}
|
|
307
|
+
yield ensureIndexReady();
|
|
299
308
|
try {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
+
const searcher = getSearcher();
|
|
310
|
+
// Determine path prefix for scoping
|
|
311
|
+
let pathPrefix;
|
|
312
|
+
if (!searchAll) {
|
|
313
|
+
// Resolve search root — default to project root
|
|
314
|
+
const searchRoot = typeof args.root === "string"
|
|
315
|
+
? path.resolve(args.root)
|
|
316
|
+
: path.resolve(projectRoot);
|
|
317
|
+
pathPrefix = searchRoot.endsWith("/")
|
|
318
|
+
? searchRoot
|
|
319
|
+
: `${searchRoot}/`;
|
|
320
|
+
// If a sub-path is specified, append it
|
|
321
|
+
if (typeof args.path === "string") {
|
|
322
|
+
pathPrefix = path.join(searchRoot, args.path);
|
|
323
|
+
if (!pathPrefix.endsWith("/"))
|
|
324
|
+
pathPrefix += "/";
|
|
325
|
+
}
|
|
309
326
|
}
|
|
310
|
-
const
|
|
311
|
-
if (!
|
|
327
|
+
const result = yield searcher.search(query, limit, { rerank: true }, undefined, pathPrefix);
|
|
328
|
+
if (!result.data || result.data.length === 0) {
|
|
312
329
|
return ok("No matches found.");
|
|
313
330
|
}
|
|
314
331
|
const minScore = typeof args.min_score === "number" ? args.min_score : 0;
|
|
315
332
|
const maxPerFile = typeof args.max_per_file === "number" ? args.max_per_file : 0;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
const MAX_SNIPPET_LINES = 8;
|
|
334
|
+
let compact = result.data.map((r) => {
|
|
335
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
336
|
+
const startLine = (_c = (_a = r.startLine) !== null && _a !== void 0 ? _a : (_b = r.generated_metadata) === null || _b === void 0 ? void 0 : _b.start_line) !== null && _c !== void 0 ? _c : 0;
|
|
337
|
+
const raw = typeof r.content === "string"
|
|
338
|
+
? r.content
|
|
339
|
+
: typeof r.text === "string"
|
|
340
|
+
? r.text
|
|
341
|
+
: "";
|
|
342
|
+
// Add line numbers and cap at MAX_SNIPPET_LINES
|
|
343
|
+
const lines = raw.split("\n");
|
|
344
|
+
const capped = lines.slice(0, MAX_SNIPPET_LINES);
|
|
345
|
+
const numbered = capped.map((line, i) => `${startLine + i + 1}│${line}`);
|
|
346
|
+
const snippet = lines.length > MAX_SNIPPET_LINES
|
|
347
|
+
? `${numbered.join("\n")}\n… (+${lines.length - MAX_SNIPPET_LINES} more lines)`
|
|
348
|
+
: numbered.join("\n");
|
|
349
|
+
return {
|
|
350
|
+
path: (_f = (_d = r.path) !== null && _d !== void 0 ? _d : (_e = r.metadata) === null || _e === void 0 ? void 0 : _e.path) !== null && _f !== void 0 ? _f : "",
|
|
351
|
+
startLine,
|
|
352
|
+
endLine: (_j = (_g = r.endLine) !== null && _g !== void 0 ? _g : (_h = r.generated_metadata) === null || _h === void 0 ? void 0 : _h.end_line) !== null && _j !== void 0 ? _j : 0,
|
|
322
353
|
score: typeof r.score === "number" ? +r.score.toFixed(3) : 0,
|
|
323
|
-
role: (
|
|
324
|
-
confidence: (
|
|
325
|
-
definedSymbols: toStringArray(r.defined_symbols).slice(0, 5),
|
|
326
|
-
snippet
|
|
327
|
-
}
|
|
354
|
+
role: (_k = r.role) !== null && _k !== void 0 ? _k : "IMPLEMENTATION",
|
|
355
|
+
confidence: (_l = r.confidence) !== null && _l !== void 0 ? _l : "Unknown",
|
|
356
|
+
definedSymbols: toStringArray((_m = r.definedSymbols) !== null && _m !== void 0 ? _m : r.defined_symbols).slice(0, 5),
|
|
357
|
+
snippet,
|
|
358
|
+
};
|
|
328
359
|
});
|
|
329
360
|
if (minScore > 0) {
|
|
330
361
|
compact = compact.filter((r) => r.score >= minScore);
|
|
@@ -339,11 +370,11 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
339
370
|
return true;
|
|
340
371
|
});
|
|
341
372
|
}
|
|
342
|
-
return ok(JSON.stringify(compact
|
|
373
|
+
return ok(JSON.stringify(compact));
|
|
343
374
|
}
|
|
344
375
|
catch (e) {
|
|
345
376
|
const msg = e instanceof Error ? e.message : String(e);
|
|
346
|
-
return err(`Search
|
|
377
|
+
return err(`Search failed: ${msg}`);
|
|
347
378
|
}
|
|
348
379
|
});
|
|
349
380
|
}
|
|
@@ -353,21 +384,16 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
353
384
|
if (!target)
|
|
354
385
|
return err("Missing required parameter: target");
|
|
355
386
|
const absPath = path.resolve(projectRoot, target);
|
|
356
|
-
const relPath = path.relative(projectRoot, absPath);
|
|
357
|
-
// Security: ensure path is within project
|
|
358
|
-
if (relPath.startsWith("..") || path.isAbsolute(relPath)) {
|
|
359
|
-
return err("Path must be within the project root.");
|
|
360
|
-
}
|
|
361
387
|
if (!fs.existsSync(absPath)) {
|
|
362
388
|
return err(`File not found: ${target}`);
|
|
363
389
|
}
|
|
364
|
-
// Try cached skeleton first
|
|
390
|
+
// Try cached skeleton first (stored with absolute path)
|
|
365
391
|
try {
|
|
366
|
-
const db =
|
|
367
|
-
const cached = yield (0, retriever_1.getStoredSkeleton)(db,
|
|
392
|
+
const db = getVectorDb();
|
|
393
|
+
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
368
394
|
if (cached) {
|
|
369
395
|
const tokens = Math.ceil(cached.length / 4);
|
|
370
|
-
return ok(`// ${
|
|
396
|
+
return ok(`// ${target} (~${tokens} tokens)\n\n${cached}`);
|
|
371
397
|
}
|
|
372
398
|
}
|
|
373
399
|
catch (_a) {
|
|
@@ -377,11 +403,11 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
377
403
|
try {
|
|
378
404
|
const content = fs.readFileSync(absPath, "utf-8");
|
|
379
405
|
const skel = yield getSkeletonizer();
|
|
380
|
-
const result = yield skel.skeletonizeFile(
|
|
406
|
+
const result = yield skel.skeletonizeFile(absPath, content);
|
|
381
407
|
if (!result.success && result.error) {
|
|
382
408
|
return err(`Skeleton generation failed: ${result.error}`);
|
|
383
409
|
}
|
|
384
|
-
return ok(`// ${
|
|
410
|
+
return ok(`// ${target} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
|
|
385
411
|
}
|
|
386
412
|
catch (e) {
|
|
387
413
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -395,38 +421,31 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
395
421
|
if (!symbol)
|
|
396
422
|
return err("Missing required parameter: symbol");
|
|
397
423
|
try {
|
|
398
|
-
const db =
|
|
424
|
+
const db = getVectorDb();
|
|
399
425
|
const builder = new graph_builder_1.GraphBuilder(db);
|
|
400
426
|
const graph = yield builder.buildGraph(symbol);
|
|
401
427
|
if (!graph.center) {
|
|
402
428
|
return ok(`Symbol '${symbol}' not found in the index.`);
|
|
403
429
|
}
|
|
404
430
|
const lines = [];
|
|
431
|
+
// Center
|
|
432
|
+
lines.push(`${graph.center.symbol} [${graph.center.role}] ${graph.center.file}:${graph.center.line + 1}`);
|
|
405
433
|
// Callers
|
|
406
434
|
if (graph.callers.length > 0) {
|
|
407
|
-
lines.push("Callers
|
|
435
|
+
lines.push("Callers:");
|
|
408
436
|
for (const caller of graph.callers) {
|
|
409
|
-
lines.push(` <- ${caller.symbol}
|
|
437
|
+
lines.push(` <- ${caller.symbol} ${caller.file}:${caller.line + 1}`);
|
|
410
438
|
}
|
|
411
439
|
}
|
|
412
440
|
else {
|
|
413
|
-
lines.push("
|
|
441
|
+
lines.push("Callers: none");
|
|
414
442
|
}
|
|
415
|
-
lines.push("");
|
|
416
|
-
// Center
|
|
417
|
-
lines.push(`${graph.center.symbol}`);
|
|
418
|
-
lines.push(` Defined in ${graph.center.file}:${graph.center.line}`);
|
|
419
|
-
lines.push(` Role: ${graph.center.role}`);
|
|
420
|
-
lines.push("");
|
|
421
443
|
// Callees
|
|
422
444
|
if (graph.callees.length > 0) {
|
|
423
|
-
lines.push(
|
|
424
|
-
for (const callee of graph.callees) {
|
|
425
|
-
lines.push(` -> ${callee}`);
|
|
426
|
-
}
|
|
445
|
+
lines.push(`Calls: ${graph.callees.join(", ")}`);
|
|
427
446
|
}
|
|
428
447
|
else {
|
|
429
|
-
lines.push("
|
|
448
|
+
lines.push("Calls: none");
|
|
430
449
|
}
|
|
431
450
|
return ok(lines.join("\n"));
|
|
432
451
|
}
|
|
@@ -442,7 +461,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
442
461
|
const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
|
|
443
462
|
const pathPrefix = typeof args.path === "string" ? args.path : undefined;
|
|
444
463
|
try {
|
|
445
|
-
const db =
|
|
464
|
+
const db = getVectorDb();
|
|
446
465
|
const table = yield db.ensureTable();
|
|
447
466
|
let query = table
|
|
448
467
|
.query()
|
|
@@ -450,7 +469,11 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
450
469
|
.where("array_length(defined_symbols) > 0")
|
|
451
470
|
.limit(pattern ? 10000 : Math.max(limit * 50, 2000));
|
|
452
471
|
if (pathPrefix) {
|
|
453
|
-
|
|
472
|
+
// Support both absolute and relative path prefixes
|
|
473
|
+
const absPrefix = path.isAbsolute(pathPrefix)
|
|
474
|
+
? pathPrefix
|
|
475
|
+
: path.resolve(projectRoot, pathPrefix);
|
|
476
|
+
query = query.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(absPrefix))}%'`);
|
|
454
477
|
}
|
|
455
478
|
const rows = yield query.toArray();
|
|
456
479
|
const map = new Map();
|
|
@@ -467,7 +490,12 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
467
490
|
existing.count += 1;
|
|
468
491
|
}
|
|
469
492
|
else {
|
|
470
|
-
map.set(sym, {
|
|
493
|
+
map.set(sym, {
|
|
494
|
+
symbol: sym,
|
|
495
|
+
count: 1,
|
|
496
|
+
path: rowPath,
|
|
497
|
+
line: Math.max(1, line + 1),
|
|
498
|
+
});
|
|
471
499
|
}
|
|
472
500
|
}
|
|
473
501
|
}
|
|
@@ -479,9 +507,10 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
479
507
|
})
|
|
480
508
|
.slice(0, limit);
|
|
481
509
|
if (entries.length === 0) {
|
|
482
|
-
return ok("No symbols found. Run '
|
|
510
|
+
return ok("No symbols found. Run 'gmax index' to build the index.");
|
|
483
511
|
}
|
|
484
|
-
|
|
512
|
+
const lines = entries.map((e) => `${e.symbol}\t${e.path}:${e.line}`);
|
|
513
|
+
return ok(lines.join("\n"));
|
|
485
514
|
}
|
|
486
515
|
catch (e) {
|
|
487
516
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -491,46 +520,35 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
491
520
|
}
|
|
492
521
|
function handleIndexStatus() {
|
|
493
522
|
return __awaiter(this, void 0, void 0, function* () {
|
|
494
|
-
var _a, _b, _c
|
|
495
|
-
yield ensureDaemonRunning();
|
|
496
|
-
const server = (0, server_registry_1.getServerForProject)(projectRoot);
|
|
497
|
-
if (!server || !(0, server_registry_1.isProcessRunning)(server.pid)) {
|
|
498
|
-
// Fall back to config file
|
|
499
|
-
const configPath = path.join(projectRoot, ".osgrep", "config.json");
|
|
500
|
-
try {
|
|
501
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
502
|
-
return ok(JSON.stringify({
|
|
503
|
-
daemon: "stopped",
|
|
504
|
-
embedMode: (_a = config.embedMode) !== null && _a !== void 0 ? _a : "unknown",
|
|
505
|
-
model: (_c = (_b = config.embedModel) !== null && _b !== void 0 ? _b : config.mlxModel) !== null && _c !== void 0 ? _c : null,
|
|
506
|
-
vectorDim: (_d = config.vectorDim) !== null && _d !== void 0 ? _d : null,
|
|
507
|
-
indexedAt: (_e = config.indexedAt) !== null && _e !== void 0 ? _e : null,
|
|
508
|
-
}, null, 2));
|
|
509
|
-
}
|
|
510
|
-
catch (_f) {
|
|
511
|
-
return ok(JSON.stringify({ daemon: "stopped", indexed: false }));
|
|
512
|
-
}
|
|
513
|
-
}
|
|
523
|
+
var _a, _b, _c;
|
|
514
524
|
try {
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
525
|
+
const config = (0, index_config_1.readIndexConfig)(config_1.PATHS.configPath);
|
|
526
|
+
const projects = (0, project_registry_1.listProjects)();
|
|
527
|
+
const db = getVectorDb();
|
|
528
|
+
const stats = yield db.getStats();
|
|
529
|
+
const fileCount = yield db.getDistinctFileCount();
|
|
530
|
+
const lines = [
|
|
531
|
+
`Index: ~/.gmax/lancedb (${stats.chunks} chunks, ${fileCount} files)`,
|
|
532
|
+
`Model: ${(_a = config === null || config === void 0 ? void 0 : config.embedModel) !== null && _a !== void 0 ? _a : "unknown"} (${(_b = config === null || config === void 0 ? void 0 : config.vectorDim) !== null && _b !== void 0 ? _b : "?"}d, ${(_c = config === null || config === void 0 ? void 0 : config.embedMode) !== null && _c !== void 0 ? _c : "unknown"})`,
|
|
533
|
+
(config === null || config === void 0 ? void 0 : config.indexedAt)
|
|
534
|
+
? `Last indexed: ${config.indexedAt}`
|
|
535
|
+
: "",
|
|
536
|
+
"",
|
|
537
|
+
"Indexed directories:",
|
|
538
|
+
...projects.map((p) => { var _a; return ` ${p.name}\t${p.root}\t${(_a = p.lastIndexed) !== null && _a !== void 0 ? _a : "unknown"}`; }),
|
|
539
|
+
].filter(Boolean);
|
|
540
|
+
return ok(lines.join("\n"));
|
|
523
541
|
}
|
|
524
542
|
catch (e) {
|
|
525
543
|
const msg = e instanceof Error ? e.message : String(e);
|
|
526
|
-
return err(`
|
|
544
|
+
return err(`Status check failed: ${msg}`);
|
|
527
545
|
}
|
|
528
546
|
});
|
|
529
547
|
}
|
|
530
548
|
// --- MCP server setup ---
|
|
531
549
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
532
550
|
const server = new index_js_1.Server({
|
|
533
|
-
name: "
|
|
551
|
+
name: "gmax",
|
|
534
552
|
version: JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), {
|
|
535
553
|
encoding: "utf-8",
|
|
536
554
|
})).version,
|
|
@@ -547,7 +565,9 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
547
565
|
const toolArgs = (args !== null && args !== void 0 ? args : {});
|
|
548
566
|
switch (name) {
|
|
549
567
|
case "semantic_search":
|
|
550
|
-
return handleSemanticSearch(toolArgs);
|
|
568
|
+
return handleSemanticSearch(toolArgs, false);
|
|
569
|
+
case "search_all":
|
|
570
|
+
return handleSemanticSearch(toolArgs, true);
|
|
551
571
|
case "code_skeleton":
|
|
552
572
|
return handleCodeSkeleton(toolArgs);
|
|
553
573
|
case "trace_calls":
|
|
@@ -561,7 +581,6 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
561
581
|
}
|
|
562
582
|
}));
|
|
563
583
|
yield server.connect(transport);
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
_daemonReady = ensureDaemon(projectRoot);
|
|
584
|
+
// Kick off index readiness check in background
|
|
585
|
+
ensureIndexReady();
|
|
567
586
|
}));
|