@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.
- package/.claude/helpers/statusline.cjs +171 -380
- package/README.md +401 -19
- package/dist/src/plugins/store/discovery.d.ts.map +1 -1
- package/dist/src/plugins/store/discovery.js +129 -11
- package/dist/src/plugins/store/discovery.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -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 [
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
//
|
|
85
|
-
//
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
239
|
-
if (fs.existsSync(
|
|
226
|
+
const auditPath = path.join(process.cwd(), '.swarm', 'security');
|
|
227
|
+
if (fs.existsSync(auditPath)) {
|
|
240
228
|
try {
|
|
241
|
-
const audits = fs.readdirSync(
|
|
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
|
|
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 /
|
|
285
|
-
|
|
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 -
|
|
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 (
|
|
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
|
|
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
|
-
|
|
336
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
subAgents =
|
|
367
|
-
}
|
|
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 +
|
|
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
|
|
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
|
}
|