grepmax 0.12.0 → 0.12.1

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(() => {
@@ -253,12 +278,16 @@ class Daemon {
253
278
  catch (_d) { }
254
279
  }
255
280
  this.subscriptions.clear();
256
- // Close server + socket
281
+ // Close server + socket + PID file
257
282
  (_a = this.server) === null || _a === void 0 ? void 0 : _a.close();
258
283
  try {
259
284
  fs.unlinkSync(config_1.PATHS.daemonSocket);
260
285
  }
261
286
  catch (_e) { }
287
+ try {
288
+ fs.unlinkSync(config_1.PATHS.daemonPidFile);
289
+ }
290
+ catch (_f) { }
262
291
  // Unregister all
263
292
  for (const root of this.processors.keys()) {
264
293
  (0, watcher_store_1.unregisterWatcherByRoot)(root);
@@ -269,11 +298,11 @@ class Daemon {
269
298
  try {
270
299
  yield ((_b = this.metaCache) === null || _b === void 0 ? void 0 : _b.close());
271
300
  }
272
- catch (_f) { }
301
+ catch (_g) { }
273
302
  try {
274
303
  yield ((_c = this.vectorDb) === null || _c === void 0 ? void 0 : _c.close());
275
304
  }
276
- catch (_g) { }
305
+ catch (_h) { }
277
306
  console.log("[daemon] Shutdown complete");
278
307
  });
279
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
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.1",
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",