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
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"version": "0.
|
|
2
|
+
"name": "grepmax",
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Robert Owens",
|
|
7
7
|
"email": "robowens@me.com"
|
|
8
8
|
},
|
|
9
9
|
"hooks": "./hooks.json",
|
|
10
|
-
"homepage": "https://github.com/reowens/
|
|
11
|
-
"repository": "https://github.com/reowens/
|
|
10
|
+
"homepage": "https://github.com/reowens/grepmax",
|
|
11
|
+
"repository": "https://github.com/reowens/grepmax",
|
|
12
12
|
"license": "Apache-2.0",
|
|
13
13
|
"keywords": [
|
|
14
14
|
"search",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const _path = require("node:path");
|
|
3
|
+
const http = require("node:http");
|
|
4
|
+
const { spawn } = require("node:child_process");
|
|
5
|
+
|
|
6
|
+
function isMlxRunning() {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const req = http.get(
|
|
9
|
+
{ hostname: "127.0.0.1", port: 8100, path: "/health", timeout: 1000 },
|
|
10
|
+
(res) => {
|
|
11
|
+
res.resume();
|
|
12
|
+
resolve(res.statusCode === 200);
|
|
13
|
+
},
|
|
14
|
+
);
|
|
15
|
+
req.on("error", () => resolve(false));
|
|
16
|
+
req.on("timeout", () => {
|
|
17
|
+
req.destroy();
|
|
18
|
+
resolve(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function startMlxServer() {
|
|
24
|
+
const pluginRoot = __dirname.replace(/\/hooks$/, "");
|
|
25
|
+
const gmaxRoot = _path.resolve(pluginRoot, "../..");
|
|
26
|
+
const serverDir = _path.join(gmaxRoot, "mlx-embed-server");
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(_path.join(serverDir, "server.py"))) return;
|
|
29
|
+
|
|
30
|
+
const logPath = "/tmp/mlx-embed-server.log";
|
|
31
|
+
const out = fs.openSync(logPath, "a");
|
|
32
|
+
|
|
33
|
+
const child = spawn("uv", ["run", "python", "server.py"], {
|
|
34
|
+
cwd: serverDir,
|
|
35
|
+
detached: true,
|
|
36
|
+
stdio: ["ignore", out, out],
|
|
37
|
+
});
|
|
38
|
+
child.unref();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
// Start MLX embed server if not running (set GMAX_EMBED_MODE=cpu to skip)
|
|
43
|
+
const embedMode =
|
|
44
|
+
process.env.GMAX_EMBED_MODE || process.env.OSGREP_EMBED_MODE || "auto";
|
|
45
|
+
if (embedMode !== "cpu") {
|
|
46
|
+
const mlxUp = await isMlxRunning();
|
|
47
|
+
if (!mlxUp) {
|
|
48
|
+
startMlxServer();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MCP server handles indexing and search directly — no daemon needed
|
|
53
|
+
const response = {
|
|
54
|
+
hookSpecificOutput: {
|
|
55
|
+
hookEventName: "SessionStart",
|
|
56
|
+
additionalContext:
|
|
57
|
+
'gmax MCP ready; prefer `gmax "<complete question>"` over grep (plain output is agent-friendly).',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
process.stdout.write(JSON.stringify(response));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
main();
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: Semantic code search. Use alongside grep - grep for exact strings,
|
|
4
|
-
allowed-tools: "
|
|
2
|
+
name: gmax
|
|
3
|
+
description: Semantic code search. Use alongside grep - grep for exact strings, gmax for concepts.
|
|
4
|
+
allowed-tools: "mcp__grepmax__semantic_search, mcp__grepmax__code_skeleton, mcp__grepmax__trace_calls, mcp__grepmax__list_symbols, mcp__grepmax__index_status, Bash(gmax:*), Read"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## What
|
|
7
|
+
## What gmax does
|
|
8
8
|
|
|
9
|
-
Finds code by meaning. When you'd ask a colleague "where do we handle auth?", use
|
|
9
|
+
Finds code by meaning. When you'd ask a colleague "where do we handle auth?", use gmax.
|
|
10
10
|
|
|
11
11
|
- grep/ripgrep: exact string match, fast
|
|
12
|
-
-
|
|
12
|
+
- gmax: concept match, finds code you couldn't grep for
|
|
13
13
|
|
|
14
14
|
## MCP tools (preferred)
|
|
15
15
|
|
|
@@ -45,11 +45,11 @@ Check index and daemon health — file count, chunks, embed mode, age, watching
|
|
|
45
45
|
If MCP tools aren't available, use the CLI via Bash:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
gmax "where do we validate user permissions" # Semantic search
|
|
49
|
+
gmax "authentication" --compact # Just file paths + line ranges
|
|
50
|
+
gmax skeleton src/giant-2000-line-file.ts # File structure
|
|
51
|
+
gmax trace handleAuth # Call graph
|
|
52
|
+
gmax symbols booking # Find symbols by name
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
## Output explained (CLI)
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
const fs = require("node:fs");
|
|
2
|
-
const os = require("node:os");
|
|
3
|
-
const _path = require("node:path");
|
|
4
|
-
const http = require("node:http");
|
|
5
|
-
const { spawn } = require("node:child_process");
|
|
6
|
-
|
|
7
|
-
function readPayload() {
|
|
8
|
-
try {
|
|
9
|
-
const raw = fs.readFileSync(0, "utf-8");
|
|
10
|
-
return raw ? JSON.parse(raw) : {};
|
|
11
|
-
} catch {
|
|
12
|
-
return {};
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isServerRunning(cwd) {
|
|
17
|
-
// Read the global server registry (matches how osgrep serve registers)
|
|
18
|
-
const registryPath = _path.join(os.homedir(), ".osgrep", "servers.json");
|
|
19
|
-
try {
|
|
20
|
-
const servers = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
|
|
21
|
-
const match = servers.find((s) => s.projectRoot === cwd);
|
|
22
|
-
if (match && typeof match.pid === "number") {
|
|
23
|
-
process.kill(match.pid, 0); // throws if not running
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
} catch {}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isMlxRunning() {
|
|
31
|
-
return new Promise((resolve) => {
|
|
32
|
-
const req = http.get(
|
|
33
|
-
{ hostname: "127.0.0.1", port: 8100, path: "/health", timeout: 1000 },
|
|
34
|
-
(res) => {
|
|
35
|
-
res.resume();
|
|
36
|
-
resolve(res.statusCode === 200);
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
req.on("error", () => resolve(false));
|
|
40
|
-
req.on("timeout", () => { req.destroy(); resolve(false); });
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function startMlxServer() {
|
|
45
|
-
// Find the mlx-embed-server directory relative to plugin root
|
|
46
|
-
const pluginRoot = __dirname.replace(/\/hooks$/, "");
|
|
47
|
-
const osgrepRoot = _path.resolve(pluginRoot, "../..");
|
|
48
|
-
const serverDir = _path.join(osgrepRoot, "mlx-embed-server");
|
|
49
|
-
|
|
50
|
-
if (!fs.existsSync(_path.join(serverDir, "server.py"))) return;
|
|
51
|
-
|
|
52
|
-
const logPath = "/tmp/mlx-embed-server.log";
|
|
53
|
-
const out = fs.openSync(logPath, "a");
|
|
54
|
-
|
|
55
|
-
const child = spawn("uv", ["run", "python", "server.py"], {
|
|
56
|
-
cwd: serverDir,
|
|
57
|
-
detached: true,
|
|
58
|
-
stdio: ["ignore", out, out],
|
|
59
|
-
});
|
|
60
|
-
child.unref();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function main() {
|
|
64
|
-
const payload = readPayload();
|
|
65
|
-
const cwd = payload.cwd || process.cwd();
|
|
66
|
-
|
|
67
|
-
// Check if osgrep serve is running (read-only — MCP server owns daemon lifecycle)
|
|
68
|
-
const daemonUp = isServerRunning(cwd);
|
|
69
|
-
|
|
70
|
-
// Start MLX embed server if not running (set OSGREP_EMBED_MODE=cpu to skip)
|
|
71
|
-
const embedMode = process.env.OSGREP_EMBED_MODE || "auto";
|
|
72
|
-
if (embedMode !== "cpu") {
|
|
73
|
-
const mlxUp = await isMlxRunning();
|
|
74
|
-
if (!mlxUp) {
|
|
75
|
-
startMlxServer();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const status = daemonUp ? "running" : "starting via MCP";
|
|
80
|
-
const response = {
|
|
81
|
-
hookSpecificOutput: {
|
|
82
|
-
hookEventName: "SessionStart",
|
|
83
|
-
additionalContext:
|
|
84
|
-
`osgrep serve ${status}; prefer \`osgrep "<complete question>"\` over grep (plain output is agent-friendly).`,
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
process.stdout.write(JSON.stringify(response));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
main();
|
|
File without changes
|