claude-flow 3.5.42 → 3.5.43

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.
@@ -10,7 +10,7 @@
10
10
  * - testgaps: Test coverage analysis (20 min interval)
11
11
  */
12
12
  import { EventEmitter } from 'events';
13
- import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'fs';
13
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync, unlinkSync } from 'fs';
14
14
  import { cpus } from 'os';
15
15
  import { join } from 'path';
16
16
  import { HeadlessWorkerExecutor, isHeadlessWorker, } from './headless-worker-executor.js';
@@ -173,8 +173,15 @@ export class WorkerDaemon extends EventEmitter {
173
173
  */
174
174
  readDaemonConfigFromFile(claudeFlowDir) {
175
175
  const configPath = join(claudeFlowDir, 'config.json');
176
- if (!existsSync(configPath))
176
+ if (!existsSync(configPath)) {
177
+ // Warn if config.yaml exists but config.json does not (#1395 Bug 4)
178
+ const yamlPath = join(claudeFlowDir, 'config.yaml');
179
+ const ymlPath = join(claudeFlowDir, 'config.yml');
180
+ if (existsSync(yamlPath) || existsSync(ymlPath)) {
181
+ this.log('warn', `Found ${existsSync(yamlPath) ? 'config.yaml' : 'config.yml'} but daemon reads only config.json — YAML config is being ignored. Convert to JSON or create config.json.`);
182
+ }
177
183
  return {};
184
+ }
178
185
  try {
179
186
  const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
180
187
  // Support both flat keys at root and nested under scopes.project
@@ -320,6 +327,54 @@ export class WorkerDaemon extends EventEmitter {
320
327
  }
321
328
  }
322
329
  }
330
+ /**
331
+ * Get the PID file path for singleton enforcement (#1395 Bug 3).
332
+ */
333
+ get pidFile() {
334
+ return join(this.projectRoot, '.claude-flow', 'daemon.pid');
335
+ }
336
+ /**
337
+ * Check if another daemon instance is already running.
338
+ * Returns the existing PID if alive, or null if no daemon is running.
339
+ */
340
+ checkExistingDaemon() {
341
+ if (!existsSync(this.pidFile))
342
+ return null;
343
+ try {
344
+ const pid = parseInt(readFileSync(this.pidFile, 'utf-8').trim(), 10);
345
+ if (isNaN(pid))
346
+ return null;
347
+ // Check if process is alive (signal 0 = existence check)
348
+ process.kill(pid, 0);
349
+ return pid; // Process is alive
350
+ }
351
+ catch {
352
+ // Process is dead — clean up stale PID file
353
+ try {
354
+ unlinkSync(this.pidFile);
355
+ }
356
+ catch { /* ignore */ }
357
+ return null;
358
+ }
359
+ }
360
+ /**
361
+ * Write PID file for singleton enforcement.
362
+ */
363
+ writePidFile() {
364
+ const dir = join(this.projectRoot, '.claude-flow');
365
+ if (!existsSync(dir))
366
+ mkdirSync(dir, { recursive: true });
367
+ writeFileSync(this.pidFile, String(process.pid), 'utf-8');
368
+ }
369
+ /**
370
+ * Remove PID file on shutdown.
371
+ */
372
+ removePidFile() {
373
+ try {
374
+ unlinkSync(this.pidFile);
375
+ }
376
+ catch { /* ignore */ }
377
+ }
323
378
  /**
324
379
  * Start the daemon and all enabled workers
325
380
  */
@@ -328,8 +383,16 @@ export class WorkerDaemon extends EventEmitter {
328
383
  this.emit('warning', 'Daemon already running');
329
384
  return;
330
385
  }
386
+ // PID singleton enforcement (#1395 Bug 3): prevent daemon accumulation
387
+ const existingPid = this.checkExistingDaemon();
388
+ if (existingPid !== null) {
389
+ this.log('info', `Daemon already running (PID: ${existingPid}), skipping start`);
390
+ this.emit('warning', `Daemon already running (PID: ${existingPid})`);
391
+ return;
392
+ }
331
393
  this.running = true;
332
394
  this.startedAt = new Date();
395
+ this.writePidFile();
333
396
  this.emit('started', { pid: process.pid, startedAt: this.startedAt });
334
397
  // Schedule all enabled workers
335
398
  for (const workerConfig of this.config.workers) {
@@ -357,6 +420,7 @@ export class WorkerDaemon extends EventEmitter {
357
420
  }
358
421
  this.timers.clear();
359
422
  this.running = false;
423
+ this.removePidFile();
360
424
  this.saveState();
361
425
  this.emit('stopped', { stoppedAt: new Date() });
362
426
  this.log('info', 'Daemon stopped');
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.5.42",
3
+ "version": "3.5.43",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",