@claude-flow/cli 3.32.0 → 3.32.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.
Files changed (34) hide show
  1. package/.claude/helpers/.helpers-version +1 -1
  2. package/.claude/helpers/helpers.manifest.json +3 -3
  3. package/.claude/helpers/statusline.cjs +38 -31
  4. package/.claude/helpers/statusline.js +19 -31
  5. package/catalog-manifest.json +2 -2
  6. package/dist/src/commands/hooks.js +20 -9
  7. package/dist/src/commands/init.d.ts +5 -0
  8. package/dist/src/commands/init.js +30 -1
  9. package/dist/src/commands/security.js +16 -11
  10. package/dist/src/funnel/insights.d.ts +1 -0
  11. package/dist/src/funnel/insights.js +4 -4
  12. package/dist/src/funnel/local-signals.d.ts +6 -1
  13. package/dist/src/funnel/local-signals.js +29 -20
  14. package/dist/src/init/executor.js +2 -2
  15. package/dist/src/mcp-tools/hooks-tools.js +13 -1
  16. package/dist/src/security/builtin-aidefence.d.ts +34 -0
  17. package/dist/src/security/builtin-aidefence.js +86 -0
  18. package/dist/src/services/fable-harness.d.ts +1 -0
  19. package/package.json +9 -8
  20. package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +1 -1
  21. package/plugins/ruflo-metaharness/scripts/smoke.sh +15 -12
  22. package/plugins/ruflo-metaharness/skills/harness-similarity/SKILL.md +1 -1
  23. package/.claude/.proven-config-version +0 -1
  24. package/.claude/proven-config.json +0 -42
  25. package/dist/src/ruvector/flash-attention.d.ts +0 -195
  26. package/dist/src/ruvector/flash-attention.js +0 -643
  27. package/dist/src/ruvector/moe-router.d.ts +0 -206
  28. package/dist/src/ruvector/moe-router.js +0 -626
  29. package/dist/src/services/event-stream.d.ts +0 -25
  30. package/dist/src/services/event-stream.js +0 -27
  31. package/dist/src/services/loop-worker-runner.d.ts +0 -16
  32. package/dist/src/services/loop-worker-runner.js +0 -34
  33. package/dist/src/services/runtime-capabilities.d.ts +0 -22
  34. package/dist/src/services/runtime-capabilities.js +0 -45
@@ -1 +1 @@
1
- 3.30.0
1
+ 3.32.2
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.32.0",
3
+ "version": "3.32.2",
4
4
  "files": {
5
5
  "auto-memory-hook.mjs": "e3e1033b24704992ddef6b31c7fa9dd7fcd9e1af7935dd77ef73402b916b31e6",
6
6
  "hook-handler.cjs": "f87ec28684bfc5fd0a54c46bd2a53a3fb037defe71d0ea4b8a5cb88a2cd87a3f",
7
7
  "intelligence.cjs": "5a55d979cb7ba5c8c4f27f3b2e6d686fbb1045d180023b803da672a37e05b915",
8
- "statusline.cjs": "7d7bdee732e1c75b863e409afb86ce0d712f4919245e990d806da6752015bffa"
8
+ "statusline.cjs": "f6fcaedad7b521248ba57c494eb1a0ce696679fa3f8709f81edf3fdecef369c2"
9
9
  }
10
10
  },
11
- "signature": "vOWP/i+ctJpD8NNHq6RhgZnxi748YJMlSSkYILMrxytsw5bhQK3322MKyz1iwyvxzNo7Jov1hPuqeDU+vCu2CQ==",
11
+ "signature": "r7TaWKVLZ6gxssGRM31J6usZrRm1Y5TAZXNmOfeLn+7VzWMrH6pZZPGTfSv6WMW49j8Ko3aamDBtahasWybSAw==",
12
12
  "algorithm": "ed25519"
13
13
  }
@@ -26,8 +26,11 @@ const { execSync } = require('child_process');
26
26
  const os = require('os');
27
27
 
28
28
  // Configuration
29
- const CONFIG = {
30
- maxAgents: 15,
29
+ const CONFIG = {
30
+ maxAgents: 15,
31
+ // Header identity defaults to project/repository name. Set `author` to
32
+ // retain the previous `git config user.name` display (#2682).
33
+ identityMode: (process.env.RUFLO_STATUSLINE_IDENTITY || 'project').toLowerCase(),
31
34
  // Session-cost display. Claude Code's cost.total_cost_usd is a client-side
32
35
  // estimate that "may differ from your actual bill" and reads as misleading on
33
36
  // subscription plans, where token usage is not billed per dollar. These let
@@ -210,7 +213,9 @@ function getStatuslineData() {
210
213
  // 60s TTL (#2337 — don't re-spawn the CLI on every rapid re-render) AND the
211
214
  // tighter promo-rotation clock (this fix — don't let a still-fresh 60s
212
215
  // cache silently freeze the promo/insight row across multiple 20s slots).
213
- if (cache.fresh && cache.promoFresh) return overlayMemoPromo(cache.data);
216
+ if (cache.fresh && cache.promoFresh) {
217
+ return applyLocalOverlays(overlayMemoPromo(cache.data));
218
+ }
214
219
 
215
220
  // #2337: prefer an already-installed CLI bin via direct `node` invocation —
216
221
  // no npx, no registry round-trip, no @latest re-resolve per render. Try
@@ -417,7 +422,7 @@ function buildLocalFallback() {
417
422
  return applyLocalOverlays({
418
423
  user: { name: 'user', gitBranch: '', modelName: 'Claude Code' },
419
424
  v3Progress: { domainsCompleted: 0, totalDomains: 5, dddProgress: 0, patternsLearned: 0, sessionsCompleted: 0 },
420
- security: { status: 'NONE', cvesFixed: 0, totalCves: 0 },
425
+ security: { status: 'NONE', findings: 0, cvesFixed: 0, totalCves: 0 },
421
426
  swarm: { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false },
422
427
  system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0, subAgents: 0 },
423
428
  lastUpdated: new Date().toISOString(),
@@ -474,14 +479,16 @@ function readJSON(filePath) {
474
479
 
475
480
  // ─── Git info (pure-Node / single exec — needed for branch display) ──────────
476
481
 
477
- function getGitInfo() {
478
- const result = {
479
- name: 'user', gitBranch: '', modified: 0, untracked: 0,
480
- staged: 0, ahead: 0, behind: 0,
481
- };
482
-
483
- const script = [
484
- 'git config user.name 2>/dev/null || echo user',
482
+ function getGitInfo() {
483
+ const result = {
484
+ name: path.basename(CWD) || 'project', gitBranch: '', modified: 0, untracked: 0,
485
+ staged: 0, ahead: 0, behind: 0,
486
+ };
487
+
488
+ const script = [
489
+ 'git rev-parse --show-toplevel 2>/dev/null || pwd',
490
+ 'echo "---SEP---"',
491
+ 'git config user.name 2>/dev/null || echo user',
485
492
  'echo "---SEP---"',
486
493
  'git branch --show-current 2>/dev/null',
487
494
  'echo "---SEP---"',
@@ -494,12 +501,14 @@ function getGitInfo() {
494
501
  if (!raw) return result;
495
502
 
496
503
  const parts = raw.split('---SEP---').map(function(s) { return s.trim(); });
497
- if (parts.length >= 4) {
498
- result.name = parts[0] || 'user';
499
- result.gitBranch = parts[1] || '';
500
-
501
- if (parts[2]) {
502
- for (const line of parts[2].split('\n')) {
504
+ if (parts.length >= 5) {
505
+ const projectName = path.basename(parts[0] || CWD) || path.basename(CWD) || 'project';
506
+ const authorName = parts[1] || 'user';
507
+ result.name = CONFIG.identityMode === 'author' ? authorName : projectName;
508
+ result.gitBranch = parts[2] || '';
509
+
510
+ if (parts[3]) {
511
+ for (const line of parts[3].split('\n')) {
503
512
  if (!line || line.length < 2) continue;
504
513
  const x = line[0], y = line[1];
505
514
  if (x === '?' && y === '?') { result.untracked++; continue; }
@@ -508,7 +517,7 @@ function getGitInfo() {
508
517
  }
509
518
  }
510
519
 
511
- const ab = (parts[3] || '0 0').split(/\s+/);
520
+ const ab = (parts[4] || '0 0').split(/\s+/);
512
521
  result.ahead = parseInt(ab[0]) || 0;
513
522
  result.behind = parseInt(ab[1]) || 0;
514
523
  }
@@ -722,8 +731,7 @@ function generateStatusline() {
722
731
  const intelligencePct = system.intelligencePct || 0;
723
732
  const memoryMB = system.memoryMB || 0;
724
733
  const subAgents = system.subAgents || 0;
725
- const cvesFixed = security.cvesFixed || 0;
726
- const totalCves = security.totalCves || 0;
734
+ const findings = Math.max(0, security.findings || 0);
727
735
  const secStatus = security.status || 'NONE';
728
736
  const adrCount = adrs.count || 0;
729
737
  const adrImpl = adrs.implemented || 0;
@@ -778,8 +786,7 @@ function generateStatusline() {
778
786
  const hooksColor = hooksEnabled > 0 ? c.brightGreen : c.dim;
779
787
  const intellColor = intelligencePct >= 80 ? c.brightGreen : intelligencePct >= 40 ? c.brightYellow : c.dim;
780
788
  const swarmInd = coordinationActive ? c.brightGreen + '◉' + c.reset + ' ' : c.dim + '○' + c.reset + ' ';
781
- const cvesClean = totalCves === 0 || cvesFixed === totalCves;
782
- const healthAllGreen = (secStatus === 'CLEAN' || secStatus === 'NONE') && cvesClean;
789
+ const healthAllGreen = (secStatus === 'CLEAN' || secStatus === 'NONE') && findings === 0;
783
790
  const opsParts = [];
784
791
  opsParts.push(c.cyan + 'Swarm ' + swarmInd + agentsColor + activeAgents + c.reset + '/' + c.brightWhite + maxAgents + c.reset);
785
792
  if (subAgents > 0) opsParts.push(c.brightPurple + '👥 ' + subAgents + c.reset);
@@ -790,14 +797,14 @@ function generateStatusline() {
790
797
  if (healthAllGreen) {
791
798
  opsParts.push(c.brightGreen + '🛡 ✓' + c.reset);
792
799
  } else {
793
- if (secStatus === 'PENDING') opsParts.push(c.brightYellow + '🛡 scan pending' + c.reset);
794
- else if (secStatus === 'IN_PROGRESS') opsParts.push(c.brightYellow + '🛡 scanning…' + c.reset);
795
- else if (secStatus === 'STALE') opsParts.push(c.brightYellow + '🛡 scan stale' + c.reset);
796
- else if (secStatus !== 'NONE' && secStatus !== 'CLEAN') opsParts.push(c.brightRed + '🛡 ' + secStatus.toLowerCase() + c.reset);
797
- if (totalCves > 0 && cvesFixed < totalCves) {
798
- const unfixed = totalCves - cvesFixed;
799
- opsParts.push(c.brightRed + '⚠ ' + unfixed + ' CVE' + (unfixed === 1 ? '' : 's') + c.reset);
800
- }
800
+ if (secStatus === 'PENDING') opsParts.push(c.brightYellow + '🛡 scan pending' + c.reset);
801
+ else if (secStatus === 'IN_PROGRESS') opsParts.push(c.brightYellow + '🛡 scanning…' + c.reset);
802
+ else if (secStatus === 'ISSUES') opsParts.push(c.brightRed + '🛡 findings' + c.reset);
803
+ else if (secStatus === 'STALE') opsParts.push(c.brightYellow + '🛡 scan stale' + c.reset);
804
+ else if (secStatus !== 'NONE' && secStatus !== 'CLEAN') opsParts.push(c.brightRed + '🛡 ' + secStatus.toLowerCase() + c.reset);
805
+ if (findings > 0) {
806
+ opsParts.push(c.brightRed + '⚠ ' + findings + ' finding' + (findings === 1 ? '' : 's') + c.reset);
807
+ }
801
808
  }
802
809
  lines.push(opsParts.join(' ' + c.dim + '·' + c.reset + ' '));
803
810
 
@@ -167,39 +167,27 @@ function getV3Progress() {
167
167
 
168
168
  // Get security status based on actual scans
169
169
  function getSecurityStatus() {
170
- // Check for security scan results in memory
170
+ // ponytail: read the NEWEST scan in .claude/security-scans/ and surface its
171
+ // real findings count. Previously hardcoded totalCves=3 and counted scan
172
+ // *files* as "CVEs fixed", fabricating "⚠ 3 CVEs" for every project.
171
173
  const scanResultsPath = path.join(process.cwd(), '.claude', 'security-scans');
172
- let cvesFixed = 0;
173
- const totalCves = 3;
174
-
175
- if (fs.existsSync(scanResultsPath)) {
176
- try {
177
- const scans = fs.readdirSync(scanResultsPath).filter(f => f.endsWith('.json'));
178
- // Each successful scan file = 1 CVE addressed
179
- cvesFixed = Math.min(totalCves, scans.length);
180
- } catch (e) {
181
- // Ignore
182
- }
183
- }
184
-
185
- // Also check .swarm/security for audit results
186
- const auditPath = path.join(process.cwd(), '.swarm', 'security');
187
- if (fs.existsSync(auditPath)) {
188
- try {
189
- const audits = fs.readdirSync(auditPath).filter(f => f.includes('audit'));
190
- cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
191
- } catch (e) {
192
- // Ignore
174
+ if (!fs.existsSync(scanResultsPath)) return { status: 'PENDING', findings: 0, cvesFixed: 0, totalCves: 0 };
175
+ try {
176
+ const files = fs.readdirSync(scanResultsPath).filter(f => f.endsWith('.json'));
177
+ if (files.length === 0) return { status: 'PENDING', findings: 0, cvesFixed: 0, totalCves: 0 };
178
+ let newest = files[0];
179
+ let newestMtime = -1;
180
+ for (const f of files) {
181
+ const st = fs.statSync(path.join(scanResultsPath, f));
182
+ if (st.mtimeMs > newestMtime) { newestMtime = st.mtimeMs; newest = f; }
193
183
  }
184
+ const scan = JSON.parse(fs.readFileSync(path.join(scanResultsPath, newest), 'utf-8'));
185
+ const findings = Math.max(0, scan.summary?.total ?? scan.totalFindings ?? scan.findings?.length ?? 0);
186
+ const status = findings > 0 ? 'ISSUES' : 'CLEAN';
187
+ return { status, findings, scannedAt: scan.timestamp, cvesFixed: 0, totalCves: 0 };
188
+ } catch (e) {
189
+ return { status: 'PENDING', findings: 0, cvesFixed: 0, totalCves: 0 };
194
190
  }
195
-
196
- const status = cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING';
197
-
198
- return {
199
- status,
200
- cvesFixed,
201
- totalCves,
202
- };
203
191
  }
204
192
 
205
193
  // Get swarm status
@@ -306,7 +294,7 @@ function generateStatusline() {
306
294
  lines.push(
307
295
  `${c.brightYellow}🤖 Swarm${c.reset} ${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
308
296
  `${c.brightPurple}👥 ${system.subAgents}${c.reset} ` +
309
- `${securityIcon} ${securityColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` +
297
+ `${securityIcon} ${securityColor}Findings ${security.findings || 0}${c.reset} ` +
310
298
  `${c.brightCyan}💾 ${system.memoryMB}MB${c.reset} ` +
311
299
  `${c.brightGreen}📂 ${String(system.contextPct).padStart(3)}%${c.reset} ` +
312
300
  `${c.dim}🧠 ${String(system.intelligencePct).padStart(3)}%${c.reset}`
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 1,
4
- "generatedAt": "2026-07-15T22:06:33.335Z",
5
- "gitSha": "a0c1ac4b",
4
+ "generatedAt": "2026-07-17T04:00:15.717Z",
5
+ "gitSha": "61047df4",
6
6
  "catalog": {
7
7
  "agents": 164,
8
8
  "tools": 387,
@@ -3487,18 +3487,26 @@ const statuslineCommand = {
3487
3487
  }
3488
3488
  // Get user info
3489
3489
  function getUserInfo() {
3490
- let name = 'user';
3490
+ const identityMode = (process.env.RUFLO_STATUSLINE_IDENTITY || 'project').toLowerCase();
3491
+ let name = path.basename(process.cwd()) || 'project';
3491
3492
  let gitBranch = '';
3492
3493
  const modelName = 'Opus 4.6 (1M context)';
3493
3494
  const isWindows = process.platform === 'win32';
3494
3495
  try {
3495
- const nameCmd = isWindows
3496
- ? 'git config user.name 2>NUL || echo user'
3497
- : 'git config user.name 2>/dev/null || echo "user"';
3496
+ const rootCmd = isWindows
3497
+ ? 'git rev-parse --show-toplevel 2>NUL'
3498
+ : 'git rev-parse --show-toplevel 2>/dev/null';
3498
3499
  const branchCmd = isWindows
3499
3500
  ? 'git branch --show-current 2>NUL || echo.'
3500
3501
  : 'git branch --show-current 2>/dev/null || echo ""';
3501
- name = execSync(nameCmd, { encoding: 'utf-8' }).trim();
3502
+ const root = execSync(rootCmd, { encoding: 'utf-8' }).trim();
3503
+ name = path.basename(root) || name;
3504
+ if (identityMode === 'author') {
3505
+ const authorCmd = isWindows
3506
+ ? 'git config user.name 2>NUL || echo user'
3507
+ : 'git config user.name 2>/dev/null || echo "user"';
3508
+ name = execSync(authorCmd, { encoding: 'utf-8' }).trim() || 'user';
3509
+ }
3502
3510
  gitBranch = execSync(branchCmd, { encoding: 'utf-8' }).trim();
3503
3511
  if (gitBranch === '.')
3504
3512
  gitBranch = '';
@@ -3555,7 +3563,10 @@ const statuslineCommand = {
3555
3563
  const terminalCols = process.stdout.columns ?? 80;
3556
3564
  const autoCompact = !ctx.flags.full && terminalCols < COMPACT_WIDTH_THRESHOLD;
3557
3565
  if (ctx.flags.compact || autoCompact) {
3558
- const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} CVE:${security.cvesFixed}/${security.totalCves} Swarm:${swarm.activeAgents}/${swarm.maxAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
3566
+ const securityCompact = security.findings > 0
3567
+ ? `Findings:${security.findings}`
3568
+ : `Security:${security.status}`;
3569
+ const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} ${securityCompact} Swarm:${swarm.activeAgents}/${swarm.maxAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
3559
3570
  output.writeln(line);
3560
3571
  return { success: true, data: statusData };
3561
3572
  }
@@ -3729,13 +3740,13 @@ const statuslineCommand = {
3729
3740
  perfIndicator;
3730
3741
  const swarmIndicator = swarm.coordinationActive ? `${c.brightGreen}◉${c.reset}` : `${c.dim}○${c.reset}`;
3731
3742
  const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
3732
- const securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'IN_PROGRESS' ? '🟡' : '🔴';
3733
- const securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
3743
+ const securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'PENDING' ? '🟡' : '🔴';
3744
+ const securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'PENDING' ? c.brightYellow : c.brightRed;
3734
3745
  const hooksColor = hooksStats.enabled > 0 ? c.brightGreen : c.dim;
3735
3746
  const line2 = `${c.brightYellow}🤖 Swarm${c.reset} ${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
3736
3747
  `${c.brightPurple}👥 ${system.subAgents}${c.reset} ` +
3737
3748
  `${c.brightBlue}🪝 ${hooksColor}${hooksStats.enabled}${c.reset}/${c.brightWhite}${hooksStats.total}${c.reset} ` +
3738
- `${securityIcon} ${securityColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` +
3749
+ `${securityIcon} ${securityColor}${security.findings > 0 ? `Findings ${security.findings}` : `Security ${security.status}`}${c.reset} ` +
3739
3750
  `${c.brightCyan}💾 ${system.memoryMB}MB${c.reset} ` +
3740
3751
  `${c.brightPurple}🧠 ${String(system.intelligencePct).padStart(3)}%${c.reset}`;
3741
3752
  const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
@@ -3,6 +3,11 @@
3
3
  * Comprehensive initialization for Claude Flow with Claude Code integration
4
4
  */
5
5
  import type { Command } from '../types.js';
6
+ export declare function runCodexInitializerCli(cwd: string, options: {
7
+ template: string;
8
+ force: boolean;
9
+ dual: boolean;
10
+ }): boolean;
6
11
  export declare const initCommand: Command;
7
12
  export default initCommand;
8
13
  //# sourceMappingURL=init.d.ts.map
@@ -6,6 +6,7 @@ import { output } from '../output.js';
6
6
  import { confirm, select, multiSelect, input } from '../prompt.js';
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
+ import { spawnSync } from 'node:child_process';
9
10
  import { executeInit, executeUpgrade, executeUpgradeWithMissing, DEFAULT_INIT_OPTIONS, MINIMAL_INIT_OPTIONS, FULL_INIT_OPTIONS, } from '../init/index.js';
10
11
  import { ENROLLMENT_SCREEN, recordEnrollmentOutcome, shouldOfferEnrollment, } from '../funnel/enrollment.js';
11
12
  import { commandExists } from '../services/harness-hosts.js';
@@ -82,6 +83,27 @@ async function resolveCodexInitializer(cwd) {
82
83
  }
83
84
  return undefined;
84
85
  }
86
+ // Keep Codex out of the CLI dependency graph so cold `npx ruflo --version`
87
+ // remains fast (#2561). An explicit `init --codex` may fetch the small,
88
+ // stable adapter on demand when it is not already installed by an umbrella
89
+ // package, the current project, or the global npm prefix.
90
+ export function runCodexInitializerCli(cwd, options) {
91
+ const npxArgs = [
92
+ '-y',
93
+ '@claude-flow/codex@latest',
94
+ 'init',
95
+ '--template',
96
+ options.template,
97
+ ...(options.force ? ['--force'] : []),
98
+ ...(options.dual ? ['--dual'] : []),
99
+ ];
100
+ const result = process.platform === 'win32'
101
+ ? spawnSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', ['npx', ...npxArgs].join(' ')], { cwd, stdio: 'inherit', windowsHide: true })
102
+ : spawnSync('npx', npxArgs, { cwd, stdio: 'inherit' });
103
+ if (result.error)
104
+ throw result.error;
105
+ return result.status === 0;
106
+ }
85
107
  // #2666-adjacent — quietly wire up Codex too when a plain `ruflo init` (no
86
108
  // --codex/--dual) runs on a machine that also has the OpenAI Codex CLI on
87
109
  // PATH: registers its MCP server and installs skills alongside the Claude
@@ -194,7 +216,14 @@ async function initCodexAction(ctx, options) {
194
216
  try {
195
217
  const CodexInitializer = await resolveCodexInitializer(ctx.cwd);
196
218
  if (!CodexInitializer) {
197
- throw new Error('Cannot find module @claude-flow/codex');
219
+ spinner.stop();
220
+ output.printInfo('Fetching the stable Codex adapter for this initialization...');
221
+ const success = runCodexInitializerCli(ctx.cwd, { template, force, dual: dualMode });
222
+ if (!success) {
223
+ output.printError('Codex initialization failed while running @claude-flow/codex@latest.');
224
+ return { success: false, exitCode: 1 };
225
+ }
226
+ return { success: true, data: { adapter: '@claude-flow/codex@latest' } };
198
227
  }
199
228
  const initializer = new CodexInitializer();
200
229
  const result = await initializer.initialize({
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { output } from '../output.js';
8
8
  import { execSync } from 'node:child_process';
9
+ import { createBuiltinAIDefence } from '../security/builtin-aidefence.js';
9
10
  // Scan subcommand
10
11
  const scanCommand = {
11
12
  name: 'scan',
@@ -861,16 +862,17 @@ const defendCommand = {
861
862
  output.writeln(output.bold('🛡️ AIDefence - AI Manipulation Defense System'));
862
863
  output.writeln(output.dim('─'.repeat(55)));
863
864
  // Dynamic import of aidefence (allows package to be optional)
864
- let createAIDefence;
865
+ let defender;
865
866
  try {
866
867
  const aidefence = await import('@claude-flow/aidefence');
867
- createAIDefence = aidefence.createAIDefence;
868
+ defender = aidefence.createAIDefence({ enableLearning });
868
869
  }
869
870
  catch {
870
- output.error('AIDefence package not installed. Run: npm install @claude-flow/aidefence');
871
- return { success: false, message: 'AIDefence not available' };
871
+ // Keep cold npx startup lean (#2561): the full learning engine remains
872
+ // user-installable, while the CLI always ships a deterministic scanner.
873
+ defender = createBuiltinAIDefence();
874
+ output.writeln(output.dim('Using built-in defense engine (install @claude-flow/aidefence for adaptive learning)'));
872
875
  }
873
- const defender = createAIDefence({ enableLearning });
874
876
  // Show stats mode
875
877
  if (showStats) {
876
878
  const stats = await defender.getStats();
@@ -893,8 +895,8 @@ const defendCommand = {
893
895
  output.writeln(output.dim(`Reading file: ${filePath}`));
894
896
  }
895
897
  catch (err) {
896
- output.error(`Failed to read file: ${filePath}`);
897
- return { success: false, message: 'File not found' };
898
+ output.printError(`Failed to read file: ${filePath}`);
899
+ return { success: false, exitCode: 2, message: 'File not found' };
898
900
  }
899
901
  }
900
902
  if (!textToScan) {
@@ -914,8 +916,9 @@ const defendCommand = {
914
916
  spinner.start();
915
917
  // Perform scan
916
918
  const startTime = performance.now();
917
- const result = quickMode
918
- ? { ...defender.quickScan(textToScan), threats: [], piiFound: false, detectionTimeMs: 0, inputHash: '', safe: !defender.quickScan(textToScan).threat }
919
+ const quickResult = quickMode ? defender.quickScan(textToScan) : undefined;
920
+ const result = quickResult
921
+ ? { ...quickResult, threats: [], piiFound: false, detectionTimeMs: 0, inputHash: '', safe: !quickResult.threat }
919
922
  : await defender.detect(textToScan);
920
923
  const scanTime = performance.now() - startTime;
921
924
  spinner.stop();
@@ -927,7 +930,8 @@ const defendCommand = {
927
930
  piiFound: result.piiFound,
928
931
  detectionTimeMs: scanTime,
929
932
  }, null, 2));
930
- return { success: true };
933
+ const safe = result.safe && !result.piiFound;
934
+ return { success: safe, exitCode: safe ? 0 : 1 };
931
935
  }
932
936
  // Text output
933
937
  output.writeln();
@@ -969,7 +973,8 @@ const defendCommand = {
969
973
  }
970
974
  }
971
975
  output.writeln(output.dim(`Detection time: ${scanTime.toFixed(3)}ms`));
972
- return { success: result.safe };
976
+ const safe = result.safe && !result.piiFound;
977
+ return { success: safe, exitCode: safe ? 0 : 1 };
973
978
  },
974
979
  };
975
980
  // Main security command
@@ -30,6 +30,7 @@ export interface LocalInsight {
30
30
  export interface LocalInsightContext {
31
31
  security?: {
32
32
  status: string;
33
+ findings?: number;
33
34
  cvesFixed: number;
34
35
  totalCves: number;
35
36
  };
@@ -42,11 +42,11 @@ function securityInsight(ctx) {
42
42
  const s = ctx.security;
43
43
  if (!s)
44
44
  return null;
45
- const pending = s.totalCves - s.cvesFixed;
46
- if (pending > 0) {
45
+ const findings = Math.max(0, s.findings ?? 0);
46
+ if (s.status === 'ISSUES' || findings > 0) {
47
47
  return {
48
- id: 'insight-cves-pending',
49
- text: `⚠ ${pending} CVE${pending === 1 ? '' : 's'} pending Run ruflo security scan --depth full`,
48
+ id: 'insight-security-findings',
49
+ text: `⚠ ${findings} security finding${findings === 1 ? '' : 's'} — Review the latest ruflo security scan`,
50
50
  priority: 90,
51
51
  };
52
52
  }
@@ -1,6 +1,11 @@
1
1
  export interface SecurityStatus {
2
- status: 'CLEAN' | 'IN_PROGRESS' | 'PENDING';
2
+ status: 'CLEAN' | 'ISSUES' | 'PENDING';
3
+ /** Generic code-pattern findings from `ruflo security scan` (not CVEs). */
4
+ findings: number;
5
+ scannedAt?: string;
6
+ /** @deprecated Retained as a zero-valued compatibility field. */
3
7
  cvesFixed: number;
8
+ /** @deprecated Retained as a zero-valued compatibility field. */
4
9
  totalCves: number;
5
10
  }
6
11
  export declare function getSecurityStatus(cwd?: string): SecurityStatus;
@@ -11,30 +11,39 @@ import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import { execSync } from 'child_process';
13
13
  export function getSecurityStatus(cwd = process.cwd()) {
14
+ const empty = {
15
+ status: 'PENDING', findings: 0, cvesFixed: 0, totalCves: 0,
16
+ };
14
17
  const scanResultsPath = path.join(cwd, '.claude', 'security-scans');
15
- let cvesFixed = 0;
16
- const totalCves = 3;
17
- if (fs.existsSync(scanResultsPath)) {
18
- try {
19
- const scans = fs.readdirSync(scanResultsPath).filter((f) => f.endsWith('.json'));
20
- cvesFixed = Math.min(totalCves, scans.length);
21
- }
22
- catch {
23
- // Ignore
18
+ if (!fs.existsSync(scanResultsPath))
19
+ return empty;
20
+ try {
21
+ const files = fs.readdirSync(scanResultsPath).filter((f) => f.endsWith('.json'));
22
+ if (files.length === 0)
23
+ return empty;
24
+ let newest = files[0];
25
+ let newestMtime = -1;
26
+ for (const f of files) {
27
+ const st = fs.statSync(path.join(scanResultsPath, f));
28
+ if (st.mtimeMs > newestMtime) {
29
+ newestMtime = st.mtimeMs;
30
+ newest = f;
31
+ }
24
32
  }
33
+ const scan = JSON.parse(fs.readFileSync(path.join(scanResultsPath, newest), 'utf-8'));
34
+ const rawFindings = scan.summary?.total ?? scan.totalFindings ?? scan.findings?.length ?? 0;
35
+ const findings = Math.max(0, Number.isFinite(rawFindings) ? rawFindings : 0);
36
+ return {
37
+ status: findings > 0 ? 'ISSUES' : 'CLEAN',
38
+ findings,
39
+ scannedAt: typeof scan.timestamp === 'string' ? scan.timestamp : undefined,
40
+ cvesFixed: 0,
41
+ totalCves: 0,
42
+ };
25
43
  }
26
- const auditPath = path.join(cwd, '.swarm', 'security');
27
- if (fs.existsSync(auditPath)) {
28
- try {
29
- const audits = fs.readdirSync(auditPath).filter((f) => f.includes('audit'));
30
- cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
31
- }
32
- catch {
33
- // Ignore
34
- }
44
+ catch {
45
+ return empty;
35
46
  }
36
- const status = cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING';
37
- return { status, cvesFixed, totalCves };
38
47
  }
39
48
  export function getSwarmStatus() {
40
49
  let activeAgents = 0;
@@ -619,7 +619,7 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
619
619
  initialized: new Date().toISOString(),
620
620
  status: 'PENDING',
621
621
  cvesFixed: 0,
622
- totalCves: 3,
622
+ totalCves: 0,
623
623
  lastScan: null,
624
624
  _note: 'Run: npx @claude-flow/cli@latest security scan'
625
625
  };
@@ -1541,7 +1541,7 @@ async function writeInitialMetrics(targetDir, options, result) {
1541
1541
  initialized: new Date().toISOString(),
1542
1542
  status: 'PENDING',
1543
1543
  cvesFixed: 0,
1544
- totalCves: 3,
1544
+ totalCves: 0,
1545
1545
  lastScan: null,
1546
1546
  _note: 'Run: npx @claude-flow/cli@latest security scan'
1547
1547
  };
@@ -2010,8 +2010,9 @@ export const hooksSessionEnd = {
2010
2010
  }
2011
2011
  // Phase 5: Wire ReflexionMemory session end + NightlyLearner consolidation via bridge
2012
2012
  let sessionPersistence = null;
2013
+ let bridge = null;
2013
2014
  try {
2014
- const bridge = await import('../memory/memory-bridge.js');
2015
+ bridge = await import('../memory/memory-bridge.js');
2015
2016
  const result = await bridge.bridgeSessionEnd({
2016
2017
  sessionId,
2017
2018
  summary: saveState ? 'Session ended with state saved' : 'Session ended',
@@ -2028,6 +2029,17 @@ export const hooksSessionEnd = {
2028
2029
  catch {
2029
2030
  // Bridge not available
2030
2031
  }
2032
+ finally {
2033
+ // Release AgentDB/ONNX resources after one-shot session persistence.
2034
+ // A partially initialized native pool can otherwise keep Node alive
2035
+ // after the command has completed all logical work (#2691).
2036
+ try {
2037
+ await bridge?.shutdownBridge();
2038
+ }
2039
+ catch {
2040
+ // Cleanup is best-effort and must not fail session-end.
2041
+ }
2042
+ }
2031
2043
  return {
2032
2044
  sessionId,
2033
2045
  duration: 3600000, // 1 hour in ms
@@ -0,0 +1,34 @@
1
+ export interface BuiltinThreat {
2
+ type: string;
3
+ severity: 'critical' | 'high' | 'medium' | 'low';
4
+ description: string;
5
+ confidence: number;
6
+ }
7
+ export interface DefenceResult {
8
+ safe: boolean;
9
+ threats: BuiltinThreat[];
10
+ piiFound: boolean;
11
+ detectionTimeMs: number;
12
+ inputHash: string;
13
+ }
14
+ export interface DefenceEngine {
15
+ detect(input: string): Promise<DefenceResult>;
16
+ quickScan(input: string): {
17
+ threat: boolean;
18
+ confidence: number;
19
+ type?: string;
20
+ };
21
+ getStats(): Promise<{
22
+ detectionCount: number;
23
+ avgDetectionTimeMs: number;
24
+ learnedPatterns: number;
25
+ mitigationStrategies: number;
26
+ avgMitigationEffectiveness: number;
27
+ }>;
28
+ getBestMitigation(type: string): Promise<{
29
+ strategy: string;
30
+ effectiveness: number;
31
+ } | null>;
32
+ }
33
+ export declare function createBuiltinAIDefence(): DefenceEngine;
34
+ //# sourceMappingURL=builtin-aidefence.d.ts.map