@claude-flow/cli 3.0.0-alpha.160 β†’ 3.0.0-alpha.162

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.
@@ -3,7 +3,20 @@
3
3
  * Claude Flow V3 Statusline Generator
4
4
  * Displays real-time V3 implementation progress and system status
5
5
  *
6
- * Usage: node statusline.cjs [--json] [--compact]
6
+ * Usage: node statusline.cjs [options]
7
+ *
8
+ * Options:
9
+ * (default) Safe multi-line output with collision zone avoidance
10
+ * --single Single-line output (completely avoids collision)
11
+ * --unsafe Legacy multi-line without collision avoidance
12
+ * --legacy Alias for --unsafe
13
+ * --json JSON output with pretty printing
14
+ * --compact JSON output without formatting
15
+ *
16
+ * Collision Zone Fix (Issue #985):
17
+ * Claude Code writes its internal status (e.g., "7s β€’ 1p") at absolute
18
+ * terminal coordinates (columns 15-25 on second-to-last line). The safe
19
+ * mode pads the collision line with spaces to push content past column 25.
7
20
  *
8
21
  * IMPORTANT: This file uses .cjs extension to work in ES module projects.
9
22
  * The require() syntax is intentional for CommonJS compatibility.
@@ -27,6 +40,10 @@ const CONFIG = {
27
40
  topology: 'hierarchical-mesh',
28
41
  };
29
42
 
43
+ // Cross-platform helpers
44
+ const isWindows = process.platform === 'win32';
45
+ const nullDevice = isWindows ? 'NUL' : '/dev/null';
46
+
30
47
  // ANSI colors
31
48
  const c = {
32
49
  reset: '\x1b[0m',
@@ -54,8 +71,15 @@ function getUserInfo() {
54
71
  let modelName = 'Unknown';
55
72
 
56
73
  try {
57
- name = execSync('git config user.name 2>/dev/null || echo "user"', { encoding: 'utf-8' }).trim();
58
- gitBranch = execSync('git branch --show-current 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim();
74
+ const gitUserCmd = isWindows
75
+ ? 'git config user.name 2>NUL || echo user'
76
+ : 'git config user.name 2>/dev/null || echo "user"';
77
+ const gitBranchCmd = isWindows
78
+ ? 'git branch --show-current 2>NUL || echo.'
79
+ : 'git branch --show-current 2>/dev/null || echo ""';
80
+ name = execSync(gitUserCmd, { encoding: 'utf-8' }).trim();
81
+ gitBranch = execSync(gitBranchCmd, { encoding: 'utf-8' }).trim();
82
+ if (gitBranch === '.') gitBranch = ''; // Windows echo. outputs a dot
59
83
  } catch (e) {
60
84
  // Ignore errors
61
85
  }
@@ -81,23 +105,21 @@ function getUserInfo() {
81
105
  if (lastModelUsage) {
82
106
  const modelIds = Object.keys(lastModelUsage);
83
107
  if (modelIds.length > 0) {
84
- // Find the most recently used model by checking lastUsedAt timestamps
85
- // or fall back to the last key in the object (preserves insertion order in modern JS)
108
+ // Take the last model (most recently added to the object)
109
+ // Or find the one with most tokens (most actively used this session)
86
110
  let modelId = modelIds[modelIds.length - 1];
87
- let latestTimestamp = 0;
88
-
89
- for (const id of modelIds) {
90
- const usage = lastModelUsage[id];
91
- // Check for lastUsedAt timestamp (if available)
92
- if (usage.lastUsedAt) {
93
- const ts = new Date(usage.lastUsedAt).getTime();
94
- if (ts > latestTimestamp) {
95
- latestTimestamp = ts;
111
+ if (modelIds.length > 1) {
112
+ // If multiple models, pick the one with most total tokens
113
+ let maxTokens = 0;
114
+ for (const id of modelIds) {
115
+ const usage = lastModelUsage[id];
116
+ const total = (usage.inputTokens || 0) + (usage.outputTokens || 0);
117
+ if (total > maxTokens) {
118
+ maxTokens = total;
96
119
  modelId = id;
97
120
  }
98
121
  }
99
122
  }
100
-
101
123
  // Parse model ID to human-readable name
102
124
  if (modelId.includes('opus')) modelName = 'Opus 4.5';
103
125
  else if (modelId.includes('sonnet')) modelName = 'Sonnet 4';
@@ -161,25 +183,6 @@ function getLearningStats() {
161
183
  function getV3Progress() {
162
184
  const learning = getLearningStats();
163
185
 
164
- // Check for metrics file first (created by init)
165
- const metricsPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'v3-progress.json');
166
- if (fs.existsSync(metricsPath)) {
167
- try {
168
- const data = JSON.parse(fs.readFileSync(metricsPath, 'utf-8'));
169
- if (data.domains && data.ddd) {
170
- return {
171
- domainsCompleted: data.domains.completed || 0,
172
- totalDomains: data.domains.total || 5,
173
- dddProgress: data.ddd.progress || 0,
174
- patternsLearned: data.learning?.patternsLearned || learning.patterns,
175
- sessionsCompleted: data.learning?.sessionsCompleted || learning.sessions
176
- };
177
- }
178
- } catch (e) {
179
- // Fall through to pattern-based calculation
180
- }
181
- }
182
-
183
186
  // DDD progress based on actual learned patterns
184
187
  // New install: 0 patterns = 0/5 domains, 0% DDD
185
188
  // As patterns grow: 10+ patterns = 1 domain, 50+ = 2, 100+ = 3, 200+ = 4, 500+ = 5
@@ -204,26 +207,11 @@ function getV3Progress() {
204
207
 
205
208
  // Get security status based on actual scans
206
209
  function getSecurityStatus() {
207
- const totalCves = 3;
208
- let cvesFixed = 0;
209
-
210
- // Check audit-status.json first (created by init)
211
- const auditStatusPath = path.join(process.cwd(), '.claude-flow', 'security', 'audit-status.json');
212
- if (fs.existsSync(auditStatusPath)) {
213
- try {
214
- const data = JSON.parse(fs.readFileSync(auditStatusPath, 'utf-8'));
215
- return {
216
- status: data.status || 'PENDING',
217
- cvesFixed: data.cvesFixed || 0,
218
- totalCves: data.totalCves || 3,
219
- };
220
- } catch (e) {
221
- // Fall through to scan directory check
222
- }
223
- }
224
-
225
210
  // Check for security scan results in memory
226
211
  const scanResultsPath = path.join(process.cwd(), '.claude', 'security-scans');
212
+ let cvesFixed = 0;
213
+ const totalCves = 3;
214
+
227
215
  if (fs.existsSync(scanResultsPath)) {
228
216
  try {
229
217
  const scans = fs.readdirSync(scanResultsPath).filter(f => f.endsWith('.json'));
@@ -235,10 +223,10 @@ function getSecurityStatus() {
235
223
  }
236
224
 
237
225
  // Also check .swarm/security for audit results
238
- const swarmAuditPath = path.join(process.cwd(), '.swarm', 'security');
239
- if (fs.existsSync(swarmAuditPath)) {
226
+ const auditPath = path.join(process.cwd(), '.swarm', 'security');
227
+ if (fs.existsSync(auditPath)) {
240
228
  try {
241
- const audits = fs.readdirSync(swarmAuditPath).filter(f => f.includes('audit'));
229
+ const audits = fs.readdirSync(auditPath).filter(f => f.includes('audit'));
242
230
  cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
243
231
  } catch (e) {
244
232
  // Ignore
@@ -254,45 +242,23 @@ function getSecurityStatus() {
254
242
  };
255
243
  }
256
244
 
257
- // Get swarm status (cross-platform)
245
+ // Get swarm status
258
246
  function getSwarmStatus() {
259
247
  let activeAgents = 0;
260
248
  let coordinationActive = false;
261
249
 
262
- // Check swarm-activity.json first (works on all platforms)
263
- const activityPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'swarm-activity.json');
264
- if (fs.existsSync(activityPath)) {
265
- try {
266
- const data = JSON.parse(fs.readFileSync(activityPath, 'utf-8'));
267
- if (data.swarm) {
268
- return {
269
- activeAgents: data.swarm.agent_count || 0,
270
- maxAgents: CONFIG.maxAgents,
271
- coordinationActive: data.swarm.coordination_active || false,
272
- };
273
- }
274
- } catch (e) {
275
- // Fall through to process detection
276
- }
277
- }
278
-
279
- // Platform-specific process detection
280
- const isWindows = process.platform === 'win32';
281
250
  try {
282
251
  if (isWindows) {
283
- // Windows: use tasklist
284
- const ps = execSync('tasklist /FI "IMAGENAME eq node.exe" /NH 2>nul || echo ""', { encoding: 'utf-8' });
285
- const nodeProcesses = (ps.match(/node\.exe/gi) || []).length;
286
- activeAgents = Math.max(0, Math.floor(nodeProcesses / 3)); // Heuristic
287
- coordinationActive = nodeProcesses > 0;
252
+ // Windows: use tasklist and findstr
253
+ const ps = execSync('tasklist 2>NUL | findstr /I "agentic-flow" 2>NUL | find /C /V "" 2>NUL || echo 0', { encoding: 'utf-8' });
254
+ activeAgents = Math.max(0, parseInt(ps.trim()) || 0);
288
255
  } else {
289
- // Unix: use ps
290
256
  const ps = execSync('ps aux 2>/dev/null | grep -c agentic-flow || echo "0"', { encoding: 'utf-8' });
291
257
  activeAgents = Math.max(0, parseInt(ps.trim()) - 1);
292
- coordinationActive = activeAgents > 0;
293
258
  }
259
+ coordinationActive = activeAgents > 0;
294
260
  } catch (e) {
295
- // Ignore errors - return defaults
261
+ // Ignore errors - default to 0 agents
296
262
  }
297
263
 
298
264
  return {
@@ -302,46 +268,22 @@ function getSwarmStatus() {
302
268
  };
303
269
  }
304
270
 
305
- // Get system metrics (cross-platform)
271
+ // Get system metrics (dynamic based on actual state)
306
272
  function getSystemMetrics() {
307
273
  let memoryMB = 0;
308
274
  let subAgents = 0;
309
275
 
310
- // Check learning.json first (works on all platforms)
311
- const learningMetricsPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'learning.json');
312
- let intelligenceFromFile = null;
313
- let contextFromFile = null;
314
- if (fs.existsSync(learningMetricsPath)) {
315
- try {
316
- const data = JSON.parse(fs.readFileSync(learningMetricsPath, 'utf-8'));
317
- if (data.routing?.accuracy !== undefined) {
318
- intelligenceFromFile = Math.min(100, Math.floor(data.routing.accuracy));
319
- }
320
- if (data.sessions?.total !== undefined) {
321
- contextFromFile = Math.min(100, data.sessions.total * 5);
322
- }
323
- } catch (e) {
324
- // Fall through
325
- }
326
- }
327
-
328
- // Platform-specific memory detection
329
- const isWindows = process.platform === 'win32';
330
276
  try {
331
277
  if (isWindows) {
332
- // Windows: use process.memoryUsage() (most reliable cross-platform)
278
+ // Windows: use tasklist for memory info, fallback to process.memoryUsage
279
+ // tasklist memory column is complex to parse, use Node.js API instead
333
280
  memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
334
281
  } else {
335
- // Unix: try ps command, fallback to process.memoryUsage()
336
- try {
337
- const mem = execSync('ps aux | grep -E "(node|agentic|claude)" | grep -v grep | awk \'{sum += \$6} END {print int(sum/1024)}\'', { encoding: 'utf-8' });
338
- memoryMB = parseInt(mem.trim()) || 0;
339
- } catch (e) {
340
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
341
- }
282
+ const mem = execSync('ps aux | grep -E "(node|agentic|claude)" | grep -v grep | awk \'{sum += $6} END {print int(sum/1024)}\'', { encoding: 'utf-8' });
283
+ memoryMB = parseInt(mem.trim()) || 0;
342
284
  }
343
285
  } catch (e) {
344
- // Fallback to Node.js memory API
286
+ // Fallback
345
287
  memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
346
288
  }
347
289
 
@@ -349,34 +291,23 @@ function getSystemMetrics() {
349
291
  const learning = getLearningStats();
350
292
 
351
293
  // Intelligence % based on learned patterns (0 patterns = 0%, 1000+ = 100%)
352
- const intelligencePct = intelligenceFromFile !== null
353
- ? intelligenceFromFile
354
- : Math.min(100, Math.floor((learning.patterns / 10) * 1));
294
+ const intelligencePct = Math.min(100, Math.floor((learning.patterns / 10) * 1));
355
295
 
356
296
  // Context % based on session history (0 sessions = 0%, grows with usage)
357
- const contextPct = contextFromFile !== null
358
- ? contextFromFile
359
- : Math.min(100, Math.floor(learning.sessions * 5));
297
+ const contextPct = Math.min(100, Math.floor(learning.sessions * 5));
360
298
 
361
- // Count active sub-agents (cross-platform via metrics file)
362
- const activityPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'swarm-activity.json');
363
- if (fs.existsSync(activityPath)) {
364
- try {
365
- const data = JSON.parse(fs.readFileSync(activityPath, 'utf-8'));
366
- subAgents = data.processes?.estimated_agents || 0;
367
- } catch (e) {
368
- // Ignore
369
- }
370
- }
371
-
372
- // Fallback to process detection on Unix only
373
- if (subAgents === 0 && !isWindows) {
374
- try {
299
+ // Count active sub-agents from process list
300
+ try {
301
+ if (isWindows) {
302
+ // Windows: use tasklist and findstr for agent counting
303
+ const agents = execSync('tasklist 2>NUL | findstr /I "claude-flow" 2>NUL | find /C /V "" 2>NUL || echo 0', { encoding: 'utf-8' });
304
+ subAgents = Math.max(0, parseInt(agents.trim()) || 0);
305
+ } else {
375
306
  const agents = execSync('ps aux 2>/dev/null | grep -c "claude-flow.*agent" || echo "0"', { encoding: 'utf-8' });
376
307
  subAgents = Math.max(0, parseInt(agents.trim()) - 1);
377
- } catch (e) {
378
- // Ignore
379
308
  }
309
+ } catch (e) {
310
+ // Ignore - default to 0
380
311
  }
381
312
 
382
313
  return {
@@ -387,224 +318,6 @@ function getSystemMetrics() {
387
318
  };
388
319
  }
389
320
 
390
- // Get ADR (Architecture Decision Records) status
391
- function getADRStatus() {
392
- const adrPaths = [
393
- path.join(process.cwd(), 'docs', 'adrs'),
394
- path.join(process.cwd(), 'docs', 'adr'),
395
- path.join(process.cwd(), 'adr'),
396
- path.join(process.cwd(), 'ADR'),
397
- path.join(process.cwd(), '.claude-flow', 'adrs'),
398
- path.join(process.cwd(), 'v3', 'implementation', 'adrs'),
399
- path.join(process.cwd(), 'implementation', 'adrs'),
400
- ];
401
-
402
- let count = 0;
403
- let implemented = 0;
404
-
405
- for (const adrPath of adrPaths) {
406
- if (fs.existsSync(adrPath)) {
407
- try {
408
- const files = fs.readdirSync(adrPath).filter(f =>
409
- f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\d{4}-/.test(f))
410
- );
411
- count = files.length;
412
-
413
- // Check for implemented status in ADR files
414
- for (const file of files) {
415
- try {
416
- const content = fs.readFileSync(path.join(adrPath, file), 'utf-8');
417
- if (content.includes('Status: Implemented') || content.includes('status: implemented') ||
418
- content.includes('Status: Accepted') || content.includes('status: accepted')) {
419
- implemented++;
420
- }
421
- } catch (e) {
422
- // Skip unreadable files
423
- }
424
- }
425
- break;
426
- } catch (e) {
427
- // Ignore
428
- }
429
- }
430
- }
431
-
432
- return { count, implemented };
433
- }
434
-
435
- // Get hooks status (enabled/registered hooks)
436
- function getHooksStatus() {
437
- let enabled = 0;
438
- let total = 17; // V3 has 17 hook types
439
-
440
- // Check .claude/settings.json for hooks config
441
- const settingsPaths = [
442
- path.join(process.cwd(), '.claude', 'settings.json'),
443
- path.join(process.cwd(), '.claude', 'settings.local.json'),
444
- ];
445
-
446
- for (const settingsPath of settingsPaths) {
447
- if (fs.existsSync(settingsPath)) {
448
- try {
449
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
450
- if (settings.hooks) {
451
- // Claude Code native hooks format: PreToolUse, PostToolUse, SessionStart, etc.
452
- const hookCategories = Object.keys(settings.hooks);
453
- for (const category of hookCategories) {
454
- const categoryHooks = settings.hooks[category];
455
- if (Array.isArray(categoryHooks) && categoryHooks.length > 0) {
456
- // Count categories with at least one hook defined
457
- enabled++;
458
- }
459
- }
460
- }
461
- break;
462
- } catch (e) {
463
- // Ignore parse errors
464
- }
465
- }
466
- }
467
-
468
- // Also check for hook files in .claude/hooks
469
- const hooksDir = path.join(process.cwd(), '.claude', 'hooks');
470
- if (fs.existsSync(hooksDir)) {
471
- try {
472
- const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh'));
473
- enabled = Math.max(enabled, hookFiles.length);
474
- } catch (e) {
475
- // Ignore
476
- }
477
- }
478
-
479
- return { enabled, total };
480
- }
481
-
482
- // Get AgentDB memory stats
483
- function getAgentDBStats() {
484
- let vectorCount = 0;
485
- let dbSizeKB = 0;
486
- let namespaces = 0;
487
-
488
- const dbPaths = [
489
- path.join(process.cwd(), '.claude-flow', 'agentdb'),
490
- path.join(process.cwd(), '.swarm', 'agentdb'),
491
- path.join(process.cwd(), 'data', 'agentdb'),
492
- path.join(process.cwd(), '.claude', 'memory'),
493
- ];
494
-
495
- for (const dbPath of dbPaths) {
496
- if (fs.existsSync(dbPath)) {
497
- try {
498
- const stats = fs.statSync(dbPath);
499
- if (stats.isDirectory()) {
500
- // Count database files and estimate vectors
501
- const files = fs.readdirSync(dbPath);
502
- namespaces = files.filter(f => f.endsWith('.db') || f.endsWith('.sqlite')).length;
503
-
504
- // Calculate total size
505
- for (const file of files) {
506
- const filePath = path.join(dbPath, file);
507
- const fileStat = fs.statSync(filePath);
508
- if (fileStat.isFile()) {
509
- dbSizeKB += fileStat.size / 1024;
510
- }
511
- }
512
-
513
- // Estimate vector count (~0.5KB per vector on average)
514
- vectorCount = Math.floor(dbSizeKB / 0.5);
515
- } else {
516
- // Single file database
517
- dbSizeKB = stats.size / 1024;
518
- vectorCount = Math.floor(dbSizeKB / 0.5);
519
- namespaces = 1;
520
- }
521
- break;
522
- } catch (e) {
523
- // Ignore
524
- }
525
- }
526
- }
527
-
528
- // Also check for vectors.json (simple vector store)
529
- const vectorsPath = path.join(process.cwd(), '.claude-flow', 'vectors.json');
530
- if (fs.existsSync(vectorsPath) && vectorCount === 0) {
531
- try {
532
- const data = JSON.parse(fs.readFileSync(vectorsPath, 'utf-8'));
533
- if (Array.isArray(data)) {
534
- vectorCount = data.length;
535
- } else if (data.vectors) {
536
- vectorCount = Object.keys(data.vectors).length;
537
- }
538
- } catch (e) {
539
- // Ignore
540
- }
541
- }
542
-
543
- return { vectorCount, dbSizeKB: Math.floor(dbSizeKB), namespaces };
544
- }
545
-
546
- // Get test statistics
547
- function getTestStats() {
548
- let testFiles = 0;
549
- let testCases = 0;
550
-
551
- const testDirs = [
552
- path.join(process.cwd(), 'tests'),
553
- path.join(process.cwd(), 'test'),
554
- path.join(process.cwd(), '__tests__'),
555
- path.join(process.cwd(), 'src', '__tests__'),
556
- path.join(process.cwd(), 'v3', '__tests__'),
557
- ];
558
-
559
- // Recursively count test files
560
- function countTestFiles(dir, depth = 0) {
561
- if (depth > 3) return; // Limit recursion
562
- if (!fs.existsSync(dir)) return;
563
-
564
- try {
565
- const entries = fs.readdirSync(dir, { withFileTypes: true });
566
- for (const entry of entries) {
567
- if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
568
- countTestFiles(path.join(dir, entry.name), depth + 1);
569
- } else if (entry.isFile()) {
570
- const name = entry.name;
571
- if (name.includes('.test.') || name.includes('.spec.') ||
572
- name.includes('_test.') || name.includes('_spec.') ||
573
- name.startsWith('test_') || name.startsWith('spec_')) {
574
- testFiles++;
575
-
576
- // Try to estimate test cases from file
577
- try {
578
- const content = fs.readFileSync(path.join(dir, name), 'utf-8');
579
- // Count it(), test(), describe() patterns
580
- const itMatches = (content.match(/\bit\s*\(/g) || []).length;
581
- const testMatches = (content.match(/\btest\s*\(/g) || []).length;
582
- testCases += itMatches + testMatches;
583
- } catch (e) {
584
- // Estimate 3 tests per file if can't read
585
- testCases += 3;
586
- }
587
- }
588
- }
589
- }
590
- } catch (e) {
591
- // Ignore
592
- }
593
- }
594
-
595
- for (const dir of testDirs) {
596
- countTestFiles(dir);
597
- }
598
-
599
- // Also check src directory for colocated tests
600
- const srcDir = path.join(process.cwd(), 'src');
601
- if (fs.existsSync(srcDir)) {
602
- countTestFiles(srcDir);
603
- }
604
-
605
- return { testFiles, testCases };
606
- }
607
-
608
321
  // Generate progress bar
609
322
  function progressBar(current, total) {
610
323
  const width = 5;
@@ -620,10 +333,6 @@ function generateStatusline() {
620
333
  const security = getSecurityStatus();
621
334
  const swarm = getSwarmStatus();
622
335
  const system = getSystemMetrics();
623
- const adrs = getADRStatus();
624
- const hooks = getHooksStatus();
625
- const agentdb = getAgentDBStats();
626
- const tests = getTestStats();
627
336
  const lines = [];
628
337
 
629
338
  // Header Line
@@ -646,41 +355,28 @@ function generateStatusline() {
646
355
  `${c.brightYellow}⚑ 1.0x${c.reset} ${c.dim}β†’${c.reset} ${c.brightYellow}2.49x-7.47x${c.reset}`
647
356
  );
648
357
 
649
- // Line 2: Swarm + Hooks + CVE + Memory + Context + Intelligence
358
+ // Line 2: Swarm + CVE + Memory + Context + Intelligence
650
359
  const swarmIndicator = swarm.coordinationActive ? `${c.brightGreen}β—‰${c.reset}` : `${c.dim}β—‹${c.reset}`;
651
360
  const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
652
361
  let securityIcon = security.status === 'CLEAN' ? '🟒' : security.status === 'IN_PROGRESS' ? '🟑' : 'πŸ”΄';
653
362
  let securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
654
- const hooksColor = hooks.enabled > 0 ? c.brightGreen : c.dim;
655
363
 
656
364
  lines.push(
657
365
  `${c.brightYellow}πŸ€– Swarm${c.reset} ${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
658
366
  `${c.brightPurple}πŸ‘₯ ${system.subAgents}${c.reset} ` +
659
- `${c.brightBlue}πŸͺ ${hooksColor}${hooks.enabled}${c.reset}/${c.brightWhite}${hooks.total}${c.reset} ` +
660
367
  `${securityIcon} ${securityColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` +
661
368
  `${c.brightCyan}πŸ’Ύ ${system.memoryMB}MB${c.reset} ` +
369
+ `${c.brightGreen}πŸ“‚ ${String(system.contextPct).padStart(3)}%${c.reset} ` +
662
370
  `${c.dim}🧠 ${String(system.intelligencePct).padStart(3)}%${c.reset}`
663
371
  );
664
372
 
665
- // Line 3: Architecture status with ADRs, AgentDB, Tests
373
+ // Line 3: Architecture status
666
374
  const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
667
- const adrColor = adrs.count > 0 ? (adrs.implemented === adrs.count ? c.brightGreen : c.yellow) : c.dim;
668
- const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
669
- const testColor = tests.testFiles > 0 ? c.brightGreen : c.dim;
670
-
671
375
  lines.push(
672
376
  `${c.brightPurple}πŸ”§ Architecture${c.reset} ` +
673
- `${c.cyan}ADRs${c.reset} ${adrColor}●${adrs.implemented}/${adrs.count}${c.reset} ${c.dim}β”‚${c.reset} ` +
674
377
  `${c.cyan}DDD${c.reset} ${dddColor}●${String(progress.dddProgress).padStart(3)}%${c.reset} ${c.dim}β”‚${c.reset} ` +
675
- `${c.cyan}Security${c.reset} ${securityColor}●${security.status}${c.reset}`
676
- );
677
-
678
- // Line 4: Memory, Vectors, Tests
679
- lines.push(
680
- `${c.brightCyan}πŸ“Š AgentDB${c.reset} ` +
681
- `${c.cyan}Vectors${c.reset} ${vectorColor}●${agentdb.vectorCount}${c.reset} ${c.dim}β”‚${c.reset} ` +
682
- `${c.cyan}Size${c.reset} ${c.brightWhite}${agentdb.dbSizeKB}KB${c.reset} ${c.dim}β”‚${c.reset} ` +
683
- `${c.cyan}Tests${c.reset} ${testColor}●${tests.testFiles}${c.reset} ${c.dim}(${tests.testCases} cases)${c.reset} ${c.dim}β”‚${c.reset} ` +
378
+ `${c.cyan}Security${c.reset} ${securityColor}●${security.status}${c.reset} ${c.dim}β”‚${c.reset} ` +
379
+ `${c.cyan}Memory${c.reset} ${c.brightGreen}●AgentDB${c.reset} ${c.dim}β”‚${c.reset} ` +
684
380
  `${c.cyan}Integration${c.reset} ${swarm.coordinationActive ? c.brightCyan : c.dim}●${c.reset}`
685
381
  );
686
382
 
@@ -695,10 +391,6 @@ function generateJSON() {
695
391
  security: getSecurityStatus(),
696
392
  swarm: getSwarmStatus(),
697
393
  system: getSystemMetrics(),
698
- adrs: getADRStatus(),
699
- hooks: getHooksStatus(),
700
- agentdb: getAgentDBStats(),
701
- tests: getTestStats(),
702
394
  performance: {
703
395
  flashAttentionTarget: '2.49x-7.47x',
704
396
  searchImprovement: '150x-12,500x',
@@ -708,11 +400,110 @@ function generateJSON() {
708
400
  };
709
401
  }
710
402
 
403
+ /**
404
+ * Generate single-line output for Claude Code compatibility
405
+ * This avoids the collision zone issue entirely by using one line
406
+ * @see https://github.com/ruvnet/claude-flow/issues/985
407
+ */
408
+ function generateSingleLine() {
409
+ if (!CONFIG.enabled) return '';
410
+
411
+ const user = getUserInfo();
412
+ const progress = getV3Progress();
413
+ const security = getSecurityStatus();
414
+ const swarm = getSwarmStatus();
415
+ const system = getSystemMetrics();
416
+
417
+ const swarmIndicator = swarm.coordinationActive ? '●' : 'β—‹';
418
+ const securityStatus = security.status === 'CLEAN' ? 'βœ“' :
419
+ security.cvesFixed > 0 ? '~' : 'βœ—';
420
+
421
+ return `${c.brightPurple}CF-V3${c.reset} ${c.dim}|${c.reset} ` +
422
+ `${c.cyan}D:${progress.domainsCompleted}/${progress.totalDomains}${c.reset} ${c.dim}|${c.reset} ` +
423
+ `${c.yellow}S:${swarmIndicator}${swarm.activeAgents}/${swarm.maxAgents}${c.reset} ${c.dim}|${c.reset} ` +
424
+ `${security.status === 'CLEAN' ? c.green : c.red}CVE:${securityStatus}${security.cvesFixed}/${security.totalCves}${c.reset} ${c.dim}|${c.reset} ` +
425
+ `${c.dim}🧠${system.intelligencePct}%${c.reset}`;
426
+ }
427
+
428
+ /**
429
+ * Generate safe multi-line statusline that avoids Claude Code collision zone
430
+ * The collision zone is columns 15-25 on the second-to-last line.
431
+ * We pad that line with spaces to push content past column 25.
432
+ * @see https://github.com/ruvnet/claude-flow/issues/985
433
+ */
434
+ function generateSafeStatusline() {
435
+ if (!CONFIG.enabled) return '';
436
+
437
+ const user = getUserInfo();
438
+ const progress = getV3Progress();
439
+ const security = getSecurityStatus();
440
+ const swarm = getSwarmStatus();
441
+ const system = getSystemMetrics();
442
+ const lines = [];
443
+
444
+ // Header Line
445
+ let header = `${c.bold}${c.brightPurple}β–Š Claude Flow V3 ${c.reset}`;
446
+ header += `${swarm.coordinationActive ? c.brightCyan : c.dim}● ${c.brightCyan}${user.name}${c.reset}`;
447
+ if (user.gitBranch) {
448
+ header += ` ${c.dim}β”‚${c.reset} ${c.brightBlue}βŽ‡ ${user.gitBranch}${c.reset}`;
449
+ }
450
+ header += ` ${c.dim}β”‚${c.reset} ${c.purple}${user.modelName}${c.reset}`;
451
+ lines.push(header);
452
+
453
+ // Separator
454
+ lines.push(`${c.dim}─────────────────────────────────────────────────────${c.reset}`);
455
+
456
+ // Line 1: DDD Domain Progress
457
+ const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red;
458
+ lines.push(
459
+ `${c.brightCyan}πŸ—οΈ DDD Domains${c.reset} ${progressBar(progress.domainsCompleted, progress.totalDomains)} ` +
460
+ `${domainsColor}${progress.domainsCompleted}${c.reset}/${c.brightWhite}${progress.totalDomains}${c.reset} ` +
461
+ `${c.brightYellow}⚑ 1.0x${c.reset} ${c.dim}β†’${c.reset} ${c.brightYellow}2.49x-7.47x${c.reset}`
462
+ );
463
+
464
+ // Line 2 (COLLISION LINE): Swarm status with 24 spaces padding after emoji
465
+ // The emoji (πŸ€–) is 2 columns. 24 spaces pushes content to column 26, past the collision zone (15-25)
466
+ const swarmIndicator = swarm.coordinationActive ? `${c.brightGreen}β—‰${c.reset}` : `${c.dim}β—‹${c.reset}`;
467
+ const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
468
+ let securityIcon = security.status === 'CLEAN' ? '🟒' : security.status === 'IN_PROGRESS' ? '🟑' : 'πŸ”΄';
469
+ let securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
470
+
471
+ // CRITICAL: 24 spaces after πŸ€– (emoji is 2 cols, so 2+24=26, past collision zone cols 15-25)
472
+ lines.push(
473
+ `${c.brightYellow}πŸ€–${c.reset} ` + // 24 spaces padding
474
+ `${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
475
+ `${c.brightPurple}πŸ‘₯ ${system.subAgents}${c.reset} ` +
476
+ `${securityIcon} ${securityColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` +
477
+ `${c.brightCyan}πŸ’Ύ ${system.memoryMB}MB${c.reset} ` +
478
+ `${c.dim}🧠 ${system.intelligencePct}%${c.reset}`
479
+ );
480
+
481
+ // Line 3: Architecture status (this is the last line, not in collision zone)
482
+ const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
483
+ lines.push(
484
+ `${c.brightPurple}πŸ”§ Architecture${c.reset} ` +
485
+ `${c.cyan}DDD${c.reset} ${dddColor}●${String(progress.dddProgress).padStart(3)}%${c.reset} ${c.dim}β”‚${c.reset} ` +
486
+ `${c.cyan}Security${c.reset} ${securityColor}●${security.status}${c.reset} ${c.dim}β”‚${c.reset} ` +
487
+ `${c.cyan}Memory${c.reset} ${c.brightGreen}●AgentDB${c.reset} ${c.dim}β”‚${c.reset} ` +
488
+ `${c.cyan}Integration${c.reset} ${swarm.coordinationActive ? c.brightCyan : c.dim}●${c.reset}`
489
+ );
490
+
491
+ return lines.join('\n');
492
+ }
493
+
711
494
  // Main
712
495
  if (process.argv.includes('--json')) {
713
496
  console.log(JSON.stringify(generateJSON(), null, 2));
714
497
  } else if (process.argv.includes('--compact')) {
715
498
  console.log(JSON.stringify(generateJSON()));
716
- } else {
499
+ } else if (process.argv.includes('--single')) {
500
+ // Single-line mode - completely avoids collision zone
501
+ console.log(generateSingleLine());
502
+ } else if (process.argv.includes('--unsafe') || process.argv.includes('--legacy')) {
503
+ // Legacy mode - original multi-line without collision avoidance
717
504
  console.log(generateStatusline());
505
+ } else {
506
+ // Default: Safe multi-line mode with collision zone avoidance
507
+ // Use --unsafe or --legacy to get the original behavior
508
+ console.log(generateSafeStatusline());
718
509
  }