claude-flow 3.5.76 → 3.5.78

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.5.76",
3
+ "version": "3.5.78",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -158,8 +158,15 @@ if (isMCPMode) {
158
158
  // Run normal CLI mode
159
159
  const { CLI } = await import('../dist/src/index.js');
160
160
  const cli = new CLI();
161
- cli.run().catch((error) => {
162
- console.error('Fatal error:', error.message);
163
- process.exit(1);
164
- });
161
+ cli.run()
162
+ .then(() => {
163
+ // #1552: Exit cleanly after one-shot commands.
164
+ // Long-running commands (daemon foreground, mcp, status --watch) never resolve,
165
+ // so this only fires for normal CLI commands.
166
+ process.exit(0);
167
+ })
168
+ .catch((error) => {
169
+ console.error('Fatal error:', error.message);
170
+ process.exit(1);
171
+ });
165
172
  }
@@ -7,6 +7,10 @@
7
7
  * ADR-072: Autopilot Integration
8
8
  * Security: Addresses prototype pollution, NaN bypass, input validation
9
9
  */
10
+ import { randomUUID } from 'node:crypto';
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, renameSync } from 'node:fs';
12
+ import { resolve, join } from 'node:path';
13
+ import { homedir } from 'node:os';
10
14
  // ── Constants ─────────────────────────────────────────────────
11
15
  export const STATE_DIR = '.claude-flow/data';
12
16
  export const STATE_FILE = `${STATE_DIR}/autopilot-state.json`;
@@ -71,9 +75,8 @@ export function validateTaskSources(sources) {
71
75
  }
72
76
  // ── State Management ──────────────────────────────────────────
73
77
  export function getDefaultState() {
74
- const crypto = require('crypto');
75
78
  return {
76
- sessionId: crypto.randomUUID(),
79
+ sessionId: randomUUID(),
77
80
  enabled: false,
78
81
  startTime: Date.now(),
79
82
  iterations: 0,
@@ -85,13 +88,11 @@ export function getDefaultState() {
85
88
  };
86
89
  }
87
90
  export function loadState() {
88
- const fs = require('fs');
89
- const path = require('path');
90
- const filePath = path.resolve(STATE_FILE);
91
+ const filePath = resolve(STATE_FILE);
91
92
  const defaults = getDefaultState();
92
93
  try {
93
- if (fs.existsSync(filePath)) {
94
- const raw = safeJsonParse(fs.readFileSync(filePath, 'utf-8'));
94
+ if (existsSync(filePath)) {
95
+ const raw = safeJsonParse(readFileSync(filePath, 'utf-8'));
95
96
  const merged = { ...defaults, ...raw };
96
97
  // Re-validate fields that could be tampered with
97
98
  merged.maxIterations = validateNumber(merged.maxIterations, 1, 1000, 50);
@@ -111,30 +112,26 @@ export function loadState() {
111
112
  return defaults;
112
113
  }
113
114
  export function saveState(state) {
114
- const fs = require('fs');
115
- const path = require('path');
116
- const dir = path.resolve(STATE_DIR);
117
- if (!fs.existsSync(dir))
118
- fs.mkdirSync(dir, { recursive: true });
115
+ const dir = resolve(STATE_DIR);
116
+ if (!existsSync(dir))
117
+ mkdirSync(dir, { recursive: true });
119
118
  // Cap history before saving
120
119
  if (state.history.length > MAX_HISTORY_ENTRIES) {
121
120
  state.history = state.history.slice(-MAX_HISTORY_ENTRIES);
122
121
  }
123
- const tmpFile = path.resolve(STATE_FILE) + '.tmp';
124
- fs.writeFileSync(tmpFile, JSON.stringify(state, null, 2));
125
- fs.renameSync(tmpFile, path.resolve(STATE_FILE));
122
+ const tmpFile = resolve(STATE_FILE) + '.tmp';
123
+ writeFileSync(tmpFile, JSON.stringify(state, null, 2));
124
+ renameSync(tmpFile, resolve(STATE_FILE));
126
125
  }
127
126
  export function appendLog(entry) {
128
- const fs = require('fs');
129
- const path = require('path');
130
- const filePath = path.resolve(LOG_FILE);
131
- const dir = path.resolve(STATE_DIR);
132
- if (!fs.existsSync(dir))
133
- fs.mkdirSync(dir, { recursive: true });
127
+ const filePath = resolve(LOG_FILE);
128
+ const dir = resolve(STATE_DIR);
129
+ if (!existsSync(dir))
130
+ mkdirSync(dir, { recursive: true });
134
131
  let log = [];
135
132
  try {
136
- if (fs.existsSync(filePath)) {
137
- log = safeJsonParse(fs.readFileSync(filePath, 'utf-8'));
133
+ if (existsSync(filePath)) {
134
+ log = safeJsonParse(readFileSync(filePath, 'utf-8'));
138
135
  if (!Array.isArray(log))
139
136
  log = [];
140
137
  }
@@ -146,16 +143,14 @@ export function appendLog(entry) {
146
143
  if (log.length > MAX_LOG_ENTRIES)
147
144
  log = log.slice(-MAX_LOG_ENTRIES);
148
145
  const tmpFile = filePath + '.tmp';
149
- fs.writeFileSync(tmpFile, JSON.stringify(log, null, 2));
150
- fs.renameSync(tmpFile, filePath);
146
+ writeFileSync(tmpFile, JSON.stringify(log, null, 2));
147
+ renameSync(tmpFile, filePath);
151
148
  }
152
149
  export function loadLog() {
153
- const fs = require('fs');
154
- const path = require('path');
155
- const filePath = path.resolve(LOG_FILE);
150
+ const filePath = resolve(LOG_FILE);
156
151
  try {
157
- if (fs.existsSync(filePath)) {
158
- const result = safeJsonParse(fs.readFileSync(filePath, 'utf-8'));
152
+ if (existsSync(filePath)) {
153
+ const result = safeJsonParse(readFileSync(filePath, 'utf-8'));
159
154
  return Array.isArray(result) ? result : [];
160
155
  }
161
156
  }
@@ -166,26 +161,23 @@ export function loadLog() {
166
161
  }
167
162
  // ── Task Discovery ────────────────────────────────────────────
168
163
  export function discoverTasks(sources) {
169
- const fs = require('fs');
170
- const path = require('path');
171
- const os = require('os');
172
164
  const tasks = [];
173
165
  // Only process valid sources
174
166
  const validSources = sources.filter(s => VALID_TASK_SOURCES.has(s));
175
167
  for (const source of validSources) {
176
168
  if (source === 'team-tasks') {
177
- const tasksDir = path.join(os.homedir(), '.claude', 'tasks');
169
+ const tasksDir = join(homedir(), '.claude', 'tasks');
178
170
  try {
179
- if (fs.existsSync(tasksDir)) {
180
- const teams = fs.readdirSync(tasksDir, { withFileTypes: true });
171
+ if (existsSync(tasksDir)) {
172
+ const teams = readdirSync(tasksDir, { withFileTypes: true });
181
173
  for (const team of teams) {
182
174
  if (!team.isDirectory())
183
175
  continue;
184
- const teamDir = path.join(tasksDir, team.name);
185
- const files = fs.readdirSync(teamDir).filter((f) => f.endsWith('.json'));
176
+ const teamDir = join(tasksDir, team.name);
177
+ const files = readdirSync(teamDir).filter((f) => f.endsWith('.json'));
186
178
  for (const file of files) {
187
179
  try {
188
- const data = safeJsonParse(fs.readFileSync(path.join(teamDir, file), 'utf-8'));
180
+ const data = safeJsonParse(readFileSync(join(teamDir, file), 'utf-8'));
189
181
  tasks.push({
190
182
  id: String(data.id || file.replace('.json', '')),
191
183
  subject: String(data.subject || data.title || file),
@@ -201,10 +193,10 @@ export function discoverTasks(sources) {
201
193
  catch { /* skip source */ }
202
194
  }
203
195
  if (source === 'swarm-tasks') {
204
- const swarmFile = path.resolve('.claude-flow/swarm-tasks.json');
196
+ const swarmFile = resolve('.claude-flow/swarm-tasks.json');
205
197
  try {
206
- if (fs.existsSync(swarmFile)) {
207
- const data = safeJsonParse(fs.readFileSync(swarmFile, 'utf-8'));
198
+ if (existsSync(swarmFile)) {
199
+ const data = safeJsonParse(readFileSync(swarmFile, 'utf-8'));
208
200
  const swarmTasks = Array.isArray(data) ? data : (data.tasks || []);
209
201
  for (const t of swarmTasks) {
210
202
  if (t && typeof t === 'object') {
@@ -222,10 +214,10 @@ export function discoverTasks(sources) {
222
214
  catch { /* skip source */ }
223
215
  }
224
216
  if (source === 'file-checklist') {
225
- const checklistFile = path.resolve('.claude-flow/data/checklist.json');
217
+ const checklistFile = resolve('.claude-flow/data/checklist.json');
226
218
  try {
227
- if (fs.existsSync(checklistFile)) {
228
- const data = safeJsonParse(fs.readFileSync(checklistFile, 'utf-8'));
219
+ if (existsSync(checklistFile)) {
220
+ const data = safeJsonParse(readFileSync(checklistFile, 'utf-8'));
229
221
  const items = Array.isArray(data) ? data : (data.items || []);
230
222
  for (const item of items) {
231
223
  if (item && typeof item === 'object') {
@@ -182,10 +182,10 @@ const logCommand = {
182
182
  ],
183
183
  action: async (ctx) => {
184
184
  if (ctx.flags?.clear) {
185
- const fs = require('fs');
186
- const path = require('path');
185
+ const { writeFileSync } = await import('node:fs');
186
+ const { resolve } = await import('node:path');
187
187
  try {
188
- fs.writeFileSync(path.resolve(LOG_FILE), '[]');
188
+ writeFileSync(resolve(LOG_FILE), '[]');
189
189
  }
190
190
  catch { /* ignore */ }
191
191
  output.writeln('Autopilot log cleared');
@@ -4,27 +4,25 @@
4
4
  *
5
5
  * Created with ❤️ by ruv.io
6
6
  */
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
8
+ import { resolve, dirname } from 'node:path';
7
9
  import { output } from '../output.js';
8
10
  const CLAIMS_CONFIG_PATHS = [
9
11
  '.claude-flow/claims.json',
10
12
  'claude-flow.claims.json',
11
13
  ];
12
14
  function getClaimsConfigPaths() {
13
- // Lazy import to keep top-level synchronous
14
- // eslint-disable-next-line @typescript-eslint/no-var-requires
15
- const path = require('path');
16
15
  return [
17
- path.resolve(CLAIMS_CONFIG_PATHS[0]),
18
- path.resolve(CLAIMS_CONFIG_PATHS[1]),
19
- path.resolve(process.env.HOME || '~', '.config/claude-flow/claims.json'),
16
+ resolve(CLAIMS_CONFIG_PATHS[0]),
17
+ resolve(CLAIMS_CONFIG_PATHS[1]),
18
+ resolve(process.env.HOME || '~', '.config/claude-flow/claims.json'),
20
19
  ];
21
20
  }
22
21
  function loadClaimsConfig() {
23
- const fs = require('fs');
24
22
  const configPaths = getClaimsConfigPaths();
25
23
  for (const configPath of configPaths) {
26
- if (fs.existsSync(configPath)) {
27
- const content = fs.readFileSync(configPath, 'utf-8');
24
+ if (existsSync(configPath)) {
25
+ const content = readFileSync(configPath, 'utf-8');
28
26
  return { config: JSON.parse(content), path: configPath };
29
27
  }
30
28
  }
@@ -41,15 +39,13 @@ function loadClaimsConfig() {
41
39
  return { config: defaultConfig, path: configPaths[0] };
42
40
  }
43
41
  function saveClaimsConfig(config, configPath) {
44
- const fs = require('fs');
45
- const path = require('path');
46
- const dir = path.dirname(configPath);
47
- if (!fs.existsSync(dir)) {
48
- fs.mkdirSync(dir, { recursive: true });
42
+ const dir = dirname(configPath);
43
+ if (!existsSync(dir)) {
44
+ mkdirSync(dir, { recursive: true });
49
45
  }
50
46
  const tmpPath = configPath + '.tmp';
51
- fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
52
- fs.renameSync(tmpPath, configPath);
47
+ writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
48
+ renameSync(tmpPath, configPath);
53
49
  }
54
50
  // List subcommand
55
51
  const listCommand = {
@@ -5,13 +5,21 @@
5
5
  * Created with ruv.io
6
6
  */
7
7
  import { output } from '../output.js';
8
- import { existsSync, statSync, rmSync, readdirSync } from 'fs';
8
+ import { existsSync, statSync, rmSync, readdirSync, readFileSync, writeFileSync } from 'fs';
9
9
  import { join } from 'path';
10
+ /**
11
+ * Ruflo-owned subdirectories within .claude/ that are safe to delete.
12
+ * Everything else in .claude/ (agents, skills, commands, settings.local.json,
13
+ * memory.db, worktrees, launch.json) belongs to Claude Code and must be preserved.
14
+ * See: https://github.com/ruvnet/ruflo/issues/1557
15
+ */
16
+ const CLAUDE_OWNED_SUBDIRS = [
17
+ { path: join('.claude', 'helpers'), description: 'Ruflo hook scripts' },
18
+ ];
10
19
  /**
11
20
  * Artifact directories and files that claude-flow/ruflo may create
12
21
  */
13
22
  const ARTIFACT_DIRS = [
14
- { path: '.claude', description: 'Claude settings, helpers, agents' },
15
23
  { path: '.claude-flow', description: 'Capabilities and configuration' },
16
24
  { path: 'data', description: 'Memory databases' },
17
25
  { path: '.swarm', description: 'Swarm state' },
@@ -119,7 +127,21 @@ export const cleanupCommand = {
119
127
  output.writeln();
120
128
  const found = [];
121
129
  let totalSize = 0;
122
- // Scan directories
130
+ // Scan ruflo-owned subdirs within .claude/ (surgical — preserves Claude Code files)
131
+ for (const artifact of CLAUDE_OWNED_SUBDIRS) {
132
+ const fullPath = join(cwd, artifact.path);
133
+ if (existsSync(fullPath)) {
134
+ const size = getSize(fullPath);
135
+ found.push({ path: artifact.path, description: artifact.description, size, type: 'dir' });
136
+ totalSize += size;
137
+ }
138
+ }
139
+ // Check if .claude/settings.json has ruflo hooks/claudeFlow blocks to clean
140
+ const settingsPath = join(cwd, '.claude', 'settings.json');
141
+ if (existsSync(settingsPath)) {
142
+ found.push({ path: join('.claude', 'settings.json'), description: 'Remove ruflo hooks/claudeFlow blocks (preserves rest)', size: 0, type: 'file' });
143
+ }
144
+ // Scan standalone artifact directories
123
145
  for (const artifact of ARTIFACT_DIRS) {
124
146
  const fullPath = join(cwd, artifact.path);
125
147
  if (existsSync(fullPath)) {
@@ -170,7 +192,17 @@ export const cleanupCommand = {
170
192
  // Actually delete
171
193
  try {
172
194
  const fullPath = join(cwd, item.path);
173
- if (item.type === 'dir') {
195
+ // Special handling: surgically clean settings.json instead of deleting
196
+ if (item.path === join('.claude', 'settings.json')) {
197
+ try {
198
+ const raw = JSON.parse(readFileSync(fullPath, 'utf-8'));
199
+ delete raw.hooks;
200
+ delete raw.claudeFlow;
201
+ writeFileSync(fullPath, JSON.stringify(raw, null, 2) + '\n', 'utf-8');
202
+ }
203
+ catch { /* settings.json parse failed, skip */ }
204
+ }
205
+ else if (item.type === 'dir') {
174
206
  rmSync(fullPath, { recursive: true, force: true });
175
207
  }
176
208
  else {
@@ -69,10 +69,12 @@ const startCommand = {
69
69
  const bgPid = getBackgroundDaemonPid(projectRoot);
70
70
  if (bgPid && isProcessRunning(bgPid)) {
71
71
  if (!quiet) {
72
- output.printWarning(`Daemon already running in background (PID: ${bgPid})`);
72
+ output.printWarning(`Daemon already running in background (PID: ${bgPid}). Stop it first with: daemon stop`);
73
73
  }
74
74
  return { success: true };
75
75
  }
76
+ // #1551: Kill any stale daemon processes that weren't tracked by PID file
77
+ await killStaleDaemons(projectRoot, quiet);
76
78
  }
77
79
  // Background mode (default): fork a detached process
78
80
  if (!foreground) {
@@ -302,11 +304,14 @@ const stopCommand = {
302
304
  await stopDaemon();
303
305
  // Also kill any background daemon by PID
304
306
  const killed = await killBackgroundDaemon(projectRoot);
307
+ // #1551: Also kill stale daemon processes not tracked by PID file
308
+ await killStaleDaemons(projectRoot, true);
305
309
  spinner.succeed(killed ? 'Worker daemon stopped' : 'Worker daemon was not running');
306
310
  }
307
311
  else {
308
312
  await stopDaemon();
309
313
  await killBackgroundDaemon(projectRoot);
314
+ await killStaleDaemons(projectRoot, true);
310
315
  }
311
316
  return { success: true };
312
317
  }
@@ -365,6 +370,46 @@ async function killBackgroundDaemon(projectRoot) {
365
370
  return false;
366
371
  }
367
372
  }
373
+ /**
374
+ * Kill stale daemon processes not tracked by the PID file (#1551).
375
+ * Uses `ps` to find all daemon processes for this project and kills them.
376
+ */
377
+ async function killStaleDaemons(projectRoot, quiet) {
378
+ try {
379
+ const { execFileSync } = await import('child_process');
380
+ const psOutput = execFileSync('ps', ['-eo', 'pid,command'], { encoding: 'utf-8', timeout: 5000 });
381
+ const lines = psOutput.split('\n');
382
+ const currentPid = process.pid;
383
+ const trackedPid = getBackgroundDaemonPid(projectRoot);
384
+ let killed = 0;
385
+ for (const line of lines) {
386
+ if (!line.includes('daemon start --foreground'))
387
+ continue;
388
+ if (!line.includes('claude-flow') && !line.includes('@claude-flow/cli'))
389
+ continue;
390
+ const pidStr = line.trim().split(/\s+/)[0];
391
+ const pid = parseInt(pidStr, 10);
392
+ if (isNaN(pid) || pid === currentPid || pid === trackedPid)
393
+ continue;
394
+ if (!isProcessRunning(pid))
395
+ continue;
396
+ try {
397
+ process.kill(pid, 'SIGTERM');
398
+ killed++;
399
+ if (!quiet) {
400
+ output.printWarning(`Killed stale daemon process (PID: ${pid})`);
401
+ }
402
+ }
403
+ catch { /* ignore — may have exited between check and kill */ }
404
+ }
405
+ if (killed > 0 && !quiet) {
406
+ output.printInfo(`Cleaned up ${killed} stale daemon process(es)`);
407
+ }
408
+ }
409
+ catch {
410
+ // ps not available or failed — skip stale cleanup
411
+ }
412
+ }
368
413
  /**
369
414
  * Get PID of background daemon from PID file
370
415
  */
@@ -5,6 +5,7 @@
5
5
  * Created with ❤️ by ruv.io
6
6
  */
7
7
  import { output } from '../output.js';
8
+ import { execSync } from 'node:child_process';
8
9
  // Scan subcommand
9
10
  const scanCommand = {
10
11
  name: 'scan',
@@ -337,7 +338,6 @@ const threatsCommand = {
337
338
  // Check for .env files committed to git
338
339
  const checkEnvInGit = () => {
339
340
  try {
340
- const { execSync } = require('child_process');
341
341
  const tracked = execSync('git ls-files --cached', { cwd: rootDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
342
342
  const envFiles = tracked.split('\n').filter((f) => /(?:^|\/)\.env(?:\.|$)/.test(f));
343
343
  for (const envFile of envFiles) {
@@ -17,6 +17,8 @@
17
17
  * @version 3.0.0
18
18
  */
19
19
  import { EventEmitter } from 'events';
20
+ import { execFileSync } from 'child_process';
21
+ import { request as httpRequestFn } from 'http';
20
22
  import { randomUUID } from 'crypto';
21
23
  import * as path from 'path';
22
24
  import * as fs from 'fs';
@@ -572,13 +574,11 @@ export class MCPServerManager extends EventEmitter {
572
574
  // Verify it's actually a node process (guards against PID reuse)
573
575
  // DA-CRIT-3: Use execFileSync to prevent command injection via PID values
574
576
  try {
575
- const { execFileSync } = require('child_process');
576
577
  const safePid = String(Math.floor(Math.abs(pid)));
577
578
  let cmdline = '';
578
579
  try {
579
580
  // Try /proc on Linux
580
- const { readFileSync } = require('fs');
581
- cmdline = readFileSync(`/proc/${safePid}/cmdline`, 'utf8');
581
+ cmdline = fs.readFileSync(`/proc/${safePid}/cmdline`, 'utf8');
582
582
  }
583
583
  catch {
584
584
  // Fall back to ps on macOS/other
@@ -608,8 +608,7 @@ export class MCPServerManager extends EventEmitter {
608
608
  async httpRequest(url, method, timeout) {
609
609
  return new Promise((resolve, reject) => {
610
610
  const urlObj = new URL(url);
611
- const http = require('http');
612
- const req = http.request({
611
+ const req = httpRequestFn({
613
612
  hostname: urlObj.hostname,
614
613
  port: urlObj.port,
615
614
  path: urlObj.pathname,
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { getProjectCwd } from './types.js';
15
15
  import { validateIdentifier } from './validate-input.js';
16
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
16
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, readdirSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
18
  import * as os from 'node:os';
19
19
  // Storage paths
@@ -183,7 +183,6 @@ export const performanceTools = [
183
183
  readFileSync(probeFile);
184
184
  diskLatencyMs = Math.round((performance.now() - t0) * 100) / 100;
185
185
  try {
186
- const { unlinkSync } = require('node:fs');
187
186
  unlinkSync(probeFile);
188
187
  }
189
188
  catch { /* best-effort */ }
@@ -421,7 +420,6 @@ export const performanceTools = [
421
420
  .sort((a, b) => b.percentOfTotal - a.percentOfTotal);
422
421
  // Cleanup probe file
423
422
  try {
424
- const { unlinkSync } = require('node:fs');
425
423
  unlinkSync(join(getPerfDir(), '.profile-probe'));
426
424
  }
427
425
  catch { /* ok */ }
@@ -472,7 +470,6 @@ export const performanceTools = [
472
470
  readFileSync(probe);
473
471
  diskLatencyBefore = Math.round((performance.now() - t0) * 100) / 100;
474
472
  try {
475
- const { unlinkSync } = require('node:fs');
476
473
  unlinkSync(probe);
477
474
  }
478
475
  catch { /* ok */ }
@@ -517,10 +514,9 @@ export const performanceTools = [
517
514
  if (aggressive) {
518
515
  try {
519
516
  const dir = getPerfDir();
520
- const { readdirSync, unlinkSync: ul } = require('node:fs');
521
517
  const probes = readdirSync(dir).filter((f) => f.startsWith('.'));
522
518
  probes.forEach((f) => { try {
523
- ul(join(dir, f));
519
+ unlinkSync(join(dir, f));
524
520
  }
525
521
  catch { /* ok */ } });
526
522
  if (probes.length > 0)
@@ -6,6 +6,9 @@
6
6
  * - TTL-based caching of coverage data
7
7
  * - Singleton router instance
8
8
  */
9
+ import * as pathMod from 'node:path';
10
+ import { existsSync } from 'node:fs';
11
+ import { readFile } from 'node:fs/promises';
9
12
  // ============================================================================
10
13
  // Caching for Performance
11
14
  // ============================================================================
@@ -378,7 +381,8 @@ export async function coverageGaps(options = {}) {
378
381
  * Returns null if path is invalid or attempts traversal
379
382
  */
380
383
  function validateProjectPath(inputPath) {
381
- const { resolve, normalize, isAbsolute } = require('path');
384
+ // eslint-disable-next-line @typescript-eslint/no-var-requires -- sync function, top-level import at file head
385
+ const { resolve, normalize, isAbsolute } = pathMod;
382
386
  // Default to cwd if not provided
383
387
  const basePath = inputPath || process.cwd();
384
388
  // Normalize and resolve the path
@@ -417,9 +421,7 @@ async function loadProjectCoverage(projectRoot, skipCache) {
417
421
  return cached.report;
418
422
  }
419
423
  }
420
- const { existsSync } = require('fs');
421
- const { readFile } = require('fs/promises');
422
- const { join, normalize } = require('path');
424
+ const { join, normalize } = pathMod;
423
425
  // Try common coverage locations (all relative to validated root)
424
426
  const coverageLocations = [
425
427
  ['coverage', 'coverage-final.json'],
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Diff Classifier for Change Analysis
3
3
  */
4
+ import { execFileSync, execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
4
6
  const DEFAULT_CONFIG = {
5
7
  maxDiffSize: 10000,
6
8
  classifyByImpact: true,
@@ -344,7 +346,7 @@ export function getGitDiffNumstat(ref = 'HEAD') {
344
346
  if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
345
347
  return cached.files;
346
348
  }
347
- const { execFileSync } = require('child_process');
349
+ // execFileSync imported at top level
348
350
  try {
349
351
  // SECURITY: Use execFileSync with args array instead of shell string
350
352
  // This prevents command injection via the ref parameter
@@ -417,8 +419,7 @@ export async function getGitDiffNumstatAsync(ref = 'HEAD') {
417
419
  if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
418
420
  return cached.files;
419
421
  }
420
- const { execFile } = require('child_process');
421
- const { promisify } = require('util');
422
+ // execFile + promisify imported at top level
422
423
  const execFileAsync = promisify(execFile);
423
424
  try {
424
425
  // SECURITY: Use execFile with args array instead of shell string
@@ -3,6 +3,7 @@
3
3
  * Queries npm registry and compares versions
4
4
  */
5
5
  import * as semver from 'semver';
6
+ import { createRequire } from 'node:module';
6
7
  import { shouldCheckForUpdates, recordCheck, getCachedVersions } from './rate-limiter.js';
7
8
  const DEFAULT_CONFIG = {
8
9
  enabled: true,
@@ -88,10 +89,10 @@ export function getInstalledVersion(packageName) {
88
89
  ];
89
90
  for (const modulePath of possiblePaths) {
90
91
  try {
91
- // Use dynamic import with require for package.json
92
- const resolved = require.resolve(modulePath, { paths: [process.cwd()] });
93
- // eslint-disable-next-line @typescript-eslint/no-var-requires
94
- const pkg = require(resolved);
92
+ // Use createRequire for ESM-compatible package.json loading
93
+ const esmRequire = createRequire(import.meta.url);
94
+ const resolved = esmRequire.resolve(modulePath, { paths: [process.cwd()] });
95
+ const pkg = esmRequire(resolved);
95
96
  return pkg.version;
96
97
  }
97
98
  catch {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.5.76",
3
+ "version": "3.5.78",
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",