grepmax 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +23 -0
- package/README.md +1 -1
- package/dist/commands/codex.js +5 -2
- package/dist/commands/context.js +1 -1
- package/dist/commands/doctor.js +37 -7
- package/dist/commands/droid.js +61 -9
- package/dist/commands/mcp.js +7 -7
- package/dist/commands/plugin.js +35 -21
- package/dist/commands/project.js +1 -1
- package/dist/commands/remove.js +9 -2
- package/dist/commands/repair.js +186 -0
- package/dist/commands/search-run.js +12 -1
- package/dist/commands/setup.js +4 -2
- package/dist/commands/status.js +1 -1
- package/dist/commands/symbols.js +1 -1
- package/dist/commands/watch.js +25 -4
- package/dist/config.js +45 -1
- package/dist/index.js +2 -0
- package/dist/lib/daemon/daemon.js +177 -48
- package/dist/lib/daemon/ipc-handler.js +12 -0
- package/dist/lib/daemon/process-manager.js +21 -3
- package/dist/lib/daemon/watcher-manager.js +18 -2
- package/dist/lib/graph/graph-builder.js +2 -2
- package/dist/lib/graph/impact.js +6 -6
- package/dist/lib/index/batch-processor.js +10 -1
- package/dist/lib/index/syncer.js +15 -4
- package/dist/lib/index/watcher-batch.js +10 -1
- package/dist/lib/llm/config.js +2 -1
- package/dist/lib/llm/investigate.js +40 -6
- package/dist/lib/llm/server.js +9 -2
- package/dist/lib/llm/tools.js +1 -1
- package/dist/lib/search/pagerank.js +1 -1
- package/dist/lib/search/searcher.js +6 -6
- package/dist/lib/store/vector-db.js +49 -10
- package/dist/lib/utils/daemon-client.js +87 -0
- package/dist/lib/utils/filter-builder.js +22 -0
- package/dist/lib/utils/scope-filter.js +3 -5
- package/dist/lib/workers/embeddings/granite.js +4 -1
- package/dist/lib/workers/pool.js +21 -1
- package/package.json +2 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/agents/semantic-explore.md +1 -1
- package/plugins/grepmax/skills/grepmax/SKILL.md +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
3
|
+
"name": "grepmax",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Robert Owens",
|
|
6
|
+
"email": "robowens@me.com"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "grepmax",
|
|
11
|
+
"source": "./plugins/grepmax",
|
|
12
|
+
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
13
|
+
"version": "0.21.0",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Robert Owens",
|
|
16
|
+
"email": "robowens@me.com"
|
|
17
|
+
},
|
|
18
|
+
"skills": [
|
|
19
|
+
"./skills/grepmax"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ gmax "where do we handle auth?" --agent # Semantic search (compact output)
|
|
|
61
61
|
gmax extract handleAuth # Full function body with line numbers
|
|
62
62
|
gmax peek handleAuth # Signature + callers + callees
|
|
63
63
|
gmax trace handleAuth -d 2 # Call graph (2-hop)
|
|
64
|
-
gmax skeleton src/lib/
|
|
64
|
+
gmax skeleton src/lib/auth.ts # File structure (bodies collapsed)
|
|
65
65
|
gmax symbols auth # List indexed symbols
|
|
66
66
|
```
|
|
67
67
|
|
package/dist/commands/codex.js
CHANGED
|
@@ -69,8 +69,11 @@ function writeSkillToAgents(skill) {
|
|
|
69
69
|
function installPlugin() {
|
|
70
70
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
71
|
try {
|
|
72
|
-
// 1. Register MCP tool
|
|
73
|
-
|
|
72
|
+
// 1. Register MCP tool. Codex requires the stdio command after `--`:
|
|
73
|
+
// codex mcp add [OPTIONS] <NAME> -- <COMMAND>...
|
|
74
|
+
// Without the separator the launch command is misparsed. AGENTS.md is only
|
|
75
|
+
// written after this resolves, so a failed registration leaves it untouched.
|
|
76
|
+
yield execAsync("codex mcp add gmax -- gmax mcp", {
|
|
74
77
|
shell,
|
|
75
78
|
env: process.env,
|
|
76
79
|
});
|
package/dist/commands/context.js
CHANGED
|
@@ -316,7 +316,7 @@ exports.context = new commander_1.Command("context")
|
|
|
316
316
|
allSymbols.add(s);
|
|
317
317
|
}
|
|
318
318
|
if (allSymbols.size > 0) {
|
|
319
|
-
const pathScope =
|
|
319
|
+
const pathScope = (0, filter_builder_1.pathStartsWith)(`${projectRoot}/`);
|
|
320
320
|
const relatedCounts = new Map();
|
|
321
321
|
const searchedFiles = new Set(uniqueFiles);
|
|
322
322
|
for (const sym of [...allSymbols].slice(0, 20)) {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -179,6 +179,13 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
179
179
|
const db = new VectorDB(config_1.PATHS.lancedbDir);
|
|
180
180
|
const table = yield db.ensureTable();
|
|
181
181
|
const totalChunks = yield table.countRows();
|
|
182
|
+
// Physical schema-width check: the shared `chunks` table is fixed-width at
|
|
183
|
+
// creation, so a tier/dim change strands it at the old width and every
|
|
184
|
+
// write throws. This is independent of the per-project registry drift
|
|
185
|
+
// checked below — the table can match the registry yet still be physically
|
|
186
|
+
// stranded — so we surface it as its own line.
|
|
187
|
+
const physicalDim = yield db.getSchemaVectorDim();
|
|
188
|
+
const schemaGap = (0, config_1.describeSchemaDimGap)(physicalDim, globalConfig.vectorDim);
|
|
182
189
|
// Summary coverage (existing check)
|
|
183
190
|
if (!opts.agent && totalChunks > 0) {
|
|
184
191
|
const withSummary = (yield table
|
|
@@ -275,8 +282,13 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
275
282
|
`orphaned=${orphanedProjects.length}`,
|
|
276
283
|
`stale_chunker=${staleChunkerProjects.length}`,
|
|
277
284
|
`stale_embedding=${staleEmbeddingProjects.length}`,
|
|
285
|
+
`schema_dim=${physicalDim !== null && physicalDim !== void 0 ? physicalDim : "none"}`,
|
|
286
|
+
`schema_dim_ok=${schemaGap ? "false" : "true"}`,
|
|
278
287
|
];
|
|
279
288
|
console.log(fields.join("\t"));
|
|
289
|
+
if (schemaGap) {
|
|
290
|
+
console.log((0, config_1.schemaDimAgentRow)(schemaGap));
|
|
291
|
+
}
|
|
280
292
|
for (const p of staleChunkerProjects) {
|
|
281
293
|
const gap = (0, config_1.describeChunkerGap)(p.chunkerVersion);
|
|
282
294
|
if (!gap)
|
|
@@ -307,12 +319,24 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
307
319
|
`current_dim=${gap.toDim}`,
|
|
308
320
|
`dim_changed=${gap.dimChanged}`,
|
|
309
321
|
`severity=${gap.severity}`,
|
|
310
|
-
|
|
322
|
+
// A dim change can't be fixed by a per-project reset (the shared
|
|
323
|
+
// table is fixed-width) — point at the global rebuild instead.
|
|
324
|
+
`fix=${gap.dimChanged ? config_1.REBUILD_COMMAND : `gmax index --reset (in ${p.root})`}`,
|
|
311
325
|
].join("\t"));
|
|
312
326
|
}
|
|
313
327
|
}
|
|
314
328
|
else {
|
|
315
329
|
console.log("\nIndex Health\n");
|
|
330
|
+
// Physical schema width — a mismatch means every write throws until a
|
|
331
|
+
// global rebuild (the shared fixed-width table can't be reshaped by a
|
|
332
|
+
// per-project reset). Surfaced first because it's the most severe.
|
|
333
|
+
if (schemaGap) {
|
|
334
|
+
console.log(`FAIL Schema: vector table is ${schemaGap.tableDim}d, config expects ${schemaGap.configDim}d`);
|
|
335
|
+
console.log(` run '${config_1.REBUILD_COMMAND}' (drops + reindexes all projects at the new width)`);
|
|
336
|
+
}
|
|
337
|
+
else if (physicalDim) {
|
|
338
|
+
console.log(`ok Schema: vector table is ${physicalDim}d`);
|
|
339
|
+
}
|
|
316
340
|
// Disk space
|
|
317
341
|
if (diskLevel !== "ok") {
|
|
318
342
|
console.log(`WARN Disk: ${formatSize(availBytes)} available (${diskLevel})`);
|
|
@@ -370,17 +394,21 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
370
394
|
}
|
|
371
395
|
// Index built with a different embedding model/dim than the current
|
|
372
396
|
// config. A dim change is breaking (search scores are invalid until a
|
|
373
|
-
// re-embed); a same-dim model swap is additive.
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
397
|
+
// re-embed); a same-dim model swap is additive. Recovery differs by kind:
|
|
398
|
+
// a same-dim model swap is fixed by a per-project `gmax index --reset`,
|
|
399
|
+
// but a dim change can't be — the shared table is fixed-width, so it
|
|
400
|
+
// needs the global rebuild (see the Schema check above).
|
|
377
401
|
if (staleEmbeddingProjects.length > 0) {
|
|
378
402
|
const gaps = staleEmbeddingProjects.map((p) => (0, config_1.describeEmbeddingGap)({ modelTier: p.modelTier, vectorDim: p.vectorDim }, {
|
|
379
403
|
modelTier: globalConfig.modelTier,
|
|
380
404
|
vectorDim: globalConfig.vectorDim,
|
|
381
405
|
}));
|
|
382
406
|
const anyBreaking = gaps.some((g) => (g === null || g === void 0 ? void 0 : g.severity) === "breaking");
|
|
383
|
-
|
|
407
|
+
const anyDimChange = gaps.some((g) => g === null || g === void 0 ? void 0 : g.dimChanged);
|
|
408
|
+
const headerFix = anyDimChange
|
|
409
|
+
? `run '${config_1.REBUILD_COMMAND}' (dim change needs a full rebuild)`
|
|
410
|
+
: "run 'gmax index --reset' per project";
|
|
411
|
+
console.log(`${anyBreaking ? "WARN" : "INFO"} Stale embedding: ${staleEmbeddingProjects.length} project(s) indexed with a different embedding model/dim — ${headerFix}`);
|
|
384
412
|
staleEmbeddingProjects.forEach((p, i) => {
|
|
385
413
|
const gap = gaps[i];
|
|
386
414
|
if (!gap)
|
|
@@ -389,7 +417,9 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
389
417
|
? `${gap.fromDim}d→${gap.toDim}d`
|
|
390
418
|
: `model ${gap.fromModel}→${gap.toModel}`;
|
|
391
419
|
console.log(` - ${p.name || path.basename(p.root)} (${change}, ${gap.severity})`);
|
|
392
|
-
console.log(
|
|
420
|
+
console.log(gap.dimChanged
|
|
421
|
+
? ` run '${config_1.REBUILD_COMMAND}'`
|
|
422
|
+
: ` run 'gmax index --reset' in ${p.root}`);
|
|
393
423
|
});
|
|
394
424
|
}
|
|
395
425
|
// Projects
|
package/dist/commands/droid.js
CHANGED
|
@@ -69,17 +69,50 @@ function parseJsonWithComments(content) {
|
|
|
69
69
|
.split("\n")
|
|
70
70
|
.map((line) => line.replace(/^\s*\/\/.*$/, ""))
|
|
71
71
|
.join("\n");
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
72
|
+
// An empty / whitespace-only file is a legitimate "no settings yet" -> {}.
|
|
73
|
+
// Anything else that fails to parse is a real, user-owned file we must NOT
|
|
74
|
+
// silently coerce to {} (that would clobber it on the next save) — let the
|
|
75
|
+
// caller decide.
|
|
76
|
+
if (stripped.trim() === "")
|
|
76
77
|
return {};
|
|
77
|
-
|
|
78
|
+
return JSON.parse(stripped);
|
|
78
79
|
}
|
|
79
80
|
function loadSettings(settingsPath) {
|
|
80
81
|
if (!node_fs_1.default.existsSync(settingsPath))
|
|
81
82
|
return {};
|
|
82
|
-
|
|
83
|
+
const raw = node_fs_1.default.readFileSync(settingsPath, "utf-8");
|
|
84
|
+
try {
|
|
85
|
+
return parseJsonWithComments(raw);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
throw new Error(`Refusing to touch ${settingsPath}: it contains invalid JSON ` +
|
|
89
|
+
`(${err.message}). Fix or remove the file, then re-run.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** True when a hook entry was installed by gmax — its command points at our
|
|
93
|
+
* hooks dir. Used to remove only gmax entries on uninstall. */
|
|
94
|
+
function isGmaxHookEntry(entry, hooksDir) {
|
|
95
|
+
var _a, _b;
|
|
96
|
+
return (_b = (_a = entry.hooks) === null || _a === void 0 ? void 0 : _a.some((h) => { var _a; return (_a = h.command) === null || _a === void 0 ? void 0 : _a.includes(hooksDir); })) !== null && _b !== void 0 ? _b : false;
|
|
97
|
+
}
|
|
98
|
+
/** Strip gmax hook entries from settings.hooks in place, preserving unrelated
|
|
99
|
+
* user hooks. Returns true if anything was removed. */
|
|
100
|
+
function removeGmaxHooks(settings, hooksDir) {
|
|
101
|
+
if (!settings.hooks)
|
|
102
|
+
return false;
|
|
103
|
+
let changed = false;
|
|
104
|
+
for (const [event, entries] of Object.entries(settings.hooks)) {
|
|
105
|
+
const kept = entries.filter((e) => !isGmaxHookEntry(e, hooksDir));
|
|
106
|
+
if (kept.length !== entries.length)
|
|
107
|
+
changed = true;
|
|
108
|
+
if (kept.length > 0)
|
|
109
|
+
settings.hooks[event] = kept;
|
|
110
|
+
else
|
|
111
|
+
delete settings.hooks[event];
|
|
112
|
+
}
|
|
113
|
+
if (Object.keys(settings.hooks).length === 0)
|
|
114
|
+
delete settings.hooks;
|
|
115
|
+
return changed;
|
|
83
116
|
}
|
|
84
117
|
function saveSettings(settingsPath, settings) {
|
|
85
118
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(settingsPath), { recursive: true });
|
|
@@ -103,10 +136,14 @@ function mergeHooks(existing, incoming) {
|
|
|
103
136
|
function installPlugin() {
|
|
104
137
|
return __awaiter(this, void 0, void 0, function* () {
|
|
105
138
|
const root = resolveDroidRoot();
|
|
139
|
+
const settingsPath = node_path_1.default.join(root, "settings.json");
|
|
140
|
+
// Validate/parse settings BEFORE writing anything. A malformed, user-owned
|
|
141
|
+
// settings.json aborts the install here — otherwise we'd either clobber it
|
|
142
|
+
// with {} or leave half-written hook scripts behind on the failure path.
|
|
143
|
+
const settings = loadSettings(settingsPath);
|
|
106
144
|
const gmaxBin = resolveGmaxBin();
|
|
107
145
|
const hooksDir = node_path_1.default.join(root, "hooks", "gmax");
|
|
108
146
|
const skillsDir = node_path_1.default.join(root, "skills", "gmax");
|
|
109
|
-
const settingsPath = node_path_1.default.join(root, "settings.json");
|
|
110
147
|
// 1. Install hook scripts (start/stop daemon)
|
|
111
148
|
const startScript = `
|
|
112
149
|
const { execFileSync } = require("child_process");
|
|
@@ -166,7 +203,6 @@ try { execFileSync("gmax", ["watch", "stop", "--all"], { stdio: "ignore", timeou
|
|
|
166
203
|
},
|
|
167
204
|
],
|
|
168
205
|
};
|
|
169
|
-
const settings = loadSettings(settingsPath);
|
|
170
206
|
settings.enableHooks = true;
|
|
171
207
|
settings.allowBackgroundProcesses = true;
|
|
172
208
|
settings.hooks = mergeHooks(settings.hooks, hookConfig);
|
|
@@ -179,12 +215,28 @@ function uninstallPlugin() {
|
|
|
179
215
|
const root = resolveDroidRoot();
|
|
180
216
|
const hooksDir = node_path_1.default.join(root, "hooks", "gmax");
|
|
181
217
|
const skillsDir = node_path_1.default.join(root, "skills", "gmax");
|
|
218
|
+
const settingsPath = node_path_1.default.join(root, "settings.json");
|
|
182
219
|
if (node_fs_1.default.existsSync(hooksDir))
|
|
183
220
|
node_fs_1.default.rmSync(hooksDir, { recursive: true, force: true });
|
|
184
221
|
if (node_fs_1.default.existsSync(skillsDir))
|
|
185
222
|
node_fs_1.default.rmSync(skillsDir, { recursive: true, force: true });
|
|
223
|
+
// Remove only gmax hook entries from settings.json, preserving unrelated user
|
|
224
|
+
// hooks. enableHooks/allowBackgroundProcesses are left alone — other hooks may
|
|
225
|
+
// depend on them.
|
|
226
|
+
if (node_fs_1.default.existsSync(settingsPath)) {
|
|
227
|
+
try {
|
|
228
|
+
const settings = loadSettings(settingsPath);
|
|
229
|
+
if (removeGmaxHooks(settings, hooksDir)) {
|
|
230
|
+
saveSettings(settingsPath, settings);
|
|
231
|
+
console.log("✅ Removed gmax hooks from settings.json");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
// Don't clobber an invalid settings file on uninstall either.
|
|
236
|
+
console.warn(`⚠️ Skipped settings cleanup: ${err.message}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
186
239
|
console.log("✅ gmax removed from Factory Droid");
|
|
187
|
-
console.log("NOTE: You may want to manually clean up 'hooks' in ~/.factory/settings.json");
|
|
188
240
|
});
|
|
189
241
|
}
|
|
190
242
|
exports.installDroid = new commander_1.Command("install-droid")
|
package/dist/commands/mcp.js
CHANGED
|
@@ -773,7 +773,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
773
773
|
"is_exported",
|
|
774
774
|
"defined_symbols",
|
|
775
775
|
])
|
|
776
|
-
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND
|
|
776
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND ${(0, filter_builder_1.pathStartsWith)(prefix)}`)
|
|
777
777
|
.limit(10)
|
|
778
778
|
.toArray();
|
|
779
779
|
if (rows.length === 0) {
|
|
@@ -867,7 +867,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
867
867
|
const metaRows = yield table
|
|
868
868
|
.query()
|
|
869
869
|
.select(["is_exported", "start_line", "end_line"])
|
|
870
|
-
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND
|
|
870
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND ${(0, filter_builder_1.pathStartsWith)(prefix)}`)
|
|
871
871
|
.limit(1)
|
|
872
872
|
.toArray();
|
|
873
873
|
const exported = metaRows.length > 0 && Boolean(metaRows[0].is_exported);
|
|
@@ -982,7 +982,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
982
982
|
const defRows = yield table
|
|
983
983
|
.query()
|
|
984
984
|
.select(["path", "start_line", "is_exported"])
|
|
985
|
-
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND
|
|
985
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND ${(0, filter_builder_1.pathStartsWith)(prefix)}`)
|
|
986
986
|
.limit(1)
|
|
987
987
|
.toArray();
|
|
988
988
|
if (defRows.length === 0) {
|
|
@@ -1037,7 +1037,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1037
1037
|
"referenced_symbols",
|
|
1038
1038
|
"is_exported",
|
|
1039
1039
|
])
|
|
1040
|
-
.where(
|
|
1040
|
+
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
1041
1041
|
.limit(500000)
|
|
1042
1042
|
.toArray();
|
|
1043
1043
|
if (rows.length === 0) {
|
|
@@ -1221,7 +1221,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1221
1221
|
const absPrefix = path.isAbsolute(pathPrefix)
|
|
1222
1222
|
? pathPrefix
|
|
1223
1223
|
: path.resolve(projectRoot, pathPrefix);
|
|
1224
|
-
query = query.where(
|
|
1224
|
+
query = query.where((0, filter_builder_1.pathStartsWith)((0, filter_builder_1.normalizePath)(absPrefix)));
|
|
1225
1225
|
}
|
|
1226
1226
|
const rows = yield query.toArray();
|
|
1227
1227
|
const map = new Map();
|
|
@@ -1388,7 +1388,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1388
1388
|
"defined_symbols",
|
|
1389
1389
|
"referenced_symbols",
|
|
1390
1390
|
])
|
|
1391
|
-
.where(
|
|
1391
|
+
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
1392
1392
|
.limit(200000)
|
|
1393
1393
|
.toArray();
|
|
1394
1394
|
if (rows.length === 0) {
|
|
@@ -1829,7 +1829,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1829
1829
|
"role",
|
|
1830
1830
|
"_distance",
|
|
1831
1831
|
])
|
|
1832
|
-
.where(
|
|
1832
|
+
.where((0, filter_builder_1.pathStartsWith)(`${projectRoot}/`))
|
|
1833
1833
|
.limit(limit + 5)
|
|
1834
1834
|
.toArray();
|
|
1835
1835
|
let filtered = results.filter((r) => !(r.path === source.path && r.start_line === source.start_line));
|
package/dist/commands/plugin.js
CHANGED
|
@@ -43,6 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.plugin = void 0;
|
|
46
|
+
exports.installAll = installAll;
|
|
46
47
|
const node_child_process_1 = require("node:child_process");
|
|
47
48
|
const fs = __importStar(require("node:fs"));
|
|
48
49
|
const os = __importStar(require("node:os"));
|
|
@@ -139,6 +140,39 @@ function getClients() {
|
|
|
139
140
|
},
|
|
140
141
|
];
|
|
141
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Install gmax plugins for every detected client; returns the number installed.
|
|
145
|
+
* Deliberately does NOT call gracefulExit — callers decide how to finish. This
|
|
146
|
+
* lets `gmax setup` keep running (and print its outro) after installing, while
|
|
147
|
+
* the `add` subcommand still exits the process itself.
|
|
148
|
+
*/
|
|
149
|
+
function installAll() {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
const clients = getClients();
|
|
152
|
+
console.log("gmax plugin add — detecting clients...\n");
|
|
153
|
+
let installed = 0;
|
|
154
|
+
for (const client of clients) {
|
|
155
|
+
if (!client.detect()) {
|
|
156
|
+
console.log(` skip ${client.name} — not found`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
yield client.install();
|
|
161
|
+
installed++;
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.error(` FAIL ${client.name} — ${err instanceof Error ? err.message : String(err)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (installed === 0) {
|
|
168
|
+
console.log("\nNo supported clients found. Install one of: claude, opencode, codex, droid");
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log(`\n${installed} plugin(s) installed.`);
|
|
172
|
+
}
|
|
173
|
+
return installed;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
142
176
|
// --- Subcommands ---
|
|
143
177
|
const addCmd = new commander_1.Command("add")
|
|
144
178
|
.description("Install or update gmax plugins")
|
|
@@ -164,27 +198,7 @@ const addCmd = new commander_1.Command("add")
|
|
|
164
198
|
return;
|
|
165
199
|
}
|
|
166
200
|
// Install all detected
|
|
167
|
-
|
|
168
|
-
let installed = 0;
|
|
169
|
-
for (const client of clients) {
|
|
170
|
-
if (!client.detect()) {
|
|
171
|
-
console.log(` skip ${client.name} — not found`);
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
yield client.install();
|
|
176
|
-
installed++;
|
|
177
|
-
}
|
|
178
|
-
catch (err) {
|
|
179
|
-
console.error(` FAIL ${client.name} — ${err instanceof Error ? err.message : String(err)}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (installed === 0) {
|
|
183
|
-
console.log("\nNo supported clients found. Install one of: claude, opencode, codex, droid");
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
console.log(`\n${installed} plugin(s) installed.`);
|
|
187
|
-
}
|
|
201
|
+
yield installAll();
|
|
188
202
|
yield (0, exit_1.gracefulExit)();
|
|
189
203
|
}));
|
|
190
204
|
const removeCmd = new commander_1.Command("remove")
|
package/dist/commands/project.js
CHANGED
|
@@ -79,7 +79,7 @@ exports.project = new commander_1.Command("project")
|
|
|
79
79
|
"defined_symbols",
|
|
80
80
|
"referenced_symbols",
|
|
81
81
|
])
|
|
82
|
-
.where(
|
|
82
|
+
.where((0, filter_builder_1.pathStartsWith)(prefix))
|
|
83
83
|
.limit(200000)
|
|
84
84
|
.toArray();
|
|
85
85
|
if (rows.length === 0) {
|
package/dist/commands/remove.js
CHANGED
|
@@ -133,10 +133,17 @@ Examples:
|
|
|
133
133
|
(0, watcher_store_1.unregisterWatcher)(watcher.pid);
|
|
134
134
|
}
|
|
135
135
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
136
|
+
// Slash-terminate so removing /repo/app can't also delete the sibling
|
|
137
|
+
// /repo/app2 — VectorDB matches `path LIKE prefix%` and MetaCache
|
|
138
|
+
// matches `key.startsWith(prefix)`, both of which bleed across siblings
|
|
139
|
+
// without the trailing slash. Mirrors the daemon remove path.
|
|
140
|
+
const rootPrefix = projectRoot.endsWith("/")
|
|
141
|
+
? projectRoot
|
|
142
|
+
: `${projectRoot}/`;
|
|
136
143
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
137
|
-
yield vectorDb.deletePathsWithPrefix(
|
|
144
|
+
yield vectorDb.deletePathsWithPrefix(rootPrefix);
|
|
138
145
|
metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
139
|
-
const keys = yield metaCache.getKeysWithPrefix(
|
|
146
|
+
const keys = yield metaCache.getKeysWithPrefix(rootPrefix);
|
|
140
147
|
for (const key of keys) {
|
|
141
148
|
metaCache.delete(key);
|
|
142
149
|
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.repair = void 0;
|
|
49
|
+
const readline = __importStar(require("node:readline"));
|
|
50
|
+
const commander_1 = require("commander");
|
|
51
|
+
const ora_1 = __importDefault(require("ora"));
|
|
52
|
+
const config_1 = require("../config");
|
|
53
|
+
const index_config_1 = require("../lib/index/index-config");
|
|
54
|
+
const vector_db_1 = require("../lib/store/vector-db");
|
|
55
|
+
const exit_1 = require("../lib/utils/exit");
|
|
56
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
57
|
+
function confirm(message) {
|
|
58
|
+
const rl = readline.createInterface({
|
|
59
|
+
input: process.stdin,
|
|
60
|
+
output: process.stdout,
|
|
61
|
+
});
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
64
|
+
rl.close();
|
|
65
|
+
resolve(answer.toLowerCase() === "y");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
exports.repair = new commander_1.Command("repair")
|
|
70
|
+
.description("Repair the centralized index (recover from a schema mismatch)")
|
|
71
|
+
.option("--rebuild", "Drop the shared vector table and re-index every project at the configured embedding dim", false)
|
|
72
|
+
.option("-y, --yes", "Skip the confirmation prompt", false)
|
|
73
|
+
.addHelpText("after", `
|
|
74
|
+
The shared LanceDB \`chunks\` table is fixed-width at creation. Switching model
|
|
75
|
+
tiers (e.g. small 384d -> standard 768d) strands the table at the old width, so
|
|
76
|
+
every write then fails. A per-project \`gmax index --reset\` only deletes rows —
|
|
77
|
+
it can't change the table width. \`${config_1.REBUILD_COMMAND}\` drops the table and
|
|
78
|
+
re-embeds all projects at the current dim.
|
|
79
|
+
|
|
80
|
+
Examples:
|
|
81
|
+
gmax repair Show schema status and what a rebuild would do
|
|
82
|
+
gmax repair --rebuild Drop the table and re-index every project
|
|
83
|
+
gmax repair --rebuild -y Rebuild without the confirmation prompt
|
|
84
|
+
`)
|
|
85
|
+
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
86
|
+
var _a, _b, _c;
|
|
87
|
+
try {
|
|
88
|
+
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
89
|
+
const configDim = globalConfig.vectorDim;
|
|
90
|
+
// Physical width of the on-disk table (null if no table yet). Opened
|
|
91
|
+
// read-only; safe to inspect even while the daemon holds the table.
|
|
92
|
+
let physicalDim = null;
|
|
93
|
+
const probe = new vector_db_1.VectorDB(config_1.PATHS.lancedbDir);
|
|
94
|
+
try {
|
|
95
|
+
physicalDim = yield probe.getSchemaVectorDim();
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
yield probe.close();
|
|
99
|
+
}
|
|
100
|
+
const projects = (0, project_registry_1.listProjects)().filter((p) => p.status === "indexed" || p.status === "pending");
|
|
101
|
+
const dimLine = physicalDim == null
|
|
102
|
+
? "Vector table: none yet"
|
|
103
|
+
: physicalDim === configDim
|
|
104
|
+
? `Vector table: ${physicalDim}d (matches config)`
|
|
105
|
+
: `Vector table: ${physicalDim}d, config expects ${configDim}d (MISMATCH)`;
|
|
106
|
+
console.log(dimLine);
|
|
107
|
+
if (!opts.rebuild) {
|
|
108
|
+
if (physicalDim != null && physicalDim !== configDim) {
|
|
109
|
+
console.log(`\nRun '${config_1.REBUILD_COMMAND}' to drop the table and re-index ` +
|
|
110
|
+
`${projects.length} project(s) at ${configDim}d.`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(`\nNothing to repair. Pass --rebuild to force a full drop + re-index anyway.`);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (projects.length === 0) {
|
|
118
|
+
console.log("\nNo indexed projects to rebuild.");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const totalChunks = projects.reduce((sum, p) => { var _a; return sum + ((_a = p.chunkCount) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
122
|
+
console.log(`\nThis will DROP the shared vector table and re-embed ${projects.length} project(s)` +
|
|
123
|
+
(totalChunks > 0
|
|
124
|
+
? ` (~${totalChunks.toLocaleString()} chunks).`
|
|
125
|
+
: "."));
|
|
126
|
+
for (const p of projects) {
|
|
127
|
+
console.log(` - ${p.name}`);
|
|
128
|
+
}
|
|
129
|
+
if (!opts.yes) {
|
|
130
|
+
const ok = yield confirm("\nContinue?");
|
|
131
|
+
if (!ok) {
|
|
132
|
+
console.log("Cancelled.");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// The rebuild must run through the daemon — it is the single writer for
|
|
137
|
+
// the shared table. Refuse rather than risk a torn drop alongside a
|
|
138
|
+
// daemon mid-flush.
|
|
139
|
+
const { ensureDaemonRunning, sendStreamingCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
140
|
+
if (!(yield ensureDaemonRunning())) {
|
|
141
|
+
console.error("Could not start the gmax daemon. Start it with 'gmax watch --daemon -b' and retry.");
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const spinner = (0, ora_1.default)({
|
|
146
|
+
text: "Dropping vector table...",
|
|
147
|
+
isSilent: !process.stdout.isTTY,
|
|
148
|
+
}).start();
|
|
149
|
+
try {
|
|
150
|
+
const done = yield sendStreamingCommand({ cmd: "repair" }, (msg) => {
|
|
151
|
+
var _a, _b, _c, _d;
|
|
152
|
+
if (msg.phase === "drop") {
|
|
153
|
+
spinner.text = "Dropping vector table...";
|
|
154
|
+
}
|
|
155
|
+
else if (msg.phase === "reindex") {
|
|
156
|
+
const doneN = (_a = msg.projectsDone) !== null && _a !== void 0 ? _a : 0;
|
|
157
|
+
const totalN = (_b = msg.projectsTotal) !== null && _b !== void 0 ? _b : projects.length;
|
|
158
|
+
const processed = (_c = msg.processed) !== null && _c !== void 0 ? _c : 0;
|
|
159
|
+
const total = (_d = msg.total) !== null && _d !== void 0 ? _d : 0;
|
|
160
|
+
const counts = total > 0 ? ` ${processed}/${total}` : "";
|
|
161
|
+
spinner.text = `Re-indexing ${msg.project} (${doneN + 1}/${totalN})${counts}`;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
if (!done.ok) {
|
|
165
|
+
spinner.fail("Rebuild failed");
|
|
166
|
+
throw new Error((_a = done.error) !== null && _a !== void 0 ? _a : "daemon repair failed");
|
|
167
|
+
}
|
|
168
|
+
const rebuilt = (_b = done.projects) !== null && _b !== void 0 ? _b : 0;
|
|
169
|
+
const indexed = (_c = done.indexed) !== null && _c !== void 0 ? _c : 0;
|
|
170
|
+
spinner.succeed(`Rebuilt ${rebuilt} project(s) at ${configDim}d • ${indexed.toLocaleString()} chunks`);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
if (spinner.isSpinning)
|
|
174
|
+
spinner.fail("Rebuild failed");
|
|
175
|
+
throw e;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
180
|
+
console.error("Failed to repair:", message);
|
|
181
|
+
process.exitCode = 1;
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
yield (0, exit_1.gracefulExit)();
|
|
185
|
+
}
|
|
186
|
+
}));
|
|
@@ -130,7 +130,18 @@ function runSearch(params) {
|
|
|
130
130
|
if (!indexState && (locked || projectStatus === "pending")) {
|
|
131
131
|
indexState = { indexing: true, pendingFiles: 0 };
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
// Decide first-run auto-index by whether the project being searched has
|
|
134
|
+
// rows — NOT whether the shared store has any rows at all. The store is
|
|
135
|
+
// centralized, so a global hasAnyRows() lets a sibling project's rows
|
|
136
|
+
// suppress this project's first-run index. Cross-project mode keeps the
|
|
137
|
+
// global check: it spans already-indexed projects and must not first-run
|
|
138
|
+
// a single directory just because the cwd happens to be unindexed.
|
|
139
|
+
const crossProject = !!options.allProjects ||
|
|
140
|
+
!!options.projects ||
|
|
141
|
+
!!options.excludeProjects;
|
|
142
|
+
const hasRows = crossProject
|
|
143
|
+
? yield vectorDb.hasAnyRows()
|
|
144
|
+
: yield vectorDb.hasRowsForPath(effectiveRoot);
|
|
134
145
|
const needsSync = options.sync || !hasRows;
|
|
135
146
|
if (needsSync) {
|
|
136
147
|
const isTTY = process.stdout.isTTY;
|
package/dist/commands/setup.js
CHANGED
|
@@ -154,8 +154,10 @@ exports.setup = new commander_1.Command("setup")
|
|
|
154
154
|
initialValue: true,
|
|
155
155
|
});
|
|
156
156
|
if (!p.isCancel(installPlugins) && installPlugins) {
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
// Call installAll() directly, not `plugin` (bare status) or the `add`
|
|
158
|
+
// subcommand — both gracefulExit() before setup's outro can run.
|
|
159
|
+
const { installAll } = yield Promise.resolve().then(() => __importStar(require("./plugin")));
|
|
160
|
+
yield installAll();
|
|
159
161
|
}
|
|
160
162
|
p.outro(`Ready — ${selectedTier.label}, ${embedMode === "gpu" ? "GPU" : "CPU"} mode`);
|
|
161
163
|
yield (0, exit_1.gracefulExit)();
|