grepmax 0.7.42 → 0.7.44
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/README.md +22 -4
- package/dist/commands/add.js +154 -0
- package/dist/commands/claude-code.js +68 -6
- package/dist/commands/opencode.js +40 -8
- package/dist/commands/related.js +6 -3
- package/dist/commands/remove.js +152 -0
- package/dist/commands/search.js +100 -17
- package/dist/commands/status.js +150 -0
- package/dist/commands/symbols.js +7 -3
- package/dist/commands/trace.js +37 -2
- package/dist/config.js +8 -2
- package/dist/index.js +6 -0
- package/dist/lib/graph/graph-builder.js +26 -6
- package/dist/lib/index/ignore-patterns.js +1 -0
- package/dist/lib/index/syncer.js +56 -41
- package/dist/lib/index/watcher.js +0 -51
- package/dist/lib/search/searcher.js +77 -4
- package/dist/lib/store/vector-db.js +40 -45
- package/dist/lib/utils/project-marker.js +75 -0
- package/dist/lib/workers/pool.js +19 -6
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +1 -1
- package/plugins/grepmax/skills/grepmax/SKILL.md +52 -18
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Natural-language search that works like `grep`. Fast, local, and built for codin
|
|
|
24
24
|
- **Role Detection:** Distinguishes `ORCHESTRATION` (high-level logic) from `DEFINITION` (types/classes).
|
|
25
25
|
- **Local & Private:** 100% local embeddings via ONNX (CPU) or MLX (Apple Silicon GPU).
|
|
26
26
|
- **Centralized Index:** One database at `~/.gmax/` — index once, search from anywhere.
|
|
27
|
-
- **LLM Summaries:** Optional Qwen3-Coder generates one-line descriptions per code chunk
|
|
27
|
+
- **LLM Summaries:** Optional Qwen3-Coder generates one-line descriptions per code chunk on demand.
|
|
28
28
|
- **Agent-Ready:** Pointer mode returns metadata (symbol, role, calls, summary) — no code snippets, ~80% fewer tokens.
|
|
29
29
|
|
|
30
30
|
## Quick Start
|
|
@@ -186,7 +186,7 @@ gmax index --reset # Full re-index from scratch
|
|
|
186
186
|
|
|
187
187
|
### `gmax watch`
|
|
188
188
|
|
|
189
|
-
Background file watcher for live reindexing. Watches for file changes and incrementally updates the centralized index.
|
|
189
|
+
Background file watcher for live reindexing. Watches for file changes and incrementally updates the centralized index. Uses FSEvents on macOS (kernel-level, low overhead) and polling on Linux.
|
|
190
190
|
|
|
191
191
|
```bash
|
|
192
192
|
gmax watch -b # Background mode (auto-stops after 30min idle)
|
|
@@ -197,6 +197,17 @@ gmax watch stop --all # Stop all watchers
|
|
|
197
197
|
|
|
198
198
|
The MCP server auto-starts a watcher on session start. You rarely need to run this manually.
|
|
199
199
|
|
|
200
|
+
### `gmax summarize`
|
|
201
|
+
|
|
202
|
+
Generate one-line LLM summaries for indexed chunks. Requires the summarizer server (Qwen3-Coder via MLX on Apple Silicon). Summaries are stored in LanceDB and appear in search results.
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
gmax summarize # Summarize all unsummarized chunks
|
|
206
|
+
gmax summarize --path src/lib/ # Only summarize chunks under a directory
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Summarization is **on-demand only** — it does not run automatically during indexing or file watching. The `summarize_directory` MCP tool provides the same functionality for AI agents.
|
|
210
|
+
|
|
200
211
|
### `gmax serve`
|
|
201
212
|
|
|
202
213
|
HTTP server with live file watching. Useful for non-MCP integrations.
|
|
@@ -307,9 +318,15 @@ To force CPU mode: `GMAX_EMBED_MODE=cpu gmax index`
|
|
|
307
318
|
|
|
308
319
|
### LLM Summaries
|
|
309
320
|
|
|
310
|
-
gmax can generate one-line natural language descriptions for every code chunk using a local LLM (Qwen3-Coder-30B-A3B via MLX). Summaries are
|
|
321
|
+
gmax can generate one-line natural language descriptions for every code chunk using a local LLM (Qwen3-Coder-30B-A3B via MLX). Summaries are stored in LanceDB — zero latency at search time.
|
|
322
|
+
|
|
323
|
+
Summarization is **on-demand**, not automatic. Run `gmax summarize` or use the `summarize_directory` MCP tool after indexing. The summarizer server runs on port `8101` and must be started separately. If unavailable, `gmax summarize` will report the server is not running.
|
|
311
324
|
|
|
312
|
-
|
|
325
|
+
```bash
|
|
326
|
+
gmax summarize # Generate summaries for all unsummarized chunks
|
|
327
|
+
gmax summarize --path src/lib/ # Scope to a directory
|
|
328
|
+
gmax doctor # Check summarizer status + coverage
|
|
329
|
+
```
|
|
313
330
|
|
|
314
331
|
Example search output with summaries:
|
|
315
332
|
```
|
|
@@ -364,6 +381,7 @@ fixtures/
|
|
|
364
381
|
| `GMAX_WORKER_TASK_TIMEOUT_MS` | Worker task timeout in ms | `120000` |
|
|
365
382
|
| `GMAX_MAX_WORKER_MEMORY_MB` | Max worker memory in MB | 50% of system RAM |
|
|
366
383
|
| `GMAX_MAX_PER_FILE` | Default max results per file in search | `3` |
|
|
384
|
+
| `GMAX_WATCH_POLL` | Force polling mode for file watcher (`1` to enable) | Off (FSEvents on macOS) |
|
|
367
385
|
|
|
368
386
|
## Contributing
|
|
369
387
|
|
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.add = void 0;
|
|
46
|
+
const node_child_process_1 = require("node:child_process");
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const commander_1 = require("commander");
|
|
49
|
+
const grammar_loader_1 = require("../lib/index/grammar-loader");
|
|
50
|
+
const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
51
|
+
const syncer_1 = require("../lib/index/syncer");
|
|
52
|
+
const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
53
|
+
const vector_db_1 = require("../lib/store/vector-db");
|
|
54
|
+
const exit_1 = require("../lib/utils/exit");
|
|
55
|
+
const project_marker_1 = require("../lib/utils/project-marker");
|
|
56
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
57
|
+
const project_root_1 = require("../lib/utils/project-root");
|
|
58
|
+
const index_config_1 = require("../lib/index/index-config");
|
|
59
|
+
exports.add = new commander_1.Command("add")
|
|
60
|
+
.description("Add a project to the gmax index")
|
|
61
|
+
.argument("[dir]", "Directory to add (defaults to current directory)")
|
|
62
|
+
.option("--no-index", "Register the project without indexing it")
|
|
63
|
+
.addHelpText("after", `
|
|
64
|
+
Examples:
|
|
65
|
+
gmax add Add the current directory
|
|
66
|
+
gmax add ~/projects/myapp Add a specific project
|
|
67
|
+
gmax add . --no-index Register only, index later with gmax index
|
|
68
|
+
`)
|
|
69
|
+
.action((dir, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
70
|
+
var _a, _b;
|
|
71
|
+
let vectorDb = null;
|
|
72
|
+
try {
|
|
73
|
+
const targetDir = dir ? path.resolve(dir) : process.cwd();
|
|
74
|
+
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(targetDir)) !== null && _a !== void 0 ? _a : targetDir;
|
|
75
|
+
const projectName = path.basename(projectRoot);
|
|
76
|
+
// Check if already registered
|
|
77
|
+
const existing = (0, project_registry_1.getProject)(projectRoot);
|
|
78
|
+
if (existing || (0, project_marker_1.hasMarker)(projectRoot)) {
|
|
79
|
+
console.log(`${projectName} is already added (${(_b = existing === null || existing === void 0 ? void 0 : existing.chunkCount) !== null && _b !== void 0 ? _b : 0} chunks).`);
|
|
80
|
+
console.log(`Run \`gmax index\` to re-index, or \`gmax index --reset\` for a full rebuild.`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Create marker file
|
|
84
|
+
(0, project_marker_1.createMarker)(projectRoot);
|
|
85
|
+
// Register as pending
|
|
86
|
+
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
87
|
+
(0, project_registry_1.registerProject)({
|
|
88
|
+
root: projectRoot,
|
|
89
|
+
name: projectName,
|
|
90
|
+
vectorDim: globalConfig.vectorDim,
|
|
91
|
+
modelTier: globalConfig.modelTier,
|
|
92
|
+
embedMode: globalConfig.embedMode,
|
|
93
|
+
lastIndexed: "",
|
|
94
|
+
chunkCount: 0,
|
|
95
|
+
status: "pending",
|
|
96
|
+
});
|
|
97
|
+
if (!opts.index) {
|
|
98
|
+
console.log(`Registered ${projectName}. Run \`gmax index\` when ready to index.`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Index the project
|
|
102
|
+
yield (0, setup_helpers_1.ensureSetup)();
|
|
103
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
104
|
+
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
105
|
+
yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
|
|
106
|
+
const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, `Adding ${projectName}...`);
|
|
107
|
+
try {
|
|
108
|
+
const result = yield (0, syncer_1.initialSync)({
|
|
109
|
+
projectRoot,
|
|
110
|
+
onProgress,
|
|
111
|
+
});
|
|
112
|
+
const failedSuffix = result.failedFiles > 0 ? ` · ${result.failedFiles} failed` : "";
|
|
113
|
+
spinner.succeed(`Added ${projectName} (${result.total} files, ${result.indexed} chunks${failedSuffix})`);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
// Update status to error
|
|
117
|
+
(0, project_registry_1.registerProject)({
|
|
118
|
+
root: projectRoot,
|
|
119
|
+
name: projectName,
|
|
120
|
+
vectorDim: globalConfig.vectorDim,
|
|
121
|
+
modelTier: globalConfig.modelTier,
|
|
122
|
+
embedMode: globalConfig.embedMode,
|
|
123
|
+
lastIndexed: "",
|
|
124
|
+
chunkCount: 0,
|
|
125
|
+
status: "error",
|
|
126
|
+
});
|
|
127
|
+
spinner.fail(`Failed to index ${projectName}`);
|
|
128
|
+
throw e;
|
|
129
|
+
}
|
|
130
|
+
// Start watcher in background
|
|
131
|
+
try {
|
|
132
|
+
const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "watch", "--path", projectRoot], { detached: true, stdio: "ignore" });
|
|
133
|
+
child.unref();
|
|
134
|
+
console.log(`Watcher started (PID: ${child.pid})`);
|
|
135
|
+
}
|
|
136
|
+
catch (_c) {
|
|
137
|
+
console.log(`Note: could not start watcher. Run: gmax watch --path ${projectRoot} -b`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
142
|
+
console.error("Failed to add project:", message);
|
|
143
|
+
process.exitCode = 1;
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (vectorDb) {
|
|
147
|
+
try {
|
|
148
|
+
yield vectorDb.close();
|
|
149
|
+
}
|
|
150
|
+
catch (_d) { }
|
|
151
|
+
}
|
|
152
|
+
yield (0, exit_1.gracefulExit)();
|
|
153
|
+
}
|
|
154
|
+
}));
|
|
@@ -1,4 +1,37 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -10,6 +43,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
43
|
};
|
|
11
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
45
|
exports.installClaudeCode = void 0;
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
13
48
|
const node_child_process_1 = require("node:child_process");
|
|
14
49
|
const commander_1 = require("commander");
|
|
15
50
|
function runClaudeCommand(args) {
|
|
@@ -29,19 +64,47 @@ function runClaudeCommand(args) {
|
|
|
29
64
|
});
|
|
30
65
|
});
|
|
31
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the gmax package root directory.
|
|
69
|
+
* Works for both npm global installs (symlinked binary) and dev mode.
|
|
70
|
+
* __dirname at runtime is dist/commands/, so go up two levels.
|
|
71
|
+
*/
|
|
72
|
+
function getPackageRoot() {
|
|
73
|
+
return path.resolve(__dirname, "../..");
|
|
74
|
+
}
|
|
32
75
|
function installPlugin() {
|
|
33
76
|
return __awaiter(this, void 0, void 0, function* () {
|
|
34
77
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
78
|
+
const packageRoot = getPackageRoot();
|
|
79
|
+
const marketplacePath = path.resolve(packageRoot);
|
|
80
|
+
// Verify the marketplace.json exists at the package root
|
|
81
|
+
const marketplaceJson = path.join(marketplacePath, ".claude-plugin", "marketplace.json");
|
|
82
|
+
if (!fs.existsSync(marketplaceJson)) {
|
|
83
|
+
console.error(`❌ Could not find marketplace.json at ${marketplaceJson}`);
|
|
84
|
+
console.error(" Is gmax installed correctly?");
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(`Installing plugin from ${marketplacePath}`);
|
|
89
|
+
// Remove old GitHub-based marketplace if present (ignore errors)
|
|
90
|
+
try {
|
|
91
|
+
yield runClaudeCommand(["marketplace", "remove", "grepmax"]);
|
|
92
|
+
}
|
|
93
|
+
catch (_a) {
|
|
94
|
+
// May not exist — fine
|
|
95
|
+
}
|
|
96
|
+
// Add local package directory as marketplace source
|
|
97
|
+
yield runClaudeCommand(["marketplace", "add", marketplacePath]);
|
|
98
|
+
console.log("✔ Marketplace registered (local)");
|
|
99
|
+
// Install the plugin from the local marketplace
|
|
38
100
|
yield runClaudeCommand(["install", "grepmax"]);
|
|
39
101
|
console.log("✅ Successfully installed the gmax plugin for Claude Code");
|
|
40
102
|
console.log("\nNext steps:");
|
|
41
103
|
console.log("1. Restart Claude Code if it's running");
|
|
42
|
-
console.log("2.
|
|
104
|
+
console.log("2. Run `gmax add` in your project to index it");
|
|
43
105
|
console.log("3. Claude will use gmax for semantic code search automatically");
|
|
44
|
-
console.log("
|
|
106
|
+
console.log("\nTo update the plugin after upgrading gmax:");
|
|
107
|
+
console.log(" gmax install-claude-code");
|
|
45
108
|
}
|
|
46
109
|
catch (error) {
|
|
47
110
|
console.error("❌ Error installing plugin:");
|
|
@@ -49,7 +112,6 @@ function installPlugin() {
|
|
|
49
112
|
console.error("\nTroubleshooting:");
|
|
50
113
|
console.error("- Ensure you have Claude Code version 2.0.36 or higher installed");
|
|
51
114
|
console.error("- Try running: claude plugin marketplace list");
|
|
52
|
-
console.error("- Check the Claude Code documentation: https://code.claude.com/docs");
|
|
53
115
|
process.exitCode = 1;
|
|
54
116
|
}
|
|
55
117
|
});
|
|
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.uninstallOpencode = exports.installOpencode = void 0;
|
|
16
|
+
const node_child_process_1 = require("node:child_process");
|
|
16
17
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
18
|
const node_os_1 = __importDefault(require("node:os"));
|
|
18
19
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -20,7 +21,21 @@ const commander_1 = require("commander");
|
|
|
20
21
|
const TOOL_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "tool", "gmax.ts");
|
|
21
22
|
const PLUGIN_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "plugin", "gmax.ts");
|
|
22
23
|
const CONFIG_PATH = node_path_1.default.join(node_os_1.default.homedir(), ".config", "opencode", "opencode.json");
|
|
23
|
-
|
|
24
|
+
function resolveGmaxBin() {
|
|
25
|
+
try {
|
|
26
|
+
return (0, node_child_process_1.execSync)("which gmax", { encoding: "utf-8" }).trim();
|
|
27
|
+
}
|
|
28
|
+
catch (_a) {
|
|
29
|
+
// Fall back to the path of the current process entry point
|
|
30
|
+
const binDir = node_path_1.default.dirname(process.argv[1]);
|
|
31
|
+
const candidate = node_path_1.default.join(binDir, "gmax");
|
|
32
|
+
if (node_fs_1.default.existsSync(candidate))
|
|
33
|
+
return candidate;
|
|
34
|
+
return "gmax";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function buildShimContent(gmaxBin) {
|
|
38
|
+
return `
|
|
24
39
|
import { tool } from "@opencode-ai/plugin";
|
|
25
40
|
|
|
26
41
|
const SKILL = \`
|
|
@@ -54,7 +69,7 @@ export async function handleAuth(req: Request) {
|
|
|
54
69
|
const claims = await validateToken(token);
|
|
55
70
|
if (!claims) return unauthorized();
|
|
56
71
|
const allowed = await checkRole(claims.role, req.path);
|
|
57
|
-
...
|
|
72
|
+
...
|
|
58
73
|
|
|
59
74
|
- **ORCHESTRATION** = contains logic, coordinates other code
|
|
60
75
|
- **DEFINITION** = types, interfaces, classes
|
|
@@ -102,8 +117,14 @@ gmax trace handleRequest
|
|
|
102
117
|
- Don't read entire files. Use the line ranges gmax gives you.
|
|
103
118
|
- If results seem off, rephrase your query like you'd ask a teammate
|
|
104
119
|
|
|
120
|
+
## If Index is Building
|
|
121
|
+
|
|
122
|
+
If you see "Indexing" or "Syncing": STOP. Tell the user the index is building. Ask if they want to wait or proceed with partial results.
|
|
123
|
+
|
|
105
124
|
\`;
|
|
106
125
|
|
|
126
|
+
const GMAX_BIN = "${gmaxBin}";
|
|
127
|
+
|
|
107
128
|
export default tool({
|
|
108
129
|
description: SKILL,
|
|
109
130
|
args: {
|
|
@@ -113,14 +134,23 @@ export default tool({
|
|
|
113
134
|
async execute({ argv }) {
|
|
114
135
|
try {
|
|
115
136
|
// @ts-ignore
|
|
116
|
-
const out = await Bun.spawn([
|
|
137
|
+
const out = await Bun.spawn([GMAX_BIN, ...argv], { stdout: "pipe" }).stdout;
|
|
117
138
|
const text = await new Response(out).text();
|
|
139
|
+
if (text.includes("Indexing") || text.includes("Building") || text.includes("Syncing")) {
|
|
140
|
+
return \`WARN: The index is currently updating.
|
|
141
|
+
|
|
142
|
+
Output so far:
|
|
143
|
+
\${text.trim()}
|
|
144
|
+
|
|
145
|
+
PLEASE READ THE "Indexing" WARNING IN MY SKILL DESCRIPTION.\`;
|
|
146
|
+
}
|
|
118
147
|
return text.trim();
|
|
119
148
|
} catch (err) {
|
|
120
149
|
return \`Error running gmax: \${err}\`;
|
|
121
150
|
}
|
|
122
151
|
},
|
|
123
152
|
})`;
|
|
153
|
+
}
|
|
124
154
|
function install() {
|
|
125
155
|
return __awaiter(this, void 0, void 0, function* () {
|
|
126
156
|
try {
|
|
@@ -134,11 +164,14 @@ function install() {
|
|
|
134
164
|
console.warn("mnt: Failed to delete legacy plugin:", e);
|
|
135
165
|
}
|
|
136
166
|
}
|
|
137
|
-
// 2.
|
|
167
|
+
// 2. Resolve absolute path to gmax binary
|
|
168
|
+
const gmaxBin = resolveGmaxBin();
|
|
169
|
+
console.log(` Resolved gmax binary: ${gmaxBin}`);
|
|
170
|
+
// 3. Create tool shim
|
|
138
171
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(TOOL_PATH), { recursive: true });
|
|
139
|
-
node_fs_1.default.writeFileSync(TOOL_PATH,
|
|
172
|
+
node_fs_1.default.writeFileSync(TOOL_PATH, buildShimContent(gmaxBin));
|
|
140
173
|
console.log("✅ Created tool shim at", TOOL_PATH);
|
|
141
|
-
//
|
|
174
|
+
// 4. Register MCP
|
|
142
175
|
if (!node_fs_1.default.existsSync(CONFIG_PATH)) {
|
|
143
176
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(CONFIG_PATH), { recursive: true });
|
|
144
177
|
node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify({}, null, 2));
|
|
@@ -150,12 +183,11 @@ function install() {
|
|
|
150
183
|
config.mcp = {};
|
|
151
184
|
config.mcp.gmax = {
|
|
152
185
|
type: "local",
|
|
153
|
-
command: [
|
|
186
|
+
command: [gmaxBin, "mcp"],
|
|
154
187
|
enabled: true,
|
|
155
188
|
};
|
|
156
189
|
node_fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
157
190
|
console.log("✅ Registered MCP server in", CONFIG_PATH);
|
|
158
|
-
console.log(" Command: check proper path if 'gmax' is not in PATH of OpenCode.");
|
|
159
191
|
}
|
|
160
192
|
catch (err) {
|
|
161
193
|
console.error("❌ Installation failed:", err);
|
package/dist/commands/related.js
CHANGED
|
@@ -59,16 +59,19 @@ exports.related = new commander_1.Command("related")
|
|
|
59
59
|
.description("Find files related by shared symbol references")
|
|
60
60
|
.argument("<file>", "File path relative to project root")
|
|
61
61
|
.option("-l, --limit <n>", "Max results per direction (default 10)", "10")
|
|
62
|
+
.option("--root <dir>", "Project root directory")
|
|
62
63
|
.action((file, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
64
|
var _a;
|
|
64
65
|
const limit = Math.min(Math.max(Number.parseInt(opts.limit || "10", 10), 1), 25);
|
|
65
66
|
let vectorDb = null;
|
|
66
67
|
try {
|
|
67
|
-
const
|
|
68
|
+
const root = opts.root ? path.resolve(opts.root) : process.cwd();
|
|
69
|
+
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
68
70
|
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
69
71
|
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
70
72
|
const absPath = path.resolve(projectRoot, file);
|
|
71
73
|
const table = yield vectorDb.ensureTable();
|
|
74
|
+
const pathScope = `path LIKE '${(0, filter_builder_1.escapeSqlString)(projectRoot)}/%'`;
|
|
72
75
|
const fileChunks = yield table
|
|
73
76
|
.query()
|
|
74
77
|
.select(["defined_symbols", "referenced_symbols"])
|
|
@@ -94,7 +97,7 @@ exports.related = new commander_1.Command("related")
|
|
|
94
97
|
const rows = yield table
|
|
95
98
|
.query()
|
|
96
99
|
.select(["path"])
|
|
97
|
-
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}')`)
|
|
100
|
+
.where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}') AND ${pathScope}`)
|
|
98
101
|
.limit(3)
|
|
99
102
|
.toArray();
|
|
100
103
|
for (const row of rows) {
|
|
@@ -110,7 +113,7 @@ exports.related = new commander_1.Command("related")
|
|
|
110
113
|
const rows = yield table
|
|
111
114
|
.query()
|
|
112
115
|
.select(["path"])
|
|
113
|
-
.where(`array_contains(referenced_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}')`)
|
|
116
|
+
.where(`array_contains(referenced_symbols, '${(0, filter_builder_1.escapeSqlString)(sym)}') AND ${pathScope}`)
|
|
114
117
|
.limit(20)
|
|
115
118
|
.toArray();
|
|
116
119
|
for (const row of rows) {
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.remove = void 0;
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const readline = __importStar(require("node:readline"));
|
|
48
|
+
const commander_1 = require("commander");
|
|
49
|
+
const meta_cache_1 = require("../lib/store/meta-cache");
|
|
50
|
+
const vector_db_1 = require("../lib/store/vector-db");
|
|
51
|
+
const exit_1 = require("../lib/utils/exit");
|
|
52
|
+
const project_marker_1 = require("../lib/utils/project-marker");
|
|
53
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
54
|
+
const project_root_1 = require("../lib/utils/project-root");
|
|
55
|
+
const watcher_registry_1 = require("../lib/utils/watcher-registry");
|
|
56
|
+
function confirm(message) {
|
|
57
|
+
const rl = readline.createInterface({
|
|
58
|
+
input: process.stdin,
|
|
59
|
+
output: process.stdout,
|
|
60
|
+
});
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve(answer.toLowerCase() === "y");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
exports.remove = new commander_1.Command("remove")
|
|
69
|
+
.description("Remove a project from the gmax index")
|
|
70
|
+
.argument("[dir]", "Directory to remove (defaults to current directory)")
|
|
71
|
+
.option("-f, --force", "Skip confirmation prompt", false)
|
|
72
|
+
.addHelpText("after", `
|
|
73
|
+
Examples:
|
|
74
|
+
gmax remove Remove the current project
|
|
75
|
+
gmax remove ~/projects/app Remove a specific project
|
|
76
|
+
gmax remove --force Skip confirmation
|
|
77
|
+
`)
|
|
78
|
+
.action((dir, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
var _a;
|
|
80
|
+
let vectorDb = null;
|
|
81
|
+
let metaCache = null;
|
|
82
|
+
try {
|
|
83
|
+
const targetDir = dir ? path.resolve(dir) : process.cwd();
|
|
84
|
+
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(targetDir)) !== null && _a !== void 0 ? _a : targetDir;
|
|
85
|
+
const projectName = path.basename(projectRoot);
|
|
86
|
+
const project = (0, project_registry_1.getProject)(projectRoot);
|
|
87
|
+
if (!project) {
|
|
88
|
+
console.log(`${projectName} is not in the gmax index.`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const chunkStr = project.chunkCount
|
|
92
|
+
? ` (${project.chunkCount.toLocaleString()} chunks)`
|
|
93
|
+
: "";
|
|
94
|
+
if (!opts.force) {
|
|
95
|
+
const ok = yield confirm(`Remove ${projectName}${chunkStr} from the index? This deletes all indexed data.`);
|
|
96
|
+
if (!ok) {
|
|
97
|
+
console.log("Cancelled.");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Stop any watcher
|
|
102
|
+
const watcher = (0, watcher_registry_1.getWatcherForProject)(projectRoot);
|
|
103
|
+
if (watcher) {
|
|
104
|
+
console.log(`Stopping watcher (PID: ${watcher.pid})...`);
|
|
105
|
+
try {
|
|
106
|
+
process.kill(watcher.pid, "SIGTERM");
|
|
107
|
+
}
|
|
108
|
+
catch (_b) { }
|
|
109
|
+
for (let i = 0; i < 50; i++) {
|
|
110
|
+
if (!(0, watcher_registry_1.isProcessRunning)(watcher.pid))
|
|
111
|
+
break;
|
|
112
|
+
yield new Promise((r) => setTimeout(r, 100));
|
|
113
|
+
}
|
|
114
|
+
(0, watcher_registry_1.unregisterWatcher)(watcher.pid);
|
|
115
|
+
}
|
|
116
|
+
// Delete vectors from LanceDB
|
|
117
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
118
|
+
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
119
|
+
yield vectorDb.deletePathsWithPrefix(projectRoot);
|
|
120
|
+
// Clean MetaCache entries
|
|
121
|
+
metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
122
|
+
const keys = yield metaCache.getKeysWithPrefix(projectRoot);
|
|
123
|
+
for (const key of keys) {
|
|
124
|
+
metaCache.delete(key);
|
|
125
|
+
}
|
|
126
|
+
// Remove from registry
|
|
127
|
+
(0, project_registry_1.removeProject)(projectRoot);
|
|
128
|
+
// Delete marker file
|
|
129
|
+
(0, project_marker_1.removeMarker)(projectRoot);
|
|
130
|
+
console.log(`Removed ${projectName}${chunkStr}.`);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
134
|
+
console.error("Failed to remove project:", message);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
if (metaCache) {
|
|
139
|
+
try {
|
|
140
|
+
metaCache.close();
|
|
141
|
+
}
|
|
142
|
+
catch (_c) { }
|
|
143
|
+
}
|
|
144
|
+
if (vectorDb) {
|
|
145
|
+
try {
|
|
146
|
+
yield vectorDb.close();
|
|
147
|
+
}
|
|
148
|
+
catch (_d) { }
|
|
149
|
+
}
|
|
150
|
+
yield (0, exit_1.gracefulExit)();
|
|
151
|
+
}
|
|
152
|
+
}));
|