grepmax 0.26.33 → 0.26.35
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 +1 -1
- package/dist/bin.js +150 -0
- package/dist/commands/watch.js +9 -0
- package/dist/config.js +13 -4
- package/dist/lib/utils/autostart.js +6 -0
- package/dist/lib/utils/stores.js +124 -0
- package/package.json +4 -4
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "grepmax",
|
|
11
11
|
"source": "./plugins/grepmax",
|
|
12
12
|
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
13
|
-
"version": "0.26.
|
|
13
|
+
"version": "0.26.35",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.commandTarget = commandTarget;
|
|
38
|
+
exports.registeredForm = registeredForm;
|
|
39
|
+
// Entry point. Picks the gmax home before anything reads config.ts: a command
|
|
40
|
+
// aimed at a project under a secondary store's prefix (src/lib/utils/stores.ts)
|
|
41
|
+
// runs against that store, in-process, so the drive it lives on can always
|
|
42
|
+
// eject. Only Node built-ins and stores.ts may be imported here.
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const stores_1 = require("./lib/utils/stores");
|
|
46
|
+
const DIR_ARG_COMMANDS = new Set(["add", "remove"]);
|
|
47
|
+
function optionValue(argv, names) {
|
|
48
|
+
for (let i = 0; i < argv.length; i++) {
|
|
49
|
+
const a = argv[i];
|
|
50
|
+
for (const n of names) {
|
|
51
|
+
if (a === n && argv[i + 1])
|
|
52
|
+
return argv[i + 1];
|
|
53
|
+
if (a.startsWith(`${n}=`))
|
|
54
|
+
return a.slice(n.length + 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
function commandTarget(argv, cwd) {
|
|
60
|
+
var _a, _b;
|
|
61
|
+
const explicit = (_b = (_a = optionValue(argv, ["--root", "--store"])) !== null && _a !== void 0 ? _a : (argv[0] === "index" ? optionValue(argv, ["--path", "-p"]) : undefined)) !== null && _b !== void 0 ? _b : process.env.GMAX_STORE;
|
|
62
|
+
if (explicit)
|
|
63
|
+
return path.resolve(cwd, explicit);
|
|
64
|
+
if (DIR_ARG_COMMANDS.has(argv[0])) {
|
|
65
|
+
const positional = argv.slice(1).find((a) => !a.startsWith("-"));
|
|
66
|
+
if (positional &&
|
|
67
|
+
(positional.includes(path.sep) ||
|
|
68
|
+
fs.existsSync(path.resolve(cwd, positional)))) {
|
|
69
|
+
return path.resolve(cwd, positional);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return cwd;
|
|
73
|
+
}
|
|
74
|
+
function selectHome() {
|
|
75
|
+
if (process.env.GMAX_HOME)
|
|
76
|
+
return;
|
|
77
|
+
const argv = process.argv.slice(2);
|
|
78
|
+
const match = (0, stores_1.matchStore)(commandTarget(argv, process.cwd()));
|
|
79
|
+
if (match.kind === "primary")
|
|
80
|
+
return;
|
|
81
|
+
if (match.kind === "offline") {
|
|
82
|
+
process.stderr.write(`gmax: this project's index lives on ${match.volume}, which is not mounted (store ${match.store.home}). Connect the drive and run the command again.\n`);
|
|
83
|
+
process.exit(2);
|
|
84
|
+
}
|
|
85
|
+
process.env.GMAX_HOME = match.store.home;
|
|
86
|
+
// A secondary store never runs a daemon: nothing may hold the drive's files
|
|
87
|
+
// open between commands, or the drive could not eject.
|
|
88
|
+
process.env.GMAX_NO_AUTOSTART = "1";
|
|
89
|
+
process.env.GMAX_SECONDARY_STORE = "1";
|
|
90
|
+
seedConfig(match.store.home);
|
|
91
|
+
alignCwdWithRegistry(match.store.home);
|
|
92
|
+
}
|
|
93
|
+
// Node reports the physical working directory, so a shell standing in
|
|
94
|
+
// ~/Development/packages/x (a symlink onto the drive) looks like
|
|
95
|
+
// /Volumes/External/dev/packages/x, while the store registered the path it was
|
|
96
|
+
// given. When the physical cwd sits inside a registered root's realpath,
|
|
97
|
+
// report the cwd in the registered form so project lookup finds it.
|
|
98
|
+
function registeredForm(cwd, roots, real = realpathOr) {
|
|
99
|
+
for (const root of roots) {
|
|
100
|
+
const rootReal = real(root);
|
|
101
|
+
const rel = path.relative(rootReal, cwd);
|
|
102
|
+
if (rel === "" || (rel && !rel.startsWith("..") && !path.isAbsolute(rel))) {
|
|
103
|
+
return rel ? path.join(root, rel) : root;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
function realpathOr(p) {
|
|
109
|
+
try {
|
|
110
|
+
return fs.realpathSync(p);
|
|
111
|
+
}
|
|
112
|
+
catch (_a) {
|
|
113
|
+
return path.resolve(p);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function alignCwdWithRegistry(home) {
|
|
117
|
+
let roots;
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(home, "projects.json"), "utf-8"));
|
|
120
|
+
roots = parsed
|
|
121
|
+
.map((p) => p.root)
|
|
122
|
+
.filter((r) => typeof r === "string");
|
|
123
|
+
}
|
|
124
|
+
catch (_a) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const physical = process.cwd();
|
|
128
|
+
const logical = registeredForm(physical, roots);
|
|
129
|
+
if (logical && logical !== physical)
|
|
130
|
+
process.cwd = () => logical;
|
|
131
|
+
}
|
|
132
|
+
// A new store embeds with the same model as the primary one.
|
|
133
|
+
function seedConfig(home) {
|
|
134
|
+
var _a;
|
|
135
|
+
const target = path.join(home, "config.json");
|
|
136
|
+
if (fs.existsSync(target))
|
|
137
|
+
return;
|
|
138
|
+
const primary = path.join((_a = process.env.HOME) !== null && _a !== void 0 ? _a : "", ".gmax", "config.json");
|
|
139
|
+
try {
|
|
140
|
+
fs.mkdirSync(home, { recursive: true });
|
|
141
|
+
fs.copyFileSync(primary, target);
|
|
142
|
+
}
|
|
143
|
+
catch (_b) {
|
|
144
|
+
// no primary config: the store starts from the defaults, as ~/.gmax did
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (require.main === module) {
|
|
148
|
+
selectHome();
|
|
149
|
+
require("./index");
|
|
150
|
+
}
|
package/dist/commands/watch.js
CHANGED
|
@@ -72,6 +72,15 @@ exports.watch = new commander_1.Command("watch")
|
|
|
72
72
|
.option("--no-idle-timeout", "Disable the 30-minute idle shutdown")
|
|
73
73
|
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
74
|
var _a, _b;
|
|
75
|
+
// A secondary store (src/bin.ts) never runs a daemon or a watcher: one
|
|
76
|
+
// would hold the external drive open, and its startup sweep of stale
|
|
77
|
+
// daemons judges liveness by its own home's lock and socket, so it takes
|
|
78
|
+
// the primary daemon for dead and shuts it down (seen 2026-09-22).
|
|
79
|
+
if (process.env.GMAX_SECONDARY_STORE === "1") {
|
|
80
|
+
console.error("gmax: this project's index lives on an external drive and runs in-process; it is never watched. Run `gmax index` after changing it.");
|
|
81
|
+
process.exitCode = 2;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
75
84
|
// --- Daemon mode ---
|
|
76
85
|
if (options.daemon) {
|
|
77
86
|
if (options.path) {
|
package/dist/config.js
CHANGED
|
@@ -218,14 +218,23 @@ exports.MAX_WORKER_MEMORY_MB = (() => {
|
|
|
218
218
|
return Math.min(HARD_CEILING, Math.max(2048, Math.floor((os.totalmem() / 1024 / 1024) * 0.5)));
|
|
219
219
|
})();
|
|
220
220
|
const HOME = os.homedir();
|
|
221
|
-
|
|
221
|
+
// Models, grammars and the pinned HF cache are shared by every store and always
|
|
222
|
+
// live on the internal disk. GMAX_HOME moves only the data root: a secondary
|
|
223
|
+
// store on an external drive (see src/lib/utils/stores.ts) sets it before this
|
|
224
|
+
// module loads, so a project's index can live on the drive with its files.
|
|
225
|
+
const SHARED_ROOT = path.join(HOME, ".gmax");
|
|
226
|
+
const GLOBAL_ROOT = process.env.GMAX_HOME
|
|
227
|
+
? path.resolve(process.env.GMAX_HOME)
|
|
228
|
+
: SHARED_ROOT;
|
|
222
229
|
exports.PATHS = {
|
|
223
230
|
globalRoot: GLOBAL_ROOT,
|
|
224
|
-
|
|
225
|
-
|
|
231
|
+
sharedRoot: SHARED_ROOT,
|
|
232
|
+
storesFile: path.join(SHARED_ROOT, "stores.json"),
|
|
233
|
+
models: path.join(SHARED_ROOT, "models"),
|
|
234
|
+
grammars: path.join(SHARED_ROOT, "grammars"),
|
|
226
235
|
// HF cache for the MLX embed server, pinned to internal disk so the server
|
|
227
236
|
// survives an unmounted external volume behind the user's HF_HOME.
|
|
228
|
-
hfDir: path.join(
|
|
237
|
+
hfDir: path.join(SHARED_ROOT, "hf"),
|
|
229
238
|
logsDir: path.join(GLOBAL_ROOT, "logs"),
|
|
230
239
|
daemonSocket: path.join(GLOBAL_ROOT, "daemon.sock"),
|
|
231
240
|
daemonPidFile: path.join(GLOBAL_ROOT, "daemon.pid"),
|
|
@@ -81,6 +81,12 @@ function autostartDisabledUndo() {
|
|
|
81
81
|
: `rm ${config_1.PATHS.autostartDisabledFile}`;
|
|
82
82
|
}
|
|
83
83
|
function autostartDisabledNotice() {
|
|
84
|
+
// A secondary store (src/bin.ts) runs in-process by design, not because the
|
|
85
|
+
// kill switch is on, so say that instead of naming an undo step. Callers use
|
|
86
|
+
// a non-null notice as the gate, so this must stay non-null.
|
|
87
|
+
if (process.env.GMAX_SECONDARY_STORE === "1") {
|
|
88
|
+
return `This project's index lives in ${config_1.PATHS.globalRoot} on an external drive — running in-process, not watched. Run \`gmax index\` after changing it.`;
|
|
89
|
+
}
|
|
84
90
|
const undo = autostartDisabledUndo();
|
|
85
91
|
if (!undo)
|
|
86
92
|
return null;
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.storesFilePath = storesFilePath;
|
|
37
|
+
exports.readStores = readStores;
|
|
38
|
+
exports.volumeOf = volumeOf;
|
|
39
|
+
exports.matchStore = matchStore;
|
|
40
|
+
const fs = __importStar(require("node:fs"));
|
|
41
|
+
const os = __importStar(require("node:os"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
function storesFilePath(home = os.homedir()) {
|
|
44
|
+
return path.join(home, ".gmax", "stores.json");
|
|
45
|
+
}
|
|
46
|
+
function readStores(file = storesFilePath()) {
|
|
47
|
+
let raw;
|
|
48
|
+
try {
|
|
49
|
+
raw = fs.readFileSync(file, "utf-8");
|
|
50
|
+
}
|
|
51
|
+
catch (_a) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
if (!Array.isArray(parsed.stores))
|
|
57
|
+
return [];
|
|
58
|
+
return parsed.stores.filter((s) => !!s &&
|
|
59
|
+
typeof s.prefix === "string" &&
|
|
60
|
+
typeof s.home === "string");
|
|
61
|
+
}
|
|
62
|
+
catch (_b) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// The volume a path lives on, for paths under /Volumes; null for the boot disk.
|
|
67
|
+
function volumeOf(p) {
|
|
68
|
+
const parts = path.resolve(p).split(path.sep);
|
|
69
|
+
return parts[1] === "Volumes" && parts[2]
|
|
70
|
+
? path.join("/Volumes", parts[2])
|
|
71
|
+
: null;
|
|
72
|
+
}
|
|
73
|
+
function realOrResolved(p) {
|
|
74
|
+
try {
|
|
75
|
+
return fs.realpathSync(p);
|
|
76
|
+
}
|
|
77
|
+
catch (_a) {
|
|
78
|
+
return path.resolve(p);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function within(child, parent) {
|
|
82
|
+
const rel = path.relative(parent, child);
|
|
83
|
+
return (rel === "" || (!!rel && !rel.startsWith("..") && !path.isAbsolute(rel)));
|
|
84
|
+
}
|
|
85
|
+
// A path behind a symlink (~/Development/packages -> /Volumes/External/...)
|
|
86
|
+
// matches through its realpath. When the drive is ejected the symlink dangles
|
|
87
|
+
// and realpath fails, so the link target is read directly to still name it.
|
|
88
|
+
function candidates(target) {
|
|
89
|
+
const out = new Set([path.resolve(target), realOrResolved(target)]);
|
|
90
|
+
let cur = path.resolve(target);
|
|
91
|
+
const tail = [];
|
|
92
|
+
while (cur !== path.dirname(cur)) {
|
|
93
|
+
try {
|
|
94
|
+
if (fs.lstatSync(cur).isSymbolicLink()) {
|
|
95
|
+
const link = path.resolve(path.dirname(cur), fs.readlinkSync(cur));
|
|
96
|
+
out.add(path.join(link, ...tail.slice().reverse()));
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (_a) {
|
|
101
|
+
// not there; keep walking up
|
|
102
|
+
}
|
|
103
|
+
tail.push(path.basename(cur));
|
|
104
|
+
cur = path.dirname(cur);
|
|
105
|
+
}
|
|
106
|
+
return [...out];
|
|
107
|
+
}
|
|
108
|
+
function matchStore(target, stores = readStores()) {
|
|
109
|
+
var _a;
|
|
110
|
+
if (!stores.length)
|
|
111
|
+
return { kind: "primary" };
|
|
112
|
+
for (const cand of candidates(target)) {
|
|
113
|
+
for (const store of stores) {
|
|
114
|
+
if (!within(cand, path.resolve(store.prefix)))
|
|
115
|
+
continue;
|
|
116
|
+
const volume = (_a = volumeOf(store.home)) !== null && _a !== void 0 ? _a : volumeOf(store.prefix);
|
|
117
|
+
if (volume && !fs.existsSync(volume)) {
|
|
118
|
+
return { kind: "offline", store, volume };
|
|
119
|
+
}
|
|
120
|
+
return { kind: "store", store };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { kind: "primary" };
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.35",
|
|
4
4
|
"author": "Robert Owens <78518764+reowens@users.noreply.github.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"main": "dist/index.js",
|
|
14
14
|
"bin": {
|
|
15
|
-
"gmax": "dist/
|
|
15
|
+
"gmax": "dist/bin.js"
|
|
16
16
|
},
|
|
17
17
|
"engines": {
|
|
18
18
|
"node": ">=22.12.0"
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"postinstall": "node scripts/postinstall.js",
|
|
22
22
|
"prebuild": "rm -rf dist tsconfig.tsbuildinfo && mkdir -p dist",
|
|
23
23
|
"build": "tsc",
|
|
24
|
-
"postbuild": "chmod +x dist/index.js",
|
|
25
|
-
"dev": "npx tsc && node dist/
|
|
24
|
+
"postbuild": "chmod +x dist/bin.js dist/index.js",
|
|
25
|
+
"dev": "npx tsc && node dist/bin.js",
|
|
26
26
|
"test": "vitest run",
|
|
27
27
|
"test:watch": "vitest",
|
|
28
28
|
"bench:recall": "npx tsx src/eval.ts",
|