grepmax 0.12.0 → 0.12.2

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.
@@ -44,6 +44,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.watch = void 0;
46
46
  const node_child_process_1 = require("node:child_process");
47
+ const fs = __importStar(require("node:fs"));
47
48
  const path = __importStar(require("node:path"));
48
49
  const commander_1 = require("commander");
49
50
  const config_1 = require("../config");
@@ -79,6 +80,16 @@ exports.watch = new commander_1.Command("watch")
79
80
  if (options.background) {
80
81
  // Skip spawn if daemon already running — prevents process accumulation
81
82
  // when SessionStart hook fires on every session/clear/resume
83
+ const pidFile = config_1.PATHS.daemonPidFile;
84
+ try {
85
+ const existingPid = Number.parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
86
+ if (existingPid) {
87
+ process.kill(existingPid, 0); // throws if dead
88
+ process.exit(0); // alive — skip
89
+ }
90
+ }
91
+ catch (_c) { }
92
+ // Also check socket as fallback
82
93
  const { isDaemonRunning } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
83
94
  if (yield isDaemonRunning()) {
84
95
  process.exit(0);
package/dist/config.js CHANGED
@@ -95,6 +95,7 @@ exports.PATHS = {
95
95
  grammars: path.join(GLOBAL_ROOT, "grammars"),
96
96
  logsDir: path.join(GLOBAL_ROOT, "logs"),
97
97
  daemonSocket: path.join(GLOBAL_ROOT, "daemon.sock"),
98
+ daemonPidFile: path.join(GLOBAL_ROOT, "daemon.pid"),
98
99
  // Centralized index storage — one database for all indexed directories
99
100
  lancedbDir: path.join(GLOBAL_ROOT, "lancedb"),
100
101
  cacheDir: path.join(GLOBAL_ROOT, "cache"),
@@ -81,11 +81,27 @@ class Daemon {
81
81
  yield (0, process_1.killProcess)(w.pid);
82
82
  (0, watcher_store_1.unregisterWatcher)(w.pid);
83
83
  }
84
- // 2. Stale socket cleanup
84
+ // 2. PID file — atomic dedup guard
85
+ const pidFile = config_1.PATHS.daemonPidFile;
86
+ try {
87
+ // Check if another daemon is alive
88
+ const existingPid = Number.parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
89
+ if (existingPid && existingPid !== process.pid) {
90
+ try {
91
+ process.kill(existingPid, 0); // throws if dead
92
+ console.error("[daemon] Another daemon is already running (PID:", existingPid + ")");
93
+ process.exit(0);
94
+ }
95
+ catch (_a) { }
96
+ }
97
+ }
98
+ catch (_b) { }
99
+ fs.writeFileSync(pidFile, String(process.pid));
100
+ // 3. Stale socket cleanup
85
101
  try {
86
102
  fs.unlinkSync(config_1.PATHS.daemonSocket);
87
103
  }
88
- catch (_a) { }
104
+ catch (_c) { }
89
105
  // 3. Open shared resources
90
106
  try {
91
107
  fs.mkdirSync(config_1.PATHS.cacheDir, { recursive: true });
@@ -99,10 +115,19 @@ class Daemon {
99
115
  }
100
116
  // 4. Register daemon (only after resources are open)
101
117
  (0, watcher_store_1.registerDaemon)(process.pid);
102
- // 5. Subscribe to all registered projects
118
+ // 5. Subscribe to all registered projects (skip missing directories)
103
119
  const projects = (0, project_registry_1.listProjects)().filter((p) => p.status === "indexed");
104
120
  for (const p of projects) {
105
- yield this.watchProject(p.root);
121
+ if (!fs.existsSync(p.root)) {
122
+ console.log(`[daemon] Skipping ${path.basename(p.root)} — directory not found`);
123
+ continue;
124
+ }
125
+ try {
126
+ yield this.watchProject(p.root);
127
+ }
128
+ catch (err) {
129
+ console.error(`[daemon] Failed to watch ${path.basename(p.root)}:`, err);
130
+ }
106
131
  }
107
132
  // 6. Heartbeat
108
133
  this.heartbeatInterval = setInterval(() => {
@@ -176,6 +201,11 @@ class Daemon {
176
201
  dataDir: config_1.PATHS.globalRoot,
177
202
  onReindex: (files, ms) => {
178
203
  console.log(`[daemon:${path.basename(root)}] Reindexed ${files} file${files !== 1 ? "s" : ""} (${(ms / 1000).toFixed(1)}s)`);
204
+ // Update project registry so gmax status shows fresh data
205
+ const proj = (0, project_registry_1.getProject)(root);
206
+ if (proj) {
207
+ (0, project_registry_1.registerProject)(Object.assign(Object.assign({}, proj), { lastIndexed: new Date().toISOString() }));
208
+ }
179
209
  },
180
210
  onActivity: () => {
181
211
  this.lastActivity = Date.now();
@@ -253,12 +283,16 @@ class Daemon {
253
283
  catch (_d) { }
254
284
  }
255
285
  this.subscriptions.clear();
256
- // Close server + socket
286
+ // Close server + socket + PID file
257
287
  (_a = this.server) === null || _a === void 0 ? void 0 : _a.close();
258
288
  try {
259
289
  fs.unlinkSync(config_1.PATHS.daemonSocket);
260
290
  }
261
291
  catch (_e) { }
292
+ try {
293
+ fs.unlinkSync(config_1.PATHS.daemonPidFile);
294
+ }
295
+ catch (_f) { }
262
296
  // Unregister all
263
297
  for (const root of this.processors.keys()) {
264
298
  (0, watcher_store_1.unregisterWatcherByRoot)(root);
@@ -269,11 +303,11 @@ class Daemon {
269
303
  try {
270
304
  yield ((_b = this.metaCache) === null || _b === void 0 ? void 0 : _b.close());
271
305
  }
272
- catch (_f) { }
306
+ catch (_g) { }
273
307
  try {
274
308
  yield ((_c = this.vectorDb) === null || _c === void 0 ? void 0 : _c.close());
275
309
  }
276
- catch (_g) { }
310
+ catch (_h) { }
277
311
  console.log("[daemon] Shutdown complete");
278
312
  });
279
313
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
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.12.0",
3
+ "version": "0.12.2",
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",