grepmax 0.10.3 → 0.10.4

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.
@@ -50,81 +50,257 @@ const commander_1 = require("commander");
50
50
  const config_1 = require("../config");
51
51
  const index_config_1 = require("../lib/index/index-config");
52
52
  const exit_1 = require("../lib/utils/exit");
53
+ const lock_1 = require("../lib/utils/lock");
54
+ const project_registry_1 = require("../lib/utils/project-registry");
53
55
  const project_root_1 = require("../lib/utils/project-root");
56
+ function formatSize(bytes) {
57
+ if (bytes < 1024)
58
+ return `${bytes} B`;
59
+ if (bytes < 1024 * 1024)
60
+ return `${(bytes / 1024).toFixed(1)} KB`;
61
+ if (bytes < 1024 * 1024 * 1024)
62
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
63
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
64
+ }
65
+ function getDirectorySize(dirPath) {
66
+ let totalSize = 0;
67
+ try {
68
+ const items = fs.readdirSync(dirPath);
69
+ for (const item of items) {
70
+ const itemPath = path.join(dirPath, item);
71
+ const stats = fs.statSync(itemPath);
72
+ if (stats.isDirectory()) {
73
+ totalSize += getDirectorySize(itemPath);
74
+ }
75
+ else {
76
+ totalSize += stats.size;
77
+ }
78
+ }
79
+ }
80
+ catch (_a) { }
81
+ return totalSize;
82
+ }
54
83
  exports.doctor = new commander_1.Command("doctor")
55
84
  .description("Check installation health, models, and index status")
56
- .action(() => __awaiter(void 0, void 0, void 0, function* () {
85
+ .option("--fix", "Auto-fix detected issues (compact, prune, remove stale locks)", false)
86
+ .option("--agent", "Compact output for AI agents", false)
87
+ .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
57
88
  var _a;
58
- console.log("đŸĨ gmax Doctor\n");
89
+ if (!opts.agent)
90
+ console.log("gmax Doctor\n");
59
91
  const root = config_1.PATHS.globalRoot;
60
92
  const models = config_1.PATHS.models;
61
93
  const grammars = config_1.PATHS.grammars;
62
- const checkDir = (name, p) => {
63
- const exists = fs.existsSync(p);
64
- const symbol = exists ? "✅" : "❌";
65
- console.log(`${symbol} ${name}: ${p}`);
66
- };
67
- checkDir("Root", root);
68
- checkDir("Models", models);
69
- checkDir("Grammars", grammars);
94
+ if (!opts.agent) {
95
+ const checkDir = (name, p) => {
96
+ const exists = fs.existsSync(p);
97
+ const symbol = exists ? "ok" : "MISSING";
98
+ console.log(`${symbol} ${name}: ${p}`);
99
+ };
100
+ checkDir("Root", root);
101
+ checkDir("Models", models);
102
+ checkDir("Grammars", grammars);
103
+ }
70
104
  const globalConfig = (0, index_config_1.readGlobalConfig)();
71
105
  const tier = (_a = config_1.MODEL_TIERS[globalConfig.modelTier]) !== null && _a !== void 0 ? _a : config_1.MODEL_TIERS.small;
72
106
  const embedModel = globalConfig.embedMode === "gpu" ? tier.mlxModel : tier.onnxModel;
73
- console.log(`\nEmbed mode: ${globalConfig.embedMode} | Model tier: ${globalConfig.modelTier} (${tier.vectorDim}d)`);
74
- console.log(`Embed model: ${embedModel}`);
75
- console.log(`ColBERT model: ${config_1.MODEL_IDS.colbert}`);
76
- const modelStatuses = [embedModel, config_1.MODEL_IDS.colbert].map((id) => {
77
- const modelPath = path.join(models, ...id.split("/"));
78
- return { id, path: modelPath, exists: fs.existsSync(modelPath) };
79
- });
80
- modelStatuses.forEach(({ id, exists }) => {
81
- const symbol = exists ? "✅" : "âš ī¸ ";
82
- console.log(`${symbol} ${id}: ${exists ? "downloaded" : "will download on first use"}`);
83
- });
84
- console.log(`\nLocal Project: ${process.cwd()}`);
85
- const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
86
- if (projectRoot) {
87
- console.log(`✅ Project root: ${projectRoot}`);
88
- console.log(` Centralized index at: ~/.gmax/lancedb/`);
89
- }
90
- else {
91
- console.log(`â„šī¸ No index found in current directory (run 'gmax index' to create one)`);
107
+ if (!opts.agent) {
108
+ console.log(`\nEmbed mode: ${globalConfig.embedMode} | Model tier: ${globalConfig.modelTier} (${tier.vectorDim}d)`);
109
+ console.log(`Embed model: ${embedModel}`);
110
+ console.log(`ColBERT model: ${config_1.MODEL_IDS.colbert}`);
111
+ const modelStatuses = [embedModel, config_1.MODEL_IDS.colbert].map((id) => {
112
+ const modelPath = path.join(models, ...id.split("/"));
113
+ return { id, path: modelPath, exists: fs.existsSync(modelPath) };
114
+ });
115
+ modelStatuses.forEach(({ id, exists }) => {
116
+ console.log(`${exists ? "ok" : "WARN"} ${id}: ${exists ? "downloaded" : "will download on first use"}`);
117
+ });
118
+ console.log(`\nLocal Project: ${process.cwd()}`);
119
+ const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
120
+ if (projectRoot) {
121
+ console.log(`ok Project root: ${projectRoot}`);
122
+ console.log(` Centralized index at: ~/.gmax/lancedb/`);
123
+ }
124
+ else {
125
+ console.log(`INFO No index found in current directory (run 'gmax index' to create one)`);
126
+ }
127
+ // Check MLX embed server
128
+ const embedUp = yield fetch("http://127.0.0.1:8100/health")
129
+ .then((r) => r.ok)
130
+ .catch(() => false);
131
+ console.log(`${embedUp ? "ok" : "WARN"} MLX Embed: ${embedUp ? "running (port 8100)" : "not running"}`);
132
+ // Check summarizer server
133
+ const summarizerUp = yield fetch("http://127.0.0.1:8101/health")
134
+ .then((r) => r.ok)
135
+ .catch(() => false);
136
+ console.log(`${summarizerUp ? "ok" : "WARN"} Summarizer: ${summarizerUp ? "running (port 8101)" : "not running"}`);
92
137
  }
93
- // Check MLX embed server
94
- const embedUp = yield fetch("http://127.0.0.1:8100/health")
95
- .then((r) => r.ok)
96
- .catch(() => false);
97
- console.log(`${embedUp ? "✅" : "âš ī¸ "} MLX Embed: ${embedUp ? "running (port 8100)" : "not running"}`);
98
- // Check summarizer server
99
- const summarizerUp = yield fetch("http://127.0.0.1:8101/health")
100
- .then((r) => r.ok)
101
- .catch(() => false);
102
- console.log(`${summarizerUp ? "✅" : "âš ī¸ "} Summarizer: ${summarizerUp ? "running (port 8101)" : "not running"}`);
103
- // Check summary coverage
138
+ // --- Index Health ---
139
+ let needsOptimize = false;
140
+ let staleLock = false;
141
+ const orphanedProjects = [];
104
142
  try {
105
143
  const { VectorDB } = yield Promise.resolve().then(() => __importStar(require("../lib/store/vector-db")));
106
144
  const db = new VectorDB(config_1.PATHS.lancedbDir);
107
145
  const table = yield db.ensureTable();
108
146
  const totalChunks = yield table.countRows();
109
- if (totalChunks > 0) {
147
+ // Summary coverage (existing check)
148
+ if (!opts.agent && totalChunks > 0) {
110
149
  const withSummary = (yield table
111
150
  .query()
112
151
  .where("length(summary) > 5")
113
152
  .select(["id"])
114
153
  .toArray()).length;
115
154
  const pct = Math.round((withSummary / totalChunks) * 100);
116
- const symbol = pct >= 90 ? "✅" : pct > 0 ? "âš ī¸ " : "❌";
117
- console.log(`${symbol} Summary coverage: ${withSummary}/${totalChunks} (${pct}%)`);
155
+ const symbol = pct >= 90 ? "ok" : pct > 0 ? "WARN" : "FAIL";
156
+ console.log(`${symbol} Summary coverage: ${withSummary}/${totalChunks} (${pct}%)`);
157
+ }
158
+ else if (!opts.agent && totalChunks === 0) {
159
+ console.log("INFO No indexed chunks yet");
160
+ }
161
+ // Index health checks
162
+ const tableStats = yield table.stats();
163
+ const diskSize = getDirectorySize(config_1.PATHS.lancedbDir);
164
+ const logicalSize = tableStats.totalBytes;
165
+ const { numFragments, numSmallFragments } = tableStats.fragmentStats;
166
+ const versions = yield table.listVersions();
167
+ // Lock status
168
+ const lockPath = path.join(config_1.PATHS.globalRoot, "LOCK");
169
+ let lockStatus = "none";
170
+ if (fs.existsSync(lockPath)) {
171
+ const { pid, startedAt } = (0, lock_1.parseLock)(lockPath);
172
+ const alive = (0, lock_1.isProcessAlive)(pid);
173
+ if (alive) {
174
+ lockStatus = `active (PID ${pid})`;
175
+ }
176
+ else {
177
+ lockStatus = `stale (PID ${pid}${startedAt ? ` @ ${startedAt}` : ""})`;
178
+ staleLock = true;
179
+ }
180
+ }
181
+ // Daemon status
182
+ const { isDaemonRunning } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
183
+ const daemonUp = yield isDaemonRunning();
184
+ // Project registry health
185
+ const projects = (0, project_registry_1.listProjects)();
186
+ for (const p of projects) {
187
+ if (!fs.existsSync(p.root)) {
188
+ orphanedProjects.push(p.root);
189
+ }
190
+ }
191
+ // Compute warning flags
192
+ const bloatRatio = logicalSize > 0 ? diskSize / logicalSize : 0;
193
+ if (bloatRatio > 2.0)
194
+ needsOptimize = true;
195
+ if (numSmallFragments > 10)
196
+ needsOptimize = true;
197
+ if (versions.length > 50)
198
+ needsOptimize = true;
199
+ if (opts.agent) {
200
+ const fields = [
201
+ "index_health",
202
+ `rows=${totalChunks}`,
203
+ `logical=${formatSize(logicalSize)}`,
204
+ `disk=${formatSize(diskSize)}`,
205
+ `fragments=${numFragments}`,
206
+ `small=${numSmallFragments}`,
207
+ `versions=${versions.length}`,
208
+ `lock=${lockStatus.split(" ")[0]}`,
209
+ `daemon=${daemonUp ? "running" : "stopped"}`,
210
+ `orphaned=${orphanedProjects.length}`,
211
+ ];
212
+ console.log(fields.join("\t"));
118
213
  }
119
214
  else {
120
- console.log("â„šī¸ No indexed chunks yet");
215
+ console.log("\nIndex Health\n");
216
+ // Storage
217
+ if (bloatRatio > 2.0) {
218
+ console.log(`WARN Storage: ${totalChunks.toLocaleString()} rows, ${formatSize(logicalSize)} logical, ${formatSize(diskSize)} disk (${bloatRatio.toFixed(1)}x — orphaned files)`);
219
+ }
220
+ else {
221
+ console.log(`ok Storage: ${totalChunks.toLocaleString()} rows, ${formatSize(logicalSize)} logical, ${formatSize(diskSize)} disk`);
222
+ }
223
+ // Fragments
224
+ if (numSmallFragments > 10) {
225
+ console.log(`WARN Fragments: ${numFragments} total, ${numSmallFragments} small — needs compaction`);
226
+ }
227
+ else {
228
+ console.log(`ok Fragments: ${numFragments} total, ${numSmallFragments} small`);
229
+ }
230
+ // Versions
231
+ if (versions.length > 50) {
232
+ console.log(`WARN Versions: ${versions.length} — pruning recommended`);
233
+ }
234
+ else {
235
+ console.log(`ok Versions: ${versions.length}`);
236
+ }
237
+ // Lock
238
+ if (staleLock) {
239
+ console.log(`WARN Lock: ${lockStatus}`);
240
+ }
241
+ else if (lockStatus === "none") {
242
+ console.log("ok Lock: none");
243
+ }
244
+ else {
245
+ console.log(`ok Lock: ${lockStatus}`);
246
+ }
247
+ // Daemon
248
+ console.log(`${daemonUp ? "ok" : "INFO"} Daemon: ${daemonUp ? "running" : "not running"}`);
249
+ // Projects
250
+ if (orphanedProjects.length > 0) {
251
+ console.log(`WARN Orphaned projects: ${orphanedProjects.length} (directories no longer exist)`);
252
+ for (const op of orphanedProjects) {
253
+ console.log(` - ${op}`);
254
+ }
255
+ }
256
+ else if (projects.length > 0) {
257
+ console.log(`ok Projects: ${projects.length} registered, all directories exist`);
258
+ }
259
+ }
260
+ // --fix auto-remediation
261
+ if (opts.fix) {
262
+ if (!opts.agent)
263
+ console.log("\nAuto-fix\n");
264
+ let fixed = 0;
265
+ if (staleLock) {
266
+ yield (0, lock_1.removeLock)(lockPath);
267
+ if (!opts.agent)
268
+ console.log("ok Removed stale lock");
269
+ fixed++;
270
+ }
271
+ if (needsOptimize) {
272
+ if (!opts.agent)
273
+ console.log("... Running optimize (compact + prune)...");
274
+ yield db.optimize(3, 0);
275
+ if (!opts.agent)
276
+ console.log("ok Optimize complete");
277
+ fixed++;
278
+ }
279
+ if (orphanedProjects.length > 0) {
280
+ for (const op of orphanedProjects) {
281
+ (0, project_registry_1.removeProject)(op);
282
+ }
283
+ if (!opts.agent)
284
+ console.log(`ok Removed ${orphanedProjects.length} orphaned project(s) from registry`);
285
+ fixed++;
286
+ }
287
+ if (fixed === 0) {
288
+ if (!opts.agent)
289
+ console.log("ok Nothing to fix");
290
+ }
121
291
  }
122
292
  yield db.close();
123
293
  }
124
294
  catch (_b) {
125
- console.log("âš ī¸ Could not check summary coverage");
295
+ if (opts.agent) {
296
+ console.log("index_health\terror=could_not_check");
297
+ }
298
+ else {
299
+ console.log("\nWARN Could not check index health");
300
+ }
301
+ }
302
+ if (!opts.agent) {
303
+ console.log(`\nSystem: ${os.platform()} ${os.arch()} | Node: ${process.version}`);
126
304
  }
127
- console.log(`\nSystem: ${os.platform()} ${os.arch()} | Node: ${process.version}`);
128
- console.log("\nIf you see ✅ everywhere, you are ready to search!");
129
305
  yield (0, exit_1.gracefulExit)();
130
306
  }));
@@ -276,9 +276,9 @@ class VectorDB {
276
276
  });
277
277
  }
278
278
  optimize() {
279
- return __awaiter(this, arguments, void 0, function* (retries = 3) {
279
+ return __awaiter(this, arguments, void 0, function* (retries = 3, retentionMs = 0) {
280
280
  const table = yield this.ensureTable();
281
- const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1000);
281
+ const cutoff = new Date(Date.now() - retentionMs);
282
282
  for (let attempt = 1; attempt <= retries; attempt++) {
283
283
  try {
284
284
  const done = (0, logger_1.timer)("vectordb", "optimize");
@@ -42,6 +42,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  });
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.parseLock = parseLock;
46
+ exports.isProcessAlive = isProcessAlive;
47
+ exports.removeLock = removeLock;
45
48
  exports.acquireWriterLock = acquireWriterLock;
46
49
  exports.acquireWriterLockWithRetry = acquireWriterLockWithRetry;
47
50
  exports.isLocked = isLocked;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
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",