grepmax 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +161 -142
- 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/list.js
CHANGED
|
@@ -44,14 +44,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.list = void 0;
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const os = __importStar(require("node:os"));
|
|
47
48
|
const path = __importStar(require("node:path"));
|
|
48
49
|
const commander_1 = require("commander");
|
|
50
|
+
const index_config_1 = require("../lib/index/index-config");
|
|
49
51
|
const exit_1 = require("../lib/utils/exit");
|
|
52
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
50
53
|
const project_root_1 = require("../lib/utils/project-root");
|
|
51
54
|
const style = {
|
|
52
55
|
bold: (s) => `\x1b[1m${s}\x1b[22m`,
|
|
53
56
|
dim: (s) => `\x1b[2m${s}\x1b[22m`,
|
|
54
57
|
green: (s) => `\x1b[32m${s}\x1b[39m`,
|
|
58
|
+
yellow: (s) => `\x1b[33m${s}\x1b[39m`,
|
|
59
|
+
red: (s) => `\x1b[31m${s}\x1b[39m`,
|
|
55
60
|
};
|
|
56
61
|
function formatSize(bytes) {
|
|
57
62
|
if (bytes < 1024)
|
|
@@ -95,26 +100,84 @@ function getDirectorySize(dirPath) {
|
|
|
95
100
|
catch (_a) { }
|
|
96
101
|
return totalSize;
|
|
97
102
|
}
|
|
103
|
+
function shortenPath(p) {
|
|
104
|
+
const home = os.homedir();
|
|
105
|
+
if (p.startsWith(home))
|
|
106
|
+
return `~${p.slice(home.length)}`;
|
|
107
|
+
return p;
|
|
108
|
+
}
|
|
109
|
+
function pad(s, len) {
|
|
110
|
+
return s.length >= len ? s : s + " ".repeat(len - s.length);
|
|
111
|
+
}
|
|
98
112
|
exports.list = new commander_1.Command("list")
|
|
99
|
-
.description("Show
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
];
|
|
108
|
-
console.log(`\n${style.bold("Project")}: ${style.green(projectRoot)}`);
|
|
109
|
-
console.log(`${style.dim("Data directory")}: ${paths.osgrepDir}\n`);
|
|
110
|
-
for (const entry of entries) {
|
|
111
|
-
if (!fs.existsSync(entry.dir)) {
|
|
112
|
-
console.log(`${entry.label}: ${style.dim("not created yet")}`);
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
const stats = fs.statSync(entry.dir);
|
|
116
|
-
const size = getDirectorySize(entry.dir);
|
|
117
|
-
console.log(`${entry.label}: ${style.green(formatSize(size))} ${style.dim(`(updated ${formatDate(stats.mtime)})`)}`);
|
|
113
|
+
.description("Show indexed projects")
|
|
114
|
+
.option("--all", "Show all known projects across the system")
|
|
115
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
+
if (options.all) {
|
|
117
|
+
yield showAllProjects();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
yield showCurrentProject();
|
|
118
121
|
}
|
|
119
122
|
yield (0, exit_1.gracefulExit)();
|
|
120
123
|
}));
|
|
124
|
+
function showCurrentProject() {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
var _a, _b, _c, _d;
|
|
127
|
+
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _a !== void 0 ? _a : process.cwd();
|
|
128
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
129
|
+
const entries = [
|
|
130
|
+
{ label: "LanceDB", dir: paths.lancedbDir },
|
|
131
|
+
{ label: "Cache", dir: paths.cacheDir },
|
|
132
|
+
];
|
|
133
|
+
console.log(`\n${style.bold("Project")}: ${style.green(projectRoot)}`);
|
|
134
|
+
console.log(`${style.dim("Data directory")}: ${paths.dataDir}\n`);
|
|
135
|
+
const config = (0, index_config_1.readIndexConfig)(paths.configPath);
|
|
136
|
+
if (config) {
|
|
137
|
+
console.log(`${style.dim("Model")}: ${(_b = config.modelTier) !== null && _b !== void 0 ? _b : "small"} (${(_c = config.vectorDim) !== null && _c !== void 0 ? _c : 384}d)`);
|
|
138
|
+
console.log(`${style.dim("Mode")}: ${(_d = config.embedMode) !== null && _d !== void 0 ? _d : "cpu"}`);
|
|
139
|
+
if (config.indexedAt) {
|
|
140
|
+
console.log(`${style.dim("Indexed")}: ${formatDate(new Date(config.indexedAt))}`);
|
|
141
|
+
}
|
|
142
|
+
console.log();
|
|
143
|
+
}
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
if (!fs.existsSync(entry.dir)) {
|
|
146
|
+
console.log(`${entry.label}: ${style.dim("not created yet")}`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const stats = fs.statSync(entry.dir);
|
|
150
|
+
const size = getDirectorySize(entry.dir);
|
|
151
|
+
console.log(`${entry.label}: ${style.green(formatSize(size))} ${style.dim(`(updated ${formatDate(stats.mtime)})`)}`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function showAllProjects() {
|
|
156
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
157
|
+
const projects = (0, project_registry_1.listProjects)();
|
|
158
|
+
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
159
|
+
if (projects.length === 0) {
|
|
160
|
+
console.log("\nNo projects registered yet. Index a project to see it here.");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log();
|
|
164
|
+
// Column widths
|
|
165
|
+
const nameWidth = Math.max(8, ...projects.map((p) => p.name.length));
|
|
166
|
+
const pathWidth = Math.max(12, ...projects.map((p) => shortenPath(p.root).length));
|
|
167
|
+
// Header
|
|
168
|
+
console.log(`${style.bold(pad("Project", nameWidth))} ${style.bold(pad("Path", pathWidth))} ${style.bold("Dims")} ${style.bold("Status")}`);
|
|
169
|
+
for (const project of projects) {
|
|
170
|
+
const dimMatch = project.vectorDim === globalConfig.vectorDim;
|
|
171
|
+
const dimsStr = `${project.vectorDim}d`;
|
|
172
|
+
const status = dimMatch
|
|
173
|
+
? style.green("ok")
|
|
174
|
+
: style.yellow("reindex needed");
|
|
175
|
+
console.log(`${pad(project.name, nameWidth)} ${style.dim(pad(shortenPath(project.root), pathWidth))} ${pad(dimsStr, 4)} ${status}`);
|
|
176
|
+
}
|
|
177
|
+
console.log(`\n${style.dim("Global config")}: ${globalConfig.modelTier} (${globalConfig.vectorDim}d), ${globalConfig.embedMode}`);
|
|
178
|
+
const needsReindex = projects.filter((p) => p.vectorDim !== globalConfig.vectorDim);
|
|
179
|
+
if (needsReindex.length > 0) {
|
|
180
|
+
console.log(style.yellow(`\n${needsReindex.length} project(s) need reindexing. Search will auto-reindex on first use.`));
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
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,65 +274,77 @@ 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
|
-
let compact =
|
|
317
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
333
|
+
let compact = result.data.map((r) => {
|
|
334
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
318
335
|
return ({
|
|
319
|
-
path: (_c = (
|
|
320
|
-
startLine: (
|
|
321
|
-
endLine: (_g = (
|
|
336
|
+
path: (_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 : "",
|
|
337
|
+
startLine: (_f = (_d = r.startLine) !== null && _d !== void 0 ? _d : (_e = r.generated_metadata) === null || _e === void 0 ? void 0 : _e.start_line) !== null && _f !== void 0 ? _f : 0,
|
|
338
|
+
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
339
|
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: typeof r.
|
|
340
|
+
role: (_k = r.role) !== null && _k !== void 0 ? _k : "IMPLEMENTATION",
|
|
341
|
+
confidence: (_l = r.confidence) !== null && _l !== void 0 ? _l : "Unknown",
|
|
342
|
+
definedSymbols: toStringArray((_m = r.definedSymbols) !== null && _m !== void 0 ? _m : r.defined_symbols).slice(0, 5),
|
|
343
|
+
snippet: typeof r.content === "string"
|
|
344
|
+
? r.content
|
|
345
|
+
: typeof r.text === "string"
|
|
346
|
+
? r.text
|
|
347
|
+
: "",
|
|
327
348
|
});
|
|
328
349
|
});
|
|
329
350
|
if (minScore > 0) {
|
|
@@ -339,11 +360,11 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
339
360
|
return true;
|
|
340
361
|
});
|
|
341
362
|
}
|
|
342
|
-
return ok(JSON.stringify(compact
|
|
363
|
+
return ok(JSON.stringify(compact));
|
|
343
364
|
}
|
|
344
365
|
catch (e) {
|
|
345
366
|
const msg = e instanceof Error ? e.message : String(e);
|
|
346
|
-
return err(`Search
|
|
367
|
+
return err(`Search failed: ${msg}`);
|
|
347
368
|
}
|
|
348
369
|
});
|
|
349
370
|
}
|
|
@@ -353,21 +374,16 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
353
374
|
if (!target)
|
|
354
375
|
return err("Missing required parameter: target");
|
|
355
376
|
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
377
|
if (!fs.existsSync(absPath)) {
|
|
362
378
|
return err(`File not found: ${target}`);
|
|
363
379
|
}
|
|
364
|
-
// Try cached skeleton first
|
|
380
|
+
// Try cached skeleton first (stored with absolute path)
|
|
365
381
|
try {
|
|
366
|
-
const db =
|
|
367
|
-
const cached = yield (0, retriever_1.getStoredSkeleton)(db,
|
|
382
|
+
const db = getVectorDb();
|
|
383
|
+
const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
|
|
368
384
|
if (cached) {
|
|
369
385
|
const tokens = Math.ceil(cached.length / 4);
|
|
370
|
-
return ok(`// ${
|
|
386
|
+
return ok(`// ${target} (~${tokens} tokens)\n\n${cached}`);
|
|
371
387
|
}
|
|
372
388
|
}
|
|
373
389
|
catch (_a) {
|
|
@@ -377,11 +393,11 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
377
393
|
try {
|
|
378
394
|
const content = fs.readFileSync(absPath, "utf-8");
|
|
379
395
|
const skel = yield getSkeletonizer();
|
|
380
|
-
const result = yield skel.skeletonizeFile(
|
|
396
|
+
const result = yield skel.skeletonizeFile(absPath, content);
|
|
381
397
|
if (!result.success && result.error) {
|
|
382
398
|
return err(`Skeleton generation failed: ${result.error}`);
|
|
383
399
|
}
|
|
384
|
-
return ok(`// ${
|
|
400
|
+
return ok(`// ${target} (~${result.tokenEstimate} tokens)\n\n${result.skeleton}`);
|
|
385
401
|
}
|
|
386
402
|
catch (e) {
|
|
387
403
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -395,7 +411,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
395
411
|
if (!symbol)
|
|
396
412
|
return err("Missing required parameter: symbol");
|
|
397
413
|
try {
|
|
398
|
-
const db =
|
|
414
|
+
const db = getVectorDb();
|
|
399
415
|
const builder = new graph_builder_1.GraphBuilder(db);
|
|
400
416
|
const graph = yield builder.buildGraph(symbol);
|
|
401
417
|
if (!graph.center) {
|
|
@@ -442,7 +458,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
442
458
|
const limit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
|
|
443
459
|
const pathPrefix = typeof args.path === "string" ? args.path : undefined;
|
|
444
460
|
try {
|
|
445
|
-
const db =
|
|
461
|
+
const db = getVectorDb();
|
|
446
462
|
const table = yield db.ensureTable();
|
|
447
463
|
let query = table
|
|
448
464
|
.query()
|
|
@@ -450,7 +466,11 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
450
466
|
.where("array_length(defined_symbols) > 0")
|
|
451
467
|
.limit(pattern ? 10000 : Math.max(limit * 50, 2000));
|
|
452
468
|
if (pathPrefix) {
|
|
453
|
-
|
|
469
|
+
// Support both absolute and relative path prefixes
|
|
470
|
+
const absPrefix = path.isAbsolute(pathPrefix)
|
|
471
|
+
? pathPrefix
|
|
472
|
+
: path.resolve(projectRoot, pathPrefix);
|
|
473
|
+
query = query.where(`path LIKE '${(0, filter_builder_1.escapeSqlString)((0, filter_builder_1.normalizePath)(absPrefix))}%'`);
|
|
454
474
|
}
|
|
455
475
|
const rows = yield query.toArray();
|
|
456
476
|
const map = new Map();
|
|
@@ -467,7 +487,12 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
467
487
|
existing.count += 1;
|
|
468
488
|
}
|
|
469
489
|
else {
|
|
470
|
-
map.set(sym, {
|
|
490
|
+
map.set(sym, {
|
|
491
|
+
symbol: sym,
|
|
492
|
+
count: 1,
|
|
493
|
+
path: rowPath,
|
|
494
|
+
line: Math.max(1, line + 1),
|
|
495
|
+
});
|
|
471
496
|
}
|
|
472
497
|
}
|
|
473
498
|
}
|
|
@@ -479,9 +504,9 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
479
504
|
})
|
|
480
505
|
.slice(0, limit);
|
|
481
506
|
if (entries.length === 0) {
|
|
482
|
-
return ok("No symbols found. Run '
|
|
507
|
+
return ok("No symbols found. Run 'gmax index' to build the index.");
|
|
483
508
|
}
|
|
484
|
-
return ok(JSON.stringify(entries
|
|
509
|
+
return ok(JSON.stringify(entries));
|
|
485
510
|
}
|
|
486
511
|
catch (e) {
|
|
487
512
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -491,46 +516,39 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
491
516
|
}
|
|
492
517
|
function handleIndexStatus() {
|
|
493
518
|
return __awaiter(this, void 0, void 0, function* () {
|
|
494
|
-
var _a, _b, _c, _d
|
|
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
|
-
}
|
|
519
|
+
var _a, _b, _c, _d;
|
|
514
520
|
try {
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
521
|
+
const config = (0, index_config_1.readIndexConfig)(config_1.PATHS.configPath);
|
|
522
|
+
const projects = (0, project_registry_1.listProjects)();
|
|
523
|
+
const db = getVectorDb();
|
|
524
|
+
const stats = yield db.getStats();
|
|
525
|
+
const fileCount = yield db.getDistinctFileCount();
|
|
526
|
+
return ok(JSON.stringify({
|
|
527
|
+
store: "centralized (~/.gmax/lancedb)",
|
|
528
|
+
totalChunks: stats.chunks,
|
|
529
|
+
totalFiles: fileCount,
|
|
530
|
+
totalBytes: stats.totalBytes,
|
|
531
|
+
embedMode: (_a = config === null || config === void 0 ? void 0 : config.embedMode) !== null && _a !== void 0 ? _a : "unknown",
|
|
532
|
+
model: (_b = config === null || config === void 0 ? void 0 : config.embedModel) !== null && _b !== void 0 ? _b : null,
|
|
533
|
+
vectorDim: (_c = config === null || config === void 0 ? void 0 : config.vectorDim) !== null && _c !== void 0 ? _c : null,
|
|
534
|
+
indexedAt: (_d = config === null || config === void 0 ? void 0 : config.indexedAt) !== null && _d !== void 0 ? _d : null,
|
|
535
|
+
indexedDirectories: projects.map((p) => ({
|
|
536
|
+
name: p.name,
|
|
537
|
+
root: p.root,
|
|
538
|
+
lastIndexed: p.lastIndexed,
|
|
539
|
+
})),
|
|
540
|
+
}));
|
|
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
|
}));
|