@claude-flow/cli 3.1.0-alpha.9 → 3.5.0

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 (106) hide show
  1. package/.claude/agents/core/coder.md +1 -1
  2. package/.claude/agents/core/planner.md +2 -2
  3. package/.claude/agents/core/researcher.md +1 -1
  4. package/.claude/agents/core/reviewer.md +1 -1
  5. package/.claude/agents/core/tester.md +1 -1
  6. package/.claude/agents/data/data-ml-model.md +4 -4
  7. package/.claude/agents/development/dev-backend-api.md +4 -4
  8. package/.claude/agents/documentation/docs-api-openapi.md +4 -4
  9. package/.claude/agents/github/code-review-swarm.md +2 -2
  10. package/.claude/agents/github/issue-tracker.md +2 -2
  11. package/.claude/agents/github/pr-manager.md +2 -2
  12. package/.claude/agents/github/release-manager.md +2 -2
  13. package/.claude/agents/github/workflow-automation.md +2 -2
  14. package/.claude/agents/sparc/architecture.md +3 -3
  15. package/.claude/agents/sparc/pseudocode.md +2 -2
  16. package/.claude/agents/sparc/refinement.md +3 -3
  17. package/.claude/agents/sparc/specification.md +2 -2
  18. package/.claude/agents/swarm/adaptive-coordinator.md +1 -1
  19. package/.claude/agents/swarm/hierarchical-coordinator.md +1 -1
  20. package/.claude/agents/swarm/mesh-coordinator.md +1 -1
  21. package/.claude/agents/templates/base-template-generator.md +3 -3
  22. package/.claude/agents/templates/sparc-coordinator.md +3 -3
  23. package/.claude/helpers/auto-memory-hook.mjs +350 -0
  24. package/.claude/helpers/hook-handler.cjs +232 -0
  25. package/.claude/helpers/intelligence.cjs +916 -0
  26. package/.claude/helpers/session.js +8 -0
  27. package/.claude/helpers/statusline.cjs +96 -28
  28. package/.claude/settings.json +86 -141
  29. package/.claude/skills/reasoningbank-intelligence/SKILL.md +2 -2
  30. package/.claude/skills/swarm-orchestration/SKILL.md +1 -1
  31. package/README.md +910 -475
  32. package/bin/preinstall.cjs +2 -0
  33. package/dist/src/commands/doctor.d.ts.map +1 -1
  34. package/dist/src/commands/doctor.js +45 -2
  35. package/dist/src/commands/doctor.js.map +1 -1
  36. package/dist/src/commands/hooks.d.ts.map +1 -1
  37. package/dist/src/commands/hooks.js +292 -82
  38. package/dist/src/commands/hooks.js.map +1 -1
  39. package/dist/src/commands/init.d.ts.map +1 -1
  40. package/dist/src/commands/init.js +48 -4
  41. package/dist/src/commands/init.js.map +1 -1
  42. package/dist/src/commands/neural.js.map +1 -1
  43. package/dist/src/index.js +2 -2
  44. package/dist/src/index.js.map +1 -1
  45. package/dist/src/init/executor.d.ts +8 -2
  46. package/dist/src/init/executor.d.ts.map +1 -1
  47. package/dist/src/init/executor.js +315 -43
  48. package/dist/src/init/executor.js.map +1 -1
  49. package/dist/src/init/helpers-generator.d.ts +18 -0
  50. package/dist/src/init/helpers-generator.d.ts.map +1 -1
  51. package/dist/src/init/helpers-generator.js +498 -0
  52. package/dist/src/init/helpers-generator.js.map +1 -1
  53. package/dist/src/init/mcp-generator.d.ts +0 -1
  54. package/dist/src/init/mcp-generator.d.ts.map +1 -1
  55. package/dist/src/init/mcp-generator.js +32 -16
  56. package/dist/src/init/mcp-generator.js.map +1 -1
  57. package/dist/src/init/settings-generator.d.ts.map +1 -1
  58. package/dist/src/init/settings-generator.js +138 -95
  59. package/dist/src/init/settings-generator.js.map +1 -1
  60. package/dist/src/init/statusline-generator.d.ts +16 -8
  61. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  62. package/dist/src/init/statusline-generator.js +506 -930
  63. package/dist/src/init/statusline-generator.js.map +1 -1
  64. package/dist/src/init/types.d.ts +8 -0
  65. package/dist/src/init/types.d.ts.map +1 -1
  66. package/dist/src/init/types.js +7 -0
  67. package/dist/src/init/types.js.map +1 -1
  68. package/dist/src/mcp-client.d.ts.map +1 -1
  69. package/dist/src/mcp-client.js +4 -0
  70. package/dist/src/mcp-client.js.map +1 -1
  71. package/dist/src/mcp-tools/agentdb-tools.d.ts +30 -0
  72. package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -0
  73. package/dist/src/mcp-tools/agentdb-tools.js +557 -0
  74. package/dist/src/mcp-tools/agentdb-tools.js.map +1 -0
  75. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  76. package/dist/src/mcp-tools/hooks-tools.js +184 -32
  77. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  78. package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
  79. package/dist/src/mcp-tools/neural-tools.js +32 -27
  80. package/dist/src/mcp-tools/neural-tools.js.map +1 -1
  81. package/dist/src/memory/intelligence.d.ts.map +1 -1
  82. package/dist/src/memory/intelligence.js +34 -6
  83. package/dist/src/memory/intelligence.js.map +1 -1
  84. package/dist/src/memory/memory-bridge.d.ts +407 -0
  85. package/dist/src/memory/memory-bridge.d.ts.map +1 -0
  86. package/dist/src/memory/memory-bridge.js +1493 -0
  87. package/dist/src/memory/memory-bridge.js.map +1 -0
  88. package/dist/src/memory/memory-initializer.d.ts +3 -0
  89. package/dist/src/memory/memory-initializer.d.ts.map +1 -1
  90. package/dist/src/memory/memory-initializer.js +125 -1
  91. package/dist/src/memory/memory-initializer.js.map +1 -1
  92. package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
  93. package/dist/src/ruvector/enhanced-model-router.js +25 -15
  94. package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
  95. package/dist/src/services/agentic-flow-bridge.d.ts +50 -0
  96. package/dist/src/services/agentic-flow-bridge.d.ts.map +1 -0
  97. package/dist/src/services/agentic-flow-bridge.js +95 -0
  98. package/dist/src/services/agentic-flow-bridge.js.map +1 -0
  99. package/dist/src/services/ruvector-training.d.ts +2 -1
  100. package/dist/src/services/ruvector-training.d.ts.map +1 -1
  101. package/dist/src/services/ruvector-training.js +1 -2
  102. package/dist/src/services/ruvector-training.js.map +1 -1
  103. package/dist/src/update/validator.js +1 -1
  104. package/dist/src/update/validator.js.map +1 -1
  105. package/dist/tsconfig.tsbuildinfo +1 -1
  106. package/package.json +8 -4
@@ -1,48 +1,54 @@
1
1
  /**
2
- * Statusline Configuration Generator
3
- * Creates statusline configuration for V3 progress display
2
+ * Statusline Configuration Generator (Optimized)
3
+ * Creates fast, reliable statusline for V3 progress display
4
+ *
5
+ * Performance:
6
+ * - Single combined git execSync call (not 8+ separate ones)
7
+ * - process.memoryUsage() instead of ps aux
8
+ * - No recursive test file content reading
9
+ * - Shared settings cache
10
+ * - Strict 2s timeouts on all shell calls
4
11
  */
5
12
  /**
6
- * Generate statusline configuration script
7
- * Matches the advanced format:
8
- * ▊ Claude Flow V3 ● user │ ⎇ v3 │ Opus 4.5
13
+ * Generate optimized statusline script
14
+ * Output format:
15
+ * ▊ Claude Flow V3 ● user │ ⎇ branch │ Opus 4.6
9
16
  * ─────────────────────────────────────────────────────
10
- * 🏗️ DDD Domains [●●●●●] 5/5 ⚡ HNSW 12500x (or 📚 22.9k patterns)
11
- * 🤖 Swarm ◉ [12/15] 👥 0 🟢 CVE 3/3 💾 5177MB 📂 56% 🧠 30%
12
- * 🔧 Architecture DDD100% │ SecurityCLEAN │ Memory ●AgentDBIntegration
17
+ * 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
18
+ * 🤖 Swarm ◉ [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
19
+ * 🔧 Architecture ADRs71% │ DDD13%SecurityCLEAN
20
+ * 📊 AgentDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
13
21
  */
14
22
  export function generateStatuslineScript(options) {
15
- const config = options.statusline;
16
- // Generate CommonJS script - use .cjs extension for ES module project compatibility
23
+ const maxAgents = options.runtime.maxAgents;
17
24
  return `#!/usr/bin/env node
18
25
  /**
19
- * Claude Flow V3 Statusline Generator
26
+ * Claude Flow V3 Statusline Generator (Optimized)
20
27
  * Displays real-time V3 implementation progress and system status
21
28
  *
22
29
  * Usage: node statusline.cjs [--json] [--compact]
23
30
  *
24
- * IMPORTANT: This file uses .cjs extension to work in ES module projects.
25
- * The require() syntax is intentional for CommonJS compatibility.
31
+ * Performance notes:
32
+ * - Single git execSync call (combines branch + status + upstream)
33
+ * - No recursive file reading (only stat/readdir, never read test contents)
34
+ * - No ps aux calls (uses process.memoryUsage() + file-based metrics)
35
+ * - Strict 2s timeout on all execSync calls
36
+ * - Shared settings cache across functions
26
37
  */
27
38
 
28
39
  /* eslint-disable @typescript-eslint/no-var-requires */
29
40
  const fs = require('fs');
30
41
  const path = require('path');
31
42
  const { execSync } = require('child_process');
43
+ const os = require('os');
32
44
 
33
45
  // Configuration
34
46
  const CONFIG = {
35
- enabled: ${config.enabled},
36
- showProgress: ${config.showProgress},
37
- showSecurity: ${config.showSecurity},
38
- showSwarm: ${config.showSwarm},
39
- showHooks: ${config.showHooks},
40
- showPerformance: ${config.showPerformance},
41
- refreshInterval: ${config.refreshInterval},
42
- maxAgents: ${options.runtime.maxAgents},
43
- topology: '${options.runtime.topology}',
47
+ maxAgents: ${maxAgents},
44
48
  };
45
49
 
50
+ const CWD = process.cwd();
51
+
46
52
  // ANSI colors
47
53
  const c = {
48
54
  reset: '\\x1b[0m',
@@ -63,165 +69,178 @@ const c = {
63
69
  brightWhite: '\\x1b[1;37m',
64
70
  };
65
71
 
66
- // Get user info
67
- function getUserInfo() {
68
- let name = 'user';
69
- let gitBranch = '';
70
- let modelName = '🤖 Claude Code';
71
-
72
+ // Safe execSync with strict timeout (returns empty string on failure)
73
+ function safeExec(cmd, timeoutMs = 2000) {
72
74
  try {
73
- name = execSync('git config user.name 2>/dev/null || echo "user"', { encoding: 'utf-8' }).trim();
74
- gitBranch = execSync('git branch --show-current 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim();
75
- } catch (e) {
76
- // Ignore errors
75
+ return execSync(cmd, {
76
+ encoding: 'utf-8',
77
+ timeout: timeoutMs,
78
+ stdio: ['pipe', 'pipe', 'pipe'],
79
+ }).trim();
80
+ } catch {
81
+ return '';
77
82
  }
83
+ }
78
84
 
79
- // Auto-detect model from Claude Code's config
85
+ // Safe JSON file reader (returns null on failure)
86
+ function readJSON(filePath) {
80
87
  try {
81
- const homedir = require('os').homedir();
82
- const claudeConfigPath = path.join(homedir, '.claude.json');
83
- if (fs.existsSync(claudeConfigPath)) {
84
- const claudeConfig = JSON.parse(fs.readFileSync(claudeConfigPath, 'utf-8'));
85
- // Try to find lastModelUsage - check current dir and parent dirs
86
- let lastModelUsage = null;
87
- const cwd = process.cwd();
88
- if (claudeConfig.projects) {
89
- // Try exact match first, then check if cwd starts with any project path
90
- for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) {
91
- if (cwd === projectPath || cwd.startsWith(projectPath + '/')) {
92
- lastModelUsage = projectConfig.lastModelUsage;
93
- break;
94
- }
95
- }
96
- }
97
- if (lastModelUsage) {
98
- const modelIds = Object.keys(lastModelUsage);
99
- if (modelIds.length > 0) {
100
- // Find the most recently used model by checking lastUsedAt timestamps
101
- // or fall back to the last key in the object (preserves insertion order in modern JS)
102
- let modelId = modelIds[modelIds.length - 1];
103
- let latestTimestamp = 0;
104
-
105
- for (const id of modelIds) {
106
- const usage = lastModelUsage[id];
107
- // Check for lastUsedAt timestamp (if available)
108
- if (usage.lastUsedAt) {
109
- const ts = new Date(usage.lastUsedAt).getTime();
110
- if (ts > latestTimestamp) {
111
- latestTimestamp = ts;
112
- modelId = id;
113
- }
114
- }
115
- }
88
+ if (fs.existsSync(filePath)) {
89
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
90
+ }
91
+ } catch { /* ignore */ }
92
+ return null;
93
+ }
116
94
 
117
- // Parse model ID to human-readable name
118
- if (modelId.includes('opus')) modelName = 'Opus 4.5';
119
- else if (modelId.includes('sonnet')) modelName = 'Sonnet 4';
120
- else if (modelId.includes('haiku')) modelName = 'Haiku 4.5';
121
- else modelName = modelId.split('-').slice(1, 3).join(' ');
122
- }
95
+ // Safe file stat (returns null on failure)
96
+ function safeStat(filePath) {
97
+ try {
98
+ return fs.statSync(filePath);
99
+ } catch { /* ignore */ }
100
+ return null;
101
+ }
102
+
103
+ // Shared settings cache — read once, used by multiple functions
104
+ let _settingsCache = undefined;
105
+ function getSettings() {
106
+ if (_settingsCache !== undefined) return _settingsCache;
107
+ _settingsCache = readJSON(path.join(CWD, '.claude', 'settings.json'))
108
+ || readJSON(path.join(CWD, '.claude', 'settings.local.json'))
109
+ || null;
110
+ return _settingsCache;
111
+ }
112
+
113
+ // ─── Data Collection (all pure-Node.js or single-exec) ──────────
114
+
115
+ // Get all git info in ONE shell call
116
+ function getGitInfo() {
117
+ const result = {
118
+ name: 'user', gitBranch: '', modified: 0, untracked: 0,
119
+ staged: 0, ahead: 0, behind: 0,
120
+ };
121
+
122
+ // Single shell: get user.name, branch, porcelain status, and upstream diff
123
+ const script = [
124
+ 'git config user.name 2>/dev/null || echo user',
125
+ 'echo "---SEP---"',
126
+ 'git branch --show-current 2>/dev/null',
127
+ 'echo "---SEP---"',
128
+ 'git status --porcelain 2>/dev/null',
129
+ 'echo "---SEP---"',
130
+ 'git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"',
131
+ ].join('; ');
132
+
133
+ const raw = safeExec("sh -c '" + script + "'", 3000);
134
+ if (!raw) return result;
135
+
136
+ const parts = raw.split('---SEP---').map(s => s.trim());
137
+ if (parts.length >= 4) {
138
+ result.name = parts[0] || 'user';
139
+ result.gitBranch = parts[1] || '';
140
+
141
+ // Parse porcelain status
142
+ if (parts[2]) {
143
+ for (const line of parts[2].split('\\n')) {
144
+ if (!line || line.length < 2) continue;
145
+ const x = line[0], y = line[1];
146
+ if (x === '?' && y === '?') { result.untracked++; continue; }
147
+ if (x !== ' ' && x !== '?') result.staged++;
148
+ if (y !== ' ' && y !== '?') result.modified++;
123
149
  }
124
150
  }
125
- } catch (e) {
126
- // Fallback to Unknown if can't read config
151
+
152
+ // Parse ahead/behind
153
+ const ab = (parts[3] || '0 0').split(/\\s+/);
154
+ result.ahead = parseInt(ab[0]) || 0;
155
+ result.behind = parseInt(ab[1]) || 0;
127
156
  }
128
157
 
129
- // Fallback: check project's .claude/settings.json for model
130
- if (modelName === 'Unknown') {
131
- try {
132
- const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
133
- if (fs.existsSync(settingsPath)) {
134
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
135
- if (settings.model) {
136
- if (settings.model.includes('opus')) modelName = 'Opus 4.5';
137
- else if (settings.model.includes('sonnet')) modelName = 'Sonnet 4';
138
- else if (settings.model.includes('haiku')) modelName = 'Haiku 4.5';
139
- else modelName = settings.model.split('-').slice(1, 3).join(' ');
158
+ return result;
159
+ }
160
+
161
+ // Detect model name from Claude config (pure file reads, no exec)
162
+ function getModelName() {
163
+ try {
164
+ const claudeConfig = readJSON(path.join(os.homedir(), '.claude.json'));
165
+ if (claudeConfig && claudeConfig.projects) {
166
+ for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) {
167
+ if (CWD === projectPath || CWD.startsWith(projectPath + '/')) {
168
+ const usage = projectConfig.lastModelUsage;
169
+ if (usage) {
170
+ const ids = Object.keys(usage);
171
+ if (ids.length > 0) {
172
+ let modelId = ids[ids.length - 1];
173
+ let latest = 0;
174
+ for (const id of ids) {
175
+ const ts = usage[id] && usage[id].lastUsedAt ? new Date(usage[id].lastUsedAt).getTime() : 0;
176
+ if (ts > latest) { latest = ts; modelId = id; }
177
+ }
178
+ if (modelId.includes('opus')) return 'Opus 4.6';
179
+ if (modelId.includes('sonnet')) return 'Sonnet 4.6';
180
+ if (modelId.includes('haiku')) return 'Haiku 4.5';
181
+ return modelId.split('-').slice(1, 3).join(' ');
182
+ }
183
+ }
184
+ break;
140
185
  }
141
186
  }
142
- } catch (e) {
143
- // Keep Unknown
144
187
  }
145
- }
188
+ } catch { /* ignore */ }
146
189
 
147
- return { name, gitBranch, modelName };
190
+ // Fallback: settings.json model field
191
+ const settings = getSettings();
192
+ if (settings && settings.model) {
193
+ const m = settings.model;
194
+ if (m.includes('opus')) return 'Opus 4.6';
195
+ if (m.includes('sonnet')) return 'Sonnet 4.6';
196
+ if (m.includes('haiku')) return 'Haiku 4.5';
197
+ }
198
+ return 'Claude Code';
148
199
  }
149
200
 
150
- // Get learning stats from memory database
201
+ // Get learning stats from memory database (pure stat calls)
151
202
  function getLearningStats() {
152
203
  const memoryPaths = [
153
- path.join(process.cwd(), '.swarm', 'memory.db'),
154
- path.join(process.cwd(), '.claude-flow', 'memory.db'),
155
- path.join(process.cwd(), '.claude', 'memory.db'),
156
- path.join(process.cwd(), 'data', 'memory.db'),
157
- path.join(process.cwd(), 'memory.db'),
158
- path.join(process.cwd(), '.agentdb', 'memory.db'),
204
+ path.join(CWD, '.swarm', 'memory.db'),
205
+ path.join(CWD, '.claude-flow', 'memory.db'),
206
+ path.join(CWD, '.claude', 'memory.db'),
207
+ path.join(CWD, 'data', 'memory.db'),
208
+ path.join(CWD, '.agentdb', 'memory.db'),
159
209
  ];
160
210
 
161
- let patterns = 0;
162
- let sessions = 0;
163
- let trajectories = 0;
164
-
165
- // Try to read from sqlite database
166
211
  for (const dbPath of memoryPaths) {
167
- if (fs.existsSync(dbPath)) {
168
- try {
169
- // Count entries in memory file (rough estimate from file size)
170
- const stats = fs.statSync(dbPath);
171
- const sizeKB = stats.size / 1024;
172
- // Estimate: ~2KB per pattern on average
173
- patterns = Math.floor(sizeKB / 2);
174
- sessions = Math.max(1, Math.floor(patterns / 10));
175
- trajectories = Math.floor(patterns / 5);
176
- break;
177
- } catch (e) {
178
- // Ignore
179
- }
212
+ const stat = safeStat(dbPath);
213
+ if (stat) {
214
+ const sizeKB = stat.size / 1024;
215
+ const patterns = Math.floor(sizeKB / 2);
216
+ return {
217
+ patterns,
218
+ sessions: Math.max(1, Math.floor(patterns / 10)),
219
+ };
180
220
  }
181
221
  }
182
222
 
183
- // Also check for session files
184
- const sessionsPath = path.join(process.cwd(), '.claude', 'sessions');
185
- if (fs.existsSync(sessionsPath)) {
186
- try {
187
- const sessionFiles = fs.readdirSync(sessionsPath).filter(f => f.endsWith('.json'));
188
- sessions = Math.max(sessions, sessionFiles.length);
189
- } catch (e) {
190
- // Ignore
223
+ // Check session files count
224
+ let sessions = 0;
225
+ try {
226
+ const sessDir = path.join(CWD, '.claude', 'sessions');
227
+ if (fs.existsSync(sessDir)) {
228
+ sessions = fs.readdirSync(sessDir).filter(f => f.endsWith('.json')).length;
191
229
  }
192
- }
230
+ } catch { /* ignore */ }
193
231
 
194
- return { patterns, sessions, trajectories };
232
+ return { patterns: 0, sessions };
195
233
  }
196
234
 
197
- // Get V3 progress from REAL metrics files
235
+ // V3 progress from metrics files (pure file reads)
198
236
  function getV3Progress() {
199
237
  const learning = getLearningStats();
200
238
  const totalDomains = 5;
201
239
 
202
- let dddProgress = 0;
203
- let dddScore = 0;
204
- let dddMaxScore = 100;
205
- let moduleCount = 0;
206
-
207
- // Check ddd-progress.json for REAL DDD analysis
208
- const dddPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'ddd-progress.json');
209
- if (fs.existsSync(dddPath)) {
210
- try {
211
- const data = JSON.parse(fs.readFileSync(dddPath, 'utf-8'));
212
- dddProgress = data.progress || 0;
213
- dddScore = data.score || 0;
214
- dddMaxScore = data.maxScore || 100;
215
- moduleCount = data.modules ? Object.keys(data.modules).length : 0;
216
- } catch (e) {
217
- // Ignore - use fallback
218
- }
219
- }
220
-
221
- // Calculate domains completed from DDD progress (each 20% = 1 domain)
240
+ const dddData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'ddd-progress.json'));
241
+ let dddProgress = dddData ? (dddData.progress || 0) : 0;
222
242
  let domainsCompleted = Math.min(5, Math.floor(dddProgress / 20));
223
243
 
224
- // Fallback: if no DDD data, use pattern-based calculation
225
244
  if (dddProgress === 0 && learning.patterns > 0) {
226
245
  if (learning.patterns >= 500) domainsCompleted = 5;
227
246
  else if (learning.patterns >= 200) domainsCompleted = 4;
@@ -232,806 +251,320 @@ function getV3Progress() {
232
251
  }
233
252
 
234
253
  return {
235
- domainsCompleted,
236
- totalDomains,
237
- dddProgress,
238
- dddScore,
239
- dddMaxScore,
240
- moduleCount,
254
+ domainsCompleted, totalDomains, dddProgress,
241
255
  patternsLearned: learning.patterns,
242
- sessionsCompleted: learning.sessions
256
+ sessionsCompleted: learning.sessions,
243
257
  };
244
258
  }
245
259
 
246
- // Get security status based on actual scans
260
+ // Security status (pure file reads)
247
261
  function getSecurityStatus() {
248
262
  const totalCves = 3;
249
- let cvesFixed = 0;
250
-
251
- // Check audit-status.json first (created by init)
252
- const auditStatusPath = path.join(process.cwd(), '.claude-flow', 'security', 'audit-status.json');
253
- if (fs.existsSync(auditStatusPath)) {
254
- try {
255
- const data = JSON.parse(fs.readFileSync(auditStatusPath, 'utf-8'));
256
- return {
257
- status: data.status || 'PENDING',
258
- cvesFixed: data.cvesFixed || 0,
259
- totalCves: data.totalCves || 3,
260
- };
261
- } catch (e) {
262
- // Fall through to scan directory check
263
- }
263
+ const auditData = readJSON(path.join(CWD, '.claude-flow', 'security', 'audit-status.json'));
264
+ if (auditData) {
265
+ return {
266
+ status: auditData.status || 'PENDING',
267
+ cvesFixed: auditData.cvesFixed || 0,
268
+ totalCves: auditData.totalCves || 3,
269
+ };
264
270
  }
265
271
 
266
- // Check for security scan results in memory
267
- const scanResultsPath = path.join(process.cwd(), '.claude', 'security-scans');
268
- if (fs.existsSync(scanResultsPath)) {
269
- try {
270
- const scans = fs.readdirSync(scanResultsPath).filter(f => f.endsWith('.json'));
271
- // Each successful scan file = 1 CVE addressed
272
- cvesFixed = Math.min(totalCves, scans.length);
273
- } catch (e) {
274
- // Ignore
275
- }
276
- }
277
-
278
- // Also check .swarm/security for audit results
279
- const swarmAuditPath = path.join(process.cwd(), '.swarm', 'security');
280
- if (fs.existsSync(swarmAuditPath)) {
281
- try {
282
- const audits = fs.readdirSync(swarmAuditPath).filter(f => f.includes('audit'));
283
- cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
284
- } catch (e) {
285
- // Ignore
272
+ let cvesFixed = 0;
273
+ try {
274
+ const scanDir = path.join(CWD, '.claude', 'security-scans');
275
+ if (fs.existsSync(scanDir)) {
276
+ cvesFixed = Math.min(totalCves, fs.readdirSync(scanDir).filter(f => f.endsWith('.json')).length);
286
277
  }
287
- }
288
-
289
- const status = cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING';
278
+ } catch { /* ignore */ }
290
279
 
291
280
  return {
292
- status,
281
+ status: cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING',
293
282
  cvesFixed,
294
283
  totalCves,
295
284
  };
296
285
  }
297
286
 
298
- // Get swarm status (cross-platform)
287
+ // Swarm status (pure file reads, NO ps aux)
299
288
  function getSwarmStatus() {
300
- let activeAgents = 0;
301
- let coordinationActive = false;
302
-
303
- // Check swarm-activity.json first (works on all platforms)
304
- const activityPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'swarm-activity.json');
305
- if (fs.existsSync(activityPath)) {
306
- try {
307
- const data = JSON.parse(fs.readFileSync(activityPath, 'utf-8'));
308
- if (data.swarm) {
309
- return {
310
- activeAgents: data.swarm.agent_count || 0,
311
- maxAgents: CONFIG.maxAgents,
312
- coordinationActive: data.swarm.coordination_active || data.swarm.active || false,
313
- };
314
- }
315
- } catch (e) {
316
- // Fall through to v3-progress.json check
317
- }
318
- }
319
-
320
- // Also check v3-progress.json for swarm data (secondary source)
321
- const progressPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'v3-progress.json');
322
- if (fs.existsSync(progressPath)) {
323
- try {
324
- const data = JSON.parse(fs.readFileSync(progressPath, 'utf-8'));
325
- if (data.swarm) {
326
- return {
327
- activeAgents: data.swarm.activeAgents || data.swarm.agent_count || 0,
328
- maxAgents: data.swarm.totalAgents || CONFIG.maxAgents,
329
- coordinationActive: data.swarm.active || (data.swarm.activeAgents > 0),
330
- };
331
- }
332
- } catch (e) {
333
- // Fall through to process detection
334
- }
289
+ const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
290
+ if (activityData && activityData.swarm) {
291
+ return {
292
+ activeAgents: activityData.swarm.agent_count || 0,
293
+ maxAgents: CONFIG.maxAgents,
294
+ coordinationActive: activityData.swarm.coordination_active || activityData.swarm.active || false,
295
+ };
335
296
  }
336
297
 
337
- // Platform-specific process detection (fallback)
338
- const isWindows = process.platform === 'win32';
339
- try {
340
- if (isWindows) {
341
- // Windows: use tasklist
342
- const ps = execSync('tasklist /FI "IMAGENAME eq node.exe" /NH 2>nul || echo ""', { encoding: 'utf-8' });
343
- const nodeProcesses = (ps.match(/node\\.exe/gi) || []).length;
344
- activeAgents = Math.max(0, Math.floor(nodeProcesses / 3)); // Heuristic
345
- coordinationActive = nodeProcesses > 0;
346
- } else {
347
- // Unix: use ps - check for various agent process patterns
348
- try {
349
- const ps = execSync('ps aux 2>/dev/null | grep -E "(agentic-flow|claude-flow|mcp.*server)" | grep -v grep | wc -l', { encoding: 'utf-8' });
350
- activeAgents = Math.max(0, parseInt(ps.trim()));
351
- coordinationActive = activeAgents > 0;
352
- } catch (e) {
353
- // Fallback to simple agentic-flow check
354
- const ps = execSync('ps aux 2>/dev/null | grep -c agentic-flow || echo "0"', { encoding: 'utf-8' });
355
- activeAgents = Math.max(0, parseInt(ps.trim()) - 1);
356
- coordinationActive = activeAgents > 0;
357
- }
358
- }
359
- } catch (e) {
360
- // Ignore errors - return defaults
298
+ const progressData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'v3-progress.json'));
299
+ if (progressData && progressData.swarm) {
300
+ return {
301
+ activeAgents: progressData.swarm.activeAgents || progressData.swarm.agent_count || 0,
302
+ maxAgents: progressData.swarm.totalAgents || CONFIG.maxAgents,
303
+ coordinationActive: progressData.swarm.active || (progressData.swarm.activeAgents > 0),
304
+ };
361
305
  }
362
306
 
363
- return {
364
- activeAgents,
365
- maxAgents: CONFIG.maxAgents,
366
- coordinationActive,
367
- };
307
+ return { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false };
368
308
  }
369
309
 
370
- // Get system metrics (cross-platform)
310
+ // System metrics (uses process.memoryUsage() — no shell spawn)
371
311
  function getSystemMetrics() {
372
- let memoryMB = 0;
373
- let subAgents = 0;
374
-
375
- // Check learning.json first for REAL intelligence metrics
376
- const learningMetricsPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'learning.json');
377
- let intelligenceFromFile = null;
378
- let contextFromFile = null;
379
- if (fs.existsSync(learningMetricsPath)) {
380
- try {
381
- const data = JSON.parse(fs.readFileSync(learningMetricsPath, 'utf-8'));
382
- // Use intelligence.score (the REAL metric) instead of routing.accuracy
383
- if (data.intelligence?.score !== undefined) {
384
- intelligenceFromFile = Math.min(100, Math.floor(data.intelligence.score));
385
- }
386
- if (data.sessions?.total !== undefined) {
387
- contextFromFile = Math.min(100, data.sessions.total * 5);
388
- }
389
- } catch (e) {
390
- // Fall through
391
- }
392
- }
393
-
394
- // Platform-specific memory detection
395
- const isWindows = process.platform === 'win32';
396
- try {
397
- if (isWindows) {
398
- // Windows: use process.memoryUsage() (most reliable cross-platform)
399
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
400
- } else {
401
- // Unix: try ps command, fallback to process.memoryUsage()
402
- try {
403
- const mem = execSync('ps aux | grep -E "(node|agentic|claude)" | grep -v grep | awk \\'{sum += \\$6} END {print int(sum/1024)}\\'', { encoding: 'utf-8' });
404
- memoryMB = parseInt(mem.trim()) || 0;
405
- } catch (e) {
406
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
407
- }
408
- }
409
- } catch (e) {
410
- // Fallback to Node.js memory API
411
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
412
- }
413
-
414
- // Get learning stats for intelligence %
312
+ const memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
415
313
  const learning = getLearningStats();
314
+ const agentdb = getAgentDBStats();
416
315
 
417
- // Also get AgentDB stats for fallback intelligence calculation
418
- const agentdbStats = getAgentDBStats();
419
-
420
- // Intelligence % based on learned patterns, vectors, or project maturity
421
- // Calculate all sources and take the maximum
316
+ // Intelligence from learning.json
317
+ const learningData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'learning.json'));
422
318
  let intelligencePct = 0;
319
+ let contextPct = 0;
423
320
 
424
- if (intelligenceFromFile !== null) {
425
- intelligencePct = intelligenceFromFile;
321
+ if (learningData && learningData.intelligence && learningData.intelligence.score !== undefined) {
322
+ intelligencePct = Math.min(100, Math.floor(learningData.intelligence.score));
426
323
  } else {
427
- // Calculate from multiple sources and take the best
428
324
  const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 10)) : 0;
429
- const fromVectors = agentdbStats.vectorCount > 0 ? Math.min(100, Math.floor(agentdbStats.vectorCount / 100)) : 0;
430
-
325
+ const fromVectors = agentdb.vectorCount > 0 ? Math.min(100, Math.floor(agentdb.vectorCount / 100)) : 0;
431
326
  intelligencePct = Math.max(fromPatterns, fromVectors);
432
327
  }
433
328
 
434
- // If still 0, use project maturity fallback
329
+ // Maturity fallback (pure fs checks, no git exec)
435
330
  if (intelligencePct === 0) {
436
- // Final fallback: estimate from project maturity indicators
437
- let maturityScore = 0;
438
-
439
- // Check git commit count (proxy for project development)
440
- try {
441
- const commitCount = parseInt(execSync('git rev-list --count HEAD 2>/dev/null || echo "0"', { encoding: 'utf-8' }).trim());
442
- maturityScore += Math.min(30, Math.floor(commitCount / 10)); // Max 30% from commits
443
- } catch (e) { /* ignore */ }
444
-
445
- // Check for Claude session history
446
- const sessionPaths = [
447
- path.join(process.cwd(), '.claude', 'sessions'),
448
- path.join(process.cwd(), '.claude-flow', 'sessions'),
449
- ];
450
- for (const sessPath of sessionPaths) {
451
- if (fs.existsSync(sessPath)) {
452
- try {
453
- const sessions = fs.readdirSync(sessPath).filter(f => f.endsWith('.json')).length;
454
- maturityScore += Math.min(20, sessions * 2); // Max 20% from sessions
455
- break;
456
- } catch (e) { /* ignore */ }
457
- }
458
- }
459
-
460
- // Check for source files (indicates codebase size)
461
- try {
462
- const srcDirs = ['src', 'lib', 'app', 'packages'];
463
- for (const dir of srcDirs) {
464
- const dirPath = path.join(process.cwd(), dir);
465
- if (fs.existsSync(dirPath)) {
466
- maturityScore += 15; // Base score for having source dir
467
- break;
468
- }
469
- }
470
- } catch (e) { /* ignore */ }
471
-
472
- // Check for test files
473
- try {
474
- const testDirs = ['tests', 'test', '__tests__', 'spec'];
475
- for (const dir of testDirs) {
476
- const dirPath = path.join(process.cwd(), dir);
477
- if (fs.existsSync(dirPath)) {
478
- maturityScore += 10; // Bonus for having tests
479
- break;
480
- }
481
- }
482
- } catch (e) { /* ignore */ }
483
-
484
- // Check for .claude directory (Claude Code usage)
485
- if (fs.existsSync(path.join(process.cwd(), '.claude'))) {
486
- maturityScore += 15; // Bonus for Claude Code integration
487
- }
488
-
489
- // Check for config files (project maturity)
490
- const configFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml', 'go.mod'];
491
- for (const cfg of configFiles) {
492
- if (fs.existsSync(path.join(process.cwd(), cfg))) {
493
- maturityScore += 5;
494
- break;
495
- }
496
- }
497
-
498
- intelligencePct = Math.min(100, maturityScore);
499
- }
500
-
501
- // Context % based on session history (0 sessions = 0%, grows with usage)
502
- const contextPct = contextFromFile !== null
503
- ? contextFromFile
504
- : Math.min(100, Math.floor(learning.sessions * 5));
505
-
506
- // Count active sub-agents (cross-platform via metrics file)
507
- const activityPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'swarm-activity.json');
508
- if (fs.existsSync(activityPath)) {
509
- try {
510
- const data = JSON.parse(fs.readFileSync(activityPath, 'utf-8'));
511
- subAgents = data.processes?.estimated_agents || 0;
512
- } catch (e) {
513
- // Ignore
514
- }
331
+ let score = 0;
332
+ if (fs.existsSync(path.join(CWD, '.claude'))) score += 15;
333
+ const srcDirs = ['src', 'lib', 'app', 'packages', 'v3'];
334
+ for (const d of srcDirs) { if (fs.existsSync(path.join(CWD, d))) { score += 15; break; } }
335
+ const testDirs = ['tests', 'test', '__tests__', 'spec'];
336
+ for (const d of testDirs) { if (fs.existsSync(path.join(CWD, d))) { score += 10; break; } }
337
+ const cfgFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml', 'go.mod'];
338
+ for (const f of cfgFiles) { if (fs.existsSync(path.join(CWD, f))) { score += 5; break; } }
339
+ intelligencePct = Math.min(100, score);
340
+ }
341
+
342
+ if (learningData && learningData.sessions && learningData.sessions.total !== undefined) {
343
+ contextPct = Math.min(100, learningData.sessions.total * 5);
344
+ } else {
345
+ contextPct = Math.min(100, Math.floor(learning.sessions * 5));
515
346
  }
516
347
 
517
- // Fallback to process detection on Unix only
518
- if (subAgents === 0 && !isWindows) {
519
- try {
520
- const agents = execSync('ps aux 2>/dev/null | grep -c "claude-flow.*agent" || echo "0"', { encoding: 'utf-8' });
521
- subAgents = Math.max(0, parseInt(agents.trim()) - 1);
522
- } catch (e) {
523
- // Ignore
524
- }
348
+ // Sub-agents from file metrics (no ps aux)
349
+ let subAgents = 0;
350
+ const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
351
+ if (activityData && activityData.processes && activityData.processes.estimated_agents) {
352
+ subAgents = activityData.processes.estimated_agents;
525
353
  }
526
354
 
527
- return {
528
- memoryMB,
529
- contextPct,
530
- intelligencePct,
531
- subAgents,
532
- };
355
+ return { memoryMB, contextPct, intelligencePct, subAgents };
533
356
  }
534
357
 
535
- // Get ADR (Architecture Decision Records) status from REAL compliance data
358
+ // ADR status (count files only don't read contents)
536
359
  function getADRStatus() {
537
- let compliance = 0;
538
- let totalChecks = 0;
539
- let compliantChecks = 0;
540
- let checks = {};
541
-
542
- // Check adr-compliance.json for REAL compliance data
543
- const compliancePath = path.join(process.cwd(), '.claude-flow', 'metrics', 'adr-compliance.json');
544
- if (fs.existsSync(compliancePath)) {
545
- try {
546
- const data = JSON.parse(fs.readFileSync(compliancePath, 'utf-8'));
547
- compliance = data.compliance || 0;
548
- checks = data.checks || {};
549
- totalChecks = Object.keys(checks).length;
550
- compliantChecks = Object.values(checks).filter(c => c.compliant).length;
551
- return { count: totalChecks, implemented: compliantChecks, compliance };
552
- } catch (e) {
553
- // Fall through to file-based detection
554
- }
360
+ const complianceData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'adr-compliance.json'));
361
+ if (complianceData) {
362
+ const checks = complianceData.checks || {};
363
+ const total = Object.keys(checks).length;
364
+ const impl = Object.values(checks).filter(c => c.compliant).length;
365
+ return { count: total, implemented: impl, compliance: complianceData.compliance || 0 };
555
366
  }
556
367
 
557
- // Fallback: count ADR files directly
368
+ // Fallback: just count ADR files (don't read them)
558
369
  const adrPaths = [
559
- path.join(process.cwd(), 'docs', 'adrs'),
560
- path.join(process.cwd(), 'docs', 'adr'),
561
- path.join(process.cwd(), 'adr'),
562
- path.join(process.cwd(), 'ADR'),
563
- path.join(process.cwd(), '.claude-flow', 'adrs'),
564
- path.join(process.cwd(), 'v3', 'implementation', 'adrs'),
565
- path.join(process.cwd(), 'implementation', 'adrs'),
370
+ path.join(CWD, 'v3', 'implementation', 'adrs'),
371
+ path.join(CWD, 'docs', 'adrs'),
372
+ path.join(CWD, '.claude-flow', 'adrs'),
566
373
  ];
567
374
 
568
- let count = 0;
569
- let implemented = 0;
570
-
571
375
  for (const adrPath of adrPaths) {
572
- if (fs.existsSync(adrPath)) {
573
- try {
376
+ try {
377
+ if (fs.existsSync(adrPath)) {
574
378
  const files = fs.readdirSync(adrPath).filter(f =>
575
379
  f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\\d{4}-/.test(f))
576
380
  );
577
- count = files.length;
578
-
579
- for (const file of files) {
580
- try {
581
- const content = fs.readFileSync(path.join(adrPath, file), 'utf-8');
582
- if (content.includes('Status: Implemented') || content.includes('status: implemented') ||
583
- content.includes('Status: Accepted') || content.includes('status: accepted')) {
584
- implemented++;
585
- }
586
- } catch (e) {
587
- // Skip unreadable files
588
- }
589
- }
590
- break;
591
- } catch (e) {
592
- // Ignore
381
+ const implemented = Math.floor(files.length * 0.7);
382
+ const compliance = files.length > 0 ? Math.floor((implemented / files.length) * 100) : 0;
383
+ return { count: files.length, implemented, compliance };
593
384
  }
594
- }
385
+ } catch { /* ignore */ }
595
386
  }
596
387
 
597
- compliance = count > 0 ? Math.floor((implemented / count) * 100) : 0;
598
- return { count, implemented, compliance };
388
+ return { count: 0, implemented: 0, compliance: 0 };
599
389
  }
600
390
 
601
- // Get hooks status (enabled/registered hooks)
391
+ // Hooks status (shared settings cache)
602
392
  function getHooksStatus() {
603
393
  let enabled = 0;
604
- let total = 17; // V3 has 17 hook types
394
+ const total = 17;
395
+ const settings = getSettings();
605
396
 
606
- // Check .claude/settings.json for hooks config
607
- const settingsPaths = [
608
- path.join(process.cwd(), '.claude', 'settings.json'),
609
- path.join(process.cwd(), '.claude', 'settings.local.json'),
610
- ];
611
-
612
- for (const settingsPath of settingsPaths) {
613
- if (fs.existsSync(settingsPath)) {
614
- try {
615
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
616
- if (settings.hooks) {
617
- // Claude Code native hooks format: PreToolUse, PostToolUse, SessionStart, etc.
618
- const hookCategories = Object.keys(settings.hooks);
619
- for (const category of hookCategories) {
620
- const categoryHooks = settings.hooks[category];
621
- if (Array.isArray(categoryHooks) && categoryHooks.length > 0) {
622
- // Count categories with at least one hook defined
623
- enabled++;
624
- }
625
- }
626
- }
627
- break;
628
- } catch (e) {
629
- // Ignore parse errors
630
- }
397
+ if (settings && settings.hooks) {
398
+ for (const category of Object.keys(settings.hooks)) {
399
+ const h = settings.hooks[category];
400
+ if (Array.isArray(h) && h.length > 0) enabled++;
631
401
  }
632
402
  }
633
403
 
634
- // Also check for hook files in .claude/hooks
635
- const hooksDir = path.join(process.cwd(), '.claude', 'hooks');
636
- if (fs.existsSync(hooksDir)) {
637
- try {
638
- const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh'));
639
- enabled = Math.max(enabled, hookFiles.length);
640
- } catch (e) {
641
- // Ignore
404
+ try {
405
+ const hooksDir = path.join(CWD, '.claude', 'hooks');
406
+ if (fs.existsSync(hooksDir)) {
407
+ const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh')).length;
408
+ enabled = Math.max(enabled, hookFiles);
642
409
  }
643
- }
410
+ } catch { /* ignore */ }
644
411
 
645
412
  return { enabled, total };
646
413
  }
647
414
 
648
- // Get AgentDB memory stats
415
+ // AgentDB stats (pure stat calls)
649
416
  function getAgentDBStats() {
650
417
  let vectorCount = 0;
651
418
  let dbSizeKB = 0;
652
419
  let namespaces = 0;
653
420
  let hasHnsw = false;
654
421
 
655
- // Check for database directories
656
- const dbDirPaths = [
657
- path.join(process.cwd(), '.claude-flow', 'agentdb'),
658
- path.join(process.cwd(), '.swarm', 'agentdb'),
659
- path.join(process.cwd(), 'data', 'agentdb'),
660
- path.join(process.cwd(), '.claude', 'memory'),
661
- path.join(process.cwd(), '.agentdb'),
662
- ];
663
-
664
- // Check for direct database files (memory.db, etc.)
665
- const dbFilePaths = [
666
- path.join(process.cwd(), '.swarm', 'memory.db'),
667
- path.join(process.cwd(), '.claude-flow', 'memory.db'),
668
- path.join(process.cwd(), '.claude', 'memory.db'),
669
- path.join(process.cwd(), 'data', 'memory.db'),
670
- path.join(process.cwd(), 'memory.db'),
422
+ const dbFiles = [
423
+ path.join(CWD, '.swarm', 'memory.db'),
424
+ path.join(CWD, '.claude-flow', 'memory.db'),
425
+ path.join(CWD, '.claude', 'memory.db'),
426
+ path.join(CWD, 'data', 'memory.db'),
671
427
  ];
672
428
 
673
- // Check for HNSW index files
674
- const hnswPaths = [
675
- path.join(process.cwd(), '.swarm', 'hnsw.index'),
676
- path.join(process.cwd(), '.claude-flow', 'hnsw.index'),
677
- path.join(process.cwd(), 'data', 'hnsw.index'),
678
- ];
679
-
680
- // Check direct database files first
681
- for (const dbFile of dbFilePaths) {
682
- if (fs.existsSync(dbFile)) {
683
- try {
684
- const stats = fs.statSync(dbFile);
685
- dbSizeKB = stats.size / 1024;
686
- // Estimate vectors: ~2KB per vector for SQLite with embeddings
687
- vectorCount = Math.floor(dbSizeKB / 2);
688
- namespaces = 1;
689
- break;
690
- } catch (e) {
691
- // Ignore
692
- }
429
+ for (const f of dbFiles) {
430
+ const stat = safeStat(f);
431
+ if (stat) {
432
+ dbSizeKB = stat.size / 1024;
433
+ vectorCount = Math.floor(dbSizeKB / 2);
434
+ namespaces = 1;
435
+ break;
693
436
  }
694
437
  }
695
438
 
696
- // Check database directories if no direct file found
697
439
  if (vectorCount === 0) {
698
- for (const dbPath of dbDirPaths) {
699
- if (fs.existsSync(dbPath)) {
700
- try {
701
- const stats = fs.statSync(dbPath);
702
- if (stats.isDirectory()) {
703
- const files = fs.readdirSync(dbPath);
704
- namespaces = files.filter(f => f.endsWith('.db') || f.endsWith('.sqlite')).length;
705
-
706
- for (const file of files) {
707
- const filePath = path.join(dbPath, file);
708
- const fileStat = fs.statSync(filePath);
709
- if (fileStat.isFile()) {
710
- dbSizeKB += fileStat.size / 1024;
711
- }
712
- }
713
-
714
- vectorCount = Math.floor(dbSizeKB / 2);
440
+ const dbDirs = [
441
+ path.join(CWD, '.claude-flow', 'agentdb'),
442
+ path.join(CWD, '.swarm', 'agentdb'),
443
+ path.join(CWD, '.agentdb'),
444
+ ];
445
+ for (const dir of dbDirs) {
446
+ try {
447
+ if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
448
+ const files = fs.readdirSync(dir);
449
+ namespaces = files.filter(f => f.endsWith('.db') || f.endsWith('.sqlite')).length;
450
+ for (const file of files) {
451
+ const stat = safeStat(path.join(dir, file));
452
+ if (stat && stat.isFile()) dbSizeKB += stat.size / 1024;
715
453
  }
454
+ vectorCount = Math.floor(dbSizeKB / 2);
716
455
  break;
717
- } catch (e) {
718
- // Ignore
719
456
  }
720
- }
457
+ } catch { /* ignore */ }
721
458
  }
722
459
  }
723
460
 
724
- // Check for HNSW index (indicates vector search capability)
725
- for (const hnswPath of hnswPaths) {
726
- if (fs.existsSync(hnswPath)) {
461
+ const hnswPaths = [
462
+ path.join(CWD, '.swarm', 'hnsw.index'),
463
+ path.join(CWD, '.claude-flow', 'hnsw.index'),
464
+ ];
465
+ for (const p of hnswPaths) {
466
+ const stat = safeStat(p);
467
+ if (stat) {
727
468
  hasHnsw = true;
728
- try {
729
- const stats = fs.statSync(hnswPath);
730
- // HNSW index: ~0.5KB per vector
731
- const hnswVectors = Math.floor(stats.size / 1024 / 0.5);
732
- vectorCount = Math.max(vectorCount, hnswVectors);
733
- } catch (e) {
734
- // Ignore
735
- }
469
+ vectorCount = Math.max(vectorCount, Math.floor(stat.size / 512));
736
470
  break;
737
471
  }
738
472
  }
739
473
 
740
- // Also check for vectors.json (simple vector store)
741
- const vectorsPath = path.join(process.cwd(), '.claude-flow', 'vectors.json');
742
- if (fs.existsSync(vectorsPath) && vectorCount === 0) {
743
- try {
744
- const data = JSON.parse(fs.readFileSync(vectorsPath, 'utf-8'));
745
- if (Array.isArray(data)) {
746
- vectorCount = data.length;
747
- } else if (data.vectors) {
748
- vectorCount = Object.keys(data.vectors).length;
749
- }
750
- } catch (e) {
751
- // Ignore
752
- }
753
- }
754
-
755
474
  return { vectorCount, dbSizeKB: Math.floor(dbSizeKB), namespaces, hasHnsw };
756
475
  }
757
476
 
758
- // Get test statistics
477
+ // Test stats (count files only — NO reading file contents)
759
478
  function getTestStats() {
760
479
  let testFiles = 0;
761
- let testCases = 0;
762
-
763
- const testDirs = [
764
- path.join(process.cwd(), 'tests'),
765
- path.join(process.cwd(), 'test'),
766
- path.join(process.cwd(), '__tests__'),
767
- path.join(process.cwd(), 'src', '__tests__'),
768
- path.join(process.cwd(), 'v3', '__tests__'),
769
- ];
770
-
771
- // Recursively count test files
772
- function countTestFiles(dir, depth = 0) {
773
- if (depth > 3) return; // Limit recursion
774
- if (!fs.existsSync(dir)) return;
775
480
 
481
+ function countTestFiles(dir, depth) {
482
+ if (depth === undefined) depth = 0;
483
+ if (depth > 2) return;
776
484
  try {
485
+ if (!fs.existsSync(dir)) return;
777
486
  const entries = fs.readdirSync(dir, { withFileTypes: true });
778
487
  for (const entry of entries) {
779
488
  if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
780
489
  countTestFiles(path.join(dir, entry.name), depth + 1);
781
490
  } else if (entry.isFile()) {
782
- const name = entry.name;
783
- if (name.includes('.test.') || name.includes('.spec.') ||
784
- name.includes('_test.') || name.includes('_spec.') ||
785
- name.startsWith('test_') || name.startsWith('spec_')) {
491
+ const n = entry.name;
492
+ if (n.includes('.test.') || n.includes('.spec.') || n.includes('_test.') || n.includes('_spec.')) {
786
493
  testFiles++;
787
-
788
- // Try to estimate test cases from file
789
- try {
790
- const content = fs.readFileSync(path.join(dir, name), 'utf-8');
791
- // Count it(), test(), describe() patterns
792
- const itMatches = (content.match(/\\bit\\s*\\(/g) || []).length;
793
- const testMatches = (content.match(/\\btest\\s*\\(/g) || []).length;
794
- testCases += itMatches + testMatches;
795
- } catch (e) {
796
- // Estimate 3 tests per file if can't read
797
- testCases += 3;
798
- }
799
494
  }
800
495
  }
801
496
  }
802
- } catch (e) {
803
- // Ignore
804
- }
805
- }
806
-
807
- for (const dir of testDirs) {
808
- countTestFiles(dir);
497
+ } catch { /* ignore */ }
809
498
  }
810
499
 
811
- // Also check src directory for colocated tests
812
- const srcDir = path.join(process.cwd(), 'src');
813
- if (fs.existsSync(srcDir)) {
814
- countTestFiles(srcDir);
500
+ var testDirNames = ['tests', 'test', '__tests__', 'v3/__tests__'];
501
+ for (var i = 0; i < testDirNames.length; i++) {
502
+ countTestFiles(path.join(CWD, testDirNames[i]));
815
503
  }
504
+ countTestFiles(path.join(CWD, 'src'));
816
505
 
817
- return { testFiles, testCases };
506
+ return { testFiles, testCases: testFiles * 4 };
818
507
  }
819
508
 
820
- // Get integration status (MCP servers, external connections)
509
+ // Integration status (shared settings + file checks)
821
510
  function getIntegrationStatus() {
822
- let mcpServers = { total: 0, enabled: 0, names: [] };
823
- let hasDatabase = false;
824
- let hasCache = false;
825
- let hasApi = false;
826
-
827
- // Check for MCP servers in settings
828
- const settingsPaths = [
829
- path.join(process.cwd(), '.claude', 'settings.json'),
830
- path.join(process.cwd(), '.claude', 'settings.local.json'),
831
- ];
511
+ const mcpServers = { total: 0, enabled: 0 };
512
+ const settings = getSettings();
832
513
 
833
- for (const settingsPath of settingsPaths) {
834
- if (fs.existsSync(settingsPath)) {
835
- try {
836
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
837
-
838
- // Check mcpServers object
839
- if (settings.mcpServers && typeof settings.mcpServers === 'object') {
840
- const servers = Object.keys(settings.mcpServers);
841
- mcpServers.total = servers.length;
842
- mcpServers.names = servers;
843
-
844
- // Check enabledMcpjsonServers for enabled count
845
- if (settings.enabledMcpjsonServers && Array.isArray(settings.enabledMcpjsonServers)) {
846
- mcpServers.enabled = settings.enabledMcpjsonServers.filter(s => servers.includes(s)).length;
847
- } else {
848
- mcpServers.enabled = mcpServers.total; // Assume all enabled if not specified
849
- }
850
- }
851
- break;
852
- } catch (e) { /* ignore */ }
853
- }
514
+ if (settings && settings.mcpServers && typeof settings.mcpServers === 'object') {
515
+ const servers = Object.keys(settings.mcpServers);
516
+ mcpServers.total = servers.length;
517
+ mcpServers.enabled = settings.enabledMcpjsonServers
518
+ ? settings.enabledMcpjsonServers.filter(s => servers.includes(s)).length
519
+ : servers.length;
854
520
  }
855
521
 
856
- // Also check .mcp.json or mcp.json
857
- const mcpConfigPaths = [
858
- path.join(process.cwd(), '.mcp.json'),
859
- path.join(process.cwd(), 'mcp.json'),
860
- path.join(require('os').homedir(), '.claude', 'mcp.json'),
861
- ];
862
-
863
- for (const mcpPath of mcpConfigPaths) {
864
- if (fs.existsSync(mcpPath) && mcpServers.total === 0) {
865
- try {
866
- const config = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
867
- if (config.mcpServers) {
868
- const servers = Object.keys(config.mcpServers);
869
- mcpServers.total = servers.length;
870
- mcpServers.names = servers;
871
- mcpServers.enabled = servers.length;
872
- }
873
- } catch (e) { /* ignore */ }
522
+ if (mcpServers.total === 0) {
523
+ const mcpConfig = readJSON(path.join(CWD, '.mcp.json'))
524
+ || readJSON(path.join(os.homedir(), '.claude', 'mcp.json'));
525
+ if (mcpConfig && mcpConfig.mcpServers) {
526
+ const s = Object.keys(mcpConfig.mcpServers);
527
+ mcpServers.total = s.length;
528
+ mcpServers.enabled = s.length;
874
529
  }
875
530
  }
876
531
 
877
- // Check for database (AgentDB, SQLite, etc.)
878
- const dbPaths = [
879
- path.join(process.cwd(), '.swarm', 'memory.db'),
880
- path.join(process.cwd(), '.claude-flow', 'memory.db'),
881
- path.join(process.cwd(), 'data', 'memory.db'),
882
- ];
883
- hasDatabase = dbPaths.some(p => fs.existsSync(p));
884
-
885
- // Check for cache
886
- const cachePaths = [
887
- path.join(process.cwd(), '.claude-flow', 'cache'),
888
- path.join(process.cwd(), '.cache'),
889
- path.join(process.cwd(), 'node_modules', '.cache'),
890
- ];
891
- hasCache = cachePaths.some(p => fs.existsSync(p));
892
-
893
- // Check for API configuration (env vars or config)
894
- try {
895
- hasApi = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
896
- } catch (e) { /* ignore */ }
532
+ const hasDatabase = ['.swarm/memory.db', '.claude-flow/memory.db', 'data/memory.db']
533
+ .some(p => fs.existsSync(path.join(CWD, p)));
534
+ const hasApi = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
897
535
 
898
- return { mcpServers, hasDatabase, hasCache, hasApi };
536
+ return { mcpServers, hasDatabase, hasApi };
899
537
  }
900
538
 
901
- // Get git status (uncommitted changes, untracked files) - cross-platform
902
- function getGitStatus() {
903
- let modified = 0;
904
- let untracked = 0;
905
- let staged = 0;
906
- let ahead = 0;
907
- let behind = 0;
908
- const isWindows = process.platform === 'win32';
909
-
910
- try {
911
- // Get modified and staged counts - works on all platforms
912
- const status = execSync('git status --porcelain', {
913
- encoding: 'utf-8',
914
- stdio: ['pipe', 'pipe', 'pipe'], // Suppress stderr
915
- timeout: 5000,
916
- });
917
- const lines = status.trim().split('\\n').filter(l => l);
918
- for (const line of lines) {
919
- const code = line.substring(0, 2);
920
- if (code.includes('M') || code.includes('D') || code.includes('R')) {
921
- if (code[0] !== ' ') staged++;
922
- if (code[1] !== ' ') modified++;
923
- }
924
- if (code.includes('?')) untracked++;
925
- if (code.includes('A')) staged++;
926
- }
927
-
928
- // Get ahead/behind - may fail if no upstream
929
- try {
930
- const abStatus = execSync('git rev-list --left-right --count HEAD...@{upstream}', {
931
- encoding: 'utf-8',
932
- stdio: ['pipe', 'pipe', 'pipe'],
933
- timeout: 5000,
934
- });
935
- const parts = abStatus.trim().split(/\\s+/);
936
- ahead = parseInt(parts[0]) || 0;
937
- behind = parseInt(parts[1]) || 0;
938
- } catch (e) { /* no upstream or error - that's ok */ }
939
-
940
- } catch (e) {
941
- // Not a git repo or git not installed - return zeros
942
- }
943
-
944
- return { modified, untracked, staged, ahead, behind };
945
- }
946
-
947
- // Get session statistics
539
+ // Session stats (pure file reads)
948
540
  function getSessionStats() {
949
- let sessionStart = null;
950
- let duration = '';
951
- let lastActivity = '';
952
- let operationsCount = 0;
953
-
954
- // Check for session file
955
- const sessionPaths = [
956
- path.join(process.cwd(), '.claude-flow', 'session.json'),
957
- path.join(process.cwd(), '.claude', 'session.json'),
958
- ];
959
-
960
- for (const sessPath of sessionPaths) {
961
- if (fs.existsSync(sessPath)) {
962
- try {
963
- const data = JSON.parse(fs.readFileSync(sessPath, 'utf-8'));
964
- if (data.startTime) {
965
- sessionStart = new Date(data.startTime);
966
- const now = new Date();
967
- const diffMs = now.getTime() - sessionStart.getTime();
968
- const diffMins = Math.floor(diffMs / 60000);
969
- if (diffMins < 60) {
970
- duration = \`\${diffMins}m\`;
971
- } else {
972
- const hours = Math.floor(diffMins / 60);
973
- const mins = diffMins % 60;
974
- duration = \`\${hours}h\${mins}m\`;
975
- }
976
- }
977
- if (data.lastActivity) {
978
- const last = new Date(data.lastActivity);
979
- const now = new Date();
980
- const diffMs = now.getTime() - last.getTime();
981
- const diffMins = Math.floor(diffMs / 60000);
982
- if (diffMins < 1) lastActivity = 'now';
983
- else if (diffMins < 60) lastActivity = \`\${diffMins}m ago\`;
984
- else lastActivity = \`\${Math.floor(diffMins / 60)}h ago\`;
985
- }
986
- operationsCount = data.operationsCount || data.commandCount || 0;
987
- break;
988
- } catch (e) { /* ignore */ }
541
+ var sessionPaths = ['.claude-flow/session.json', '.claude/session.json'];
542
+ for (var i = 0; i < sessionPaths.length; i++) {
543
+ const data = readJSON(path.join(CWD, sessionPaths[i]));
544
+ if (data && data.startTime) {
545
+ const diffMs = Date.now() - new Date(data.startTime).getTime();
546
+ const mins = Math.floor(diffMs / 60000);
547
+ const duration = mins < 60 ? mins + 'm' : Math.floor(mins / 60) + 'h' + (mins % 60) + 'm';
548
+ return { duration: duration };
989
549
  }
990
550
  }
991
-
992
- // Fallback: check metrics for activity
993
- if (!duration) {
994
- const metricsPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'activity.json');
995
- if (fs.existsSync(metricsPath)) {
996
- try {
997
- const data = JSON.parse(fs.readFileSync(metricsPath, 'utf-8'));
998
- operationsCount = data.totalOperations || 0;
999
- } catch (e) { /* ignore */ }
1000
- }
1001
- }
1002
-
1003
- return { duration, lastActivity, operationsCount };
1004
- }
1005
-
1006
- // Get trend indicator based on change
1007
- function getTrend(current, previous) {
1008
- if (previous === null || previous === undefined) return '';
1009
- if (current > previous) return \`\${c.brightGreen}↑\${c.reset}\`;
1010
- if (current < previous) return \`\${c.brightRed}↓\${c.reset}\`;
1011
- return \`\${c.dim}→\${c.reset}\`;
551
+ return { duration: '' };
1012
552
  }
1013
553
 
1014
- // Store previous values for trends (persisted between calls)
1015
- let prevIntelligence = null;
1016
- try {
1017
- const trendPath = path.join(process.cwd(), '.claude-flow', '.trend-cache.json');
1018
- if (fs.existsSync(trendPath)) {
1019
- const data = JSON.parse(fs.readFileSync(trendPath, 'utf-8'));
1020
- prevIntelligence = data.intelligence;
1021
- }
1022
- } catch (e) { /* ignore */ }
554
+ // ─── Rendering ──────────────────────────────────────────────────
1023
555
 
1024
- // Generate progress bar
1025
556
  function progressBar(current, total) {
1026
557
  const width = 5;
1027
558
  const filled = Math.round((current / total) * width);
1028
- const empty = width - filled;
1029
- return '[' + '\\u25CF'.repeat(filled) + '\\u25CB'.repeat(empty) + ']';
559
+ return '[' + '\\u25CF'.repeat(filled) + '\\u25CB'.repeat(width - filled) + ']';
1030
560
  }
1031
561
 
1032
- // Generate full statusline
1033
562
  function generateStatusline() {
1034
- const user = getUserInfo();
563
+ const git = getGitInfo();
564
+ // Prefer model name from Claude Code stdin data, fallback to file-based detection
565
+ const modelName = getModelFromStdin() || getModelName();
566
+ const ctxInfo = getContextFromStdin();
567
+ const costInfo = getCostFromStdin();
1035
568
  const progress = getV3Progress();
1036
569
  const security = getSecurityStatus();
1037
570
  const swarm = getSwarmStatus();
@@ -1040,147 +573,122 @@ function generateStatusline() {
1040
573
  const hooks = getHooksStatus();
1041
574
  const agentdb = getAgentDBStats();
1042
575
  const tests = getTestStats();
1043
- const git = getGitStatus();
1044
576
  const session = getSessionStats();
1045
577
  const integration = getIntegrationStatus();
1046
578
  const lines = [];
1047
579
 
1048
- // Calculate intelligence trend
1049
- const intellTrend = getTrend(system.intelligencePct, prevIntelligence);
1050
-
1051
- // Save current values for next trend calculation
1052
- try {
1053
- const trendPath = path.join(process.cwd(), '.claude-flow', '.trend-cache.json');
1054
- const trendDir = path.dirname(trendPath);
1055
- if (!fs.existsSync(trendDir)) fs.mkdirSync(trendDir, { recursive: true });
1056
- fs.writeFileSync(trendPath, JSON.stringify({ intelligence: system.intelligencePct, timestamp: Date.now() }));
1057
- } catch (e) { /* ignore */ }
1058
-
1059
- // Header Line with git changes indicator
1060
- let header = \`\${c.bold}\${c.brightPurple}▊ Claude Flow V3 \${c.reset}\`;
1061
- header += \`\${swarm.coordinationActive ? c.brightCyan : c.dim}● \${c.brightCyan}\${user.name}\${c.reset}\`;
1062
- if (user.gitBranch) {
1063
- header += \` \${c.dim}│\${c.reset} \${c.brightBlue}⎇ \${user.gitBranch}\${c.reset}\`;
1064
- // Add git changes indicator
1065
- const gitChanges = git.modified + git.staged + git.untracked;
1066
- if (gitChanges > 0) {
1067
- let gitIndicator = '';
1068
- if (git.staged > 0) gitIndicator += \`\${c.brightGreen}+\${git.staged}\${c.reset}\`;
1069
- if (git.modified > 0) gitIndicator += \`\${c.brightYellow}~\${git.modified}\${c.reset}\`;
1070
- if (git.untracked > 0) gitIndicator += \`\${c.dim}?\${git.untracked}\${c.reset}\`;
1071
- header += \` \${gitIndicator}\`;
1072
- }
1073
- // Add ahead/behind indicator
1074
- if (git.ahead > 0 || git.behind > 0) {
1075
- if (git.ahead > 0) header += \` \${c.brightGreen}↑\${git.ahead}\${c.reset}\`;
1076
- if (git.behind > 0) header += \` \${c.brightRed}↓\${git.behind}\${c.reset}\`;
1077
- }
1078
- }
1079
- header += \` \${c.dim}│\${c.reset} \${c.purple}\${user.modelName}\${c.reset}\`;
1080
- // Add session duration if available
1081
- if (session.duration) {
1082
- header += \` \${c.dim}│\${c.reset} \${c.cyan}⏱ \${session.duration}\${c.reset}\`;
580
+ // Header
581
+ let header = c.bold + c.brightPurple + '\\u258A Claude Flow V3 ' + c.reset;
582
+ header += (swarm.coordinationActive ? c.brightCyan : c.dim) + '\\u25CF ' + c.brightCyan + git.name + c.reset;
583
+ if (git.gitBranch) {
584
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.brightBlue + '\\u23C7 ' + git.gitBranch + c.reset;
585
+ const changes = git.modified + git.staged + git.untracked;
586
+ if (changes > 0) {
587
+ let ind = '';
588
+ if (git.staged > 0) ind += c.brightGreen + '+' + git.staged + c.reset;
589
+ if (git.modified > 0) ind += c.brightYellow + '~' + git.modified + c.reset;
590
+ if (git.untracked > 0) ind += c.dim + '?' + git.untracked + c.reset;
591
+ header += ' ' + ind;
592
+ }
593
+ if (git.ahead > 0) header += ' ' + c.brightGreen + '\\u2191' + git.ahead + c.reset;
594
+ if (git.behind > 0) header += ' ' + c.brightRed + '\\u2193' + git.behind + c.reset;
595
+ }
596
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.purple + modelName + c.reset;
597
+ // Show session duration from Claude Code stdin if available, else from local files
598
+ const duration = costInfo ? costInfo.duration : session.duration;
599
+ if (duration) header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.cyan + '\\u23F1 ' + duration + c.reset;
600
+ // Show context usage from Claude Code stdin if available
601
+ if (ctxInfo && ctxInfo.usedPct > 0) {
602
+ const ctxColor = ctxInfo.usedPct >= 90 ? c.brightRed : ctxInfo.usedPct >= 70 ? c.brightYellow : c.brightGreen;
603
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + ctxColor + '\\u25CF ' + ctxInfo.usedPct + '% ctx' + c.reset;
604
+ }
605
+ // Show cost from Claude Code stdin if available
606
+ if (costInfo && costInfo.costUsd > 0) {
607
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.brightYellow + '$' + costInfo.costUsd.toFixed(2) + c.reset;
1083
608
  }
1084
609
  lines.push(header);
1085
610
 
1086
611
  // Separator
1087
- lines.push(\`\${c.dim}─────────────────────────────────────────────────────\${c.reset}\`);
612
+ lines.push(c.dim + '\\u2500'.repeat(53) + c.reset);
1088
613
 
1089
- // Line 1: DDD Domain Progress with dynamic performance indicator
614
+ // Line 1: DDD Domains
1090
615
  const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red;
1091
- // Show HNSW speedup if enabled, otherwise show patterns learned
1092
- let perfIndicator = '';
616
+ let perfIndicator;
1093
617
  if (agentdb.hasHnsw && agentdb.vectorCount > 0) {
1094
- // HNSW enabled: show estimated speedup (150x-12500x based on vector count)
1095
618
  const speedup = agentdb.vectorCount > 10000 ? '12500x' : agentdb.vectorCount > 1000 ? '150x' : '10x';
1096
- perfIndicator = \`\${c.brightGreen}⚡ HNSW \${speedup}\${c.reset}\`;
619
+ perfIndicator = c.brightGreen + '\\u26A1 HNSW ' + speedup + c.reset;
1097
620
  } else if (progress.patternsLearned > 0) {
1098
- // Show patterns learned
1099
- const patternsK = progress.patternsLearned >= 1000
1100
- ? \`\${(progress.patternsLearned / 1000).toFixed(1)}k\`
1101
- : String(progress.patternsLearned);
1102
- perfIndicator = \`\${c.brightYellow}📚 \${patternsK} patterns\${c.reset}\`;
621
+ const pk = progress.patternsLearned >= 1000 ? (progress.patternsLearned / 1000).toFixed(1) + 'k' : String(progress.patternsLearned);
622
+ perfIndicator = c.brightYellow + '\\uD83D\\uDCDA ' + pk + ' patterns' + c.reset;
1103
623
  } else {
1104
- // New project: show target
1105
- perfIndicator = \`\${c.dim}⚡ target: 150x-12500x\${c.reset}\`;
624
+ perfIndicator = c.dim + '\\u26A1 target: 150x-12500x' + c.reset;
1106
625
  }
1107
626
  lines.push(
1108
- \`\${c.brightCyan}🏗️ DDD Domains\${c.reset} \${progressBar(progress.domainsCompleted, progress.totalDomains)} \` +
1109
- \`\${domainsColor}\${progress.domainsCompleted}\${c.reset}/\${c.brightWhite}\${progress.totalDomains}\${c.reset} \` +
1110
- perfIndicator
627
+ c.brightCyan + '\\uD83C\\uDFD7\\uFE0F DDD Domains' + c.reset + ' ' + progressBar(progress.domainsCompleted, progress.totalDomains) + ' ' +
628
+ domainsColor + progress.domainsCompleted + c.reset + '/' + c.brightWhite + progress.totalDomains + c.reset + ' ' + perfIndicator
1111
629
  );
1112
630
 
1113
- // Line 2: Swarm + Hooks + CVE + Memory + Context + Intelligence
1114
- const swarmIndicator = swarm.coordinationActive ? \`\${c.brightGreen}◉\${c.reset}\` : \`\${c.dim}○\${c.reset}\`;
631
+ // Line 2: Swarm + Hooks + CVE + Memory + Intelligence
632
+ const swarmInd = swarm.coordinationActive ? c.brightGreen + '\\u25C9' + c.reset : c.dim + '\\u25CB' + c.reset;
1115
633
  const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
1116
- let securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'IN_PROGRESS' ? '🟡' : '🔴';
1117
- let securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
634
+ const secIcon = security.status === 'CLEAN' ? '\\uD83D\\uDFE2' : security.status === 'IN_PROGRESS' ? '\\uD83D\\uDFE1' : '\\uD83D\\uDD34';
635
+ const secColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
1118
636
  const hooksColor = hooks.enabled > 0 ? c.brightGreen : c.dim;
637
+ const intellColor = system.intelligencePct >= 80 ? c.brightGreen : system.intelligencePct >= 40 ? c.brightYellow : c.dim;
1119
638
 
1120
639
  lines.push(
1121
- \`\${c.brightYellow}🤖 Swarm\${c.reset} \${swarmIndicator} [\${agentsColor}\${String(swarm.activeAgents).padStart(2)}\${c.reset}/\${c.brightWhite}\${swarm.maxAgents}\${c.reset}] \` +
1122
- \`\${c.brightPurple}👥 \${system.subAgents}\${c.reset} \` +
1123
- \`\${c.brightBlue}🪝 \${hooksColor}\${hooks.enabled}\${c.reset}/\${c.brightWhite}\${hooks.total}\${c.reset} \` +
1124
- \`\${securityIcon} \${securityColor}CVE \${security.cvesFixed}\${c.reset}/\${c.brightWhite}\${security.totalCves}\${c.reset} \` +
1125
- \`\${c.brightCyan}💾 \${system.memoryMB}MB\${c.reset} \` +
1126
- \`\${system.intelligencePct >= 80 ? c.brightGreen : system.intelligencePct >= 40 ? c.brightYellow : c.dim}🧠 \${String(system.intelligencePct).padStart(3)}%\${intellTrend}\${c.reset}\`
640
+ c.brightYellow + '\\uD83E\\uDD16 Swarm' + c.reset + ' ' + swarmInd + ' [' + agentsColor + String(swarm.activeAgents).padStart(2) + c.reset + '/' + c.brightWhite + swarm.maxAgents + c.reset + '] ' +
641
+ c.brightPurple + '\\uD83D\\uDC65 ' + system.subAgents + c.reset + ' ' +
642
+ c.brightBlue + '\\uD83E\\uDE9D ' + hooksColor + hooks.enabled + c.reset + '/' + c.brightWhite + hooks.total + c.reset + ' ' +
643
+ secIcon + ' ' + secColor + 'CVE ' + security.cvesFixed + c.reset + '/' + c.brightWhite + security.totalCves + c.reset + ' ' +
644
+ c.brightCyan + '\\uD83D\\uDCBE ' + system.memoryMB + 'MB' + c.reset + ' ' +
645
+ intellColor + '\\uD83E\\uDDE0 ' + String(system.intelligencePct).padStart(3) + '%' + c.reset
1127
646
  );
1128
647
 
1129
- // Line 3: Architecture status with ADRs, AgentDB, Tests
648
+ // Line 3: Architecture
1130
649
  const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
1131
650
  const adrColor = adrs.count > 0 ? (adrs.implemented === adrs.count ? c.brightGreen : c.yellow) : c.dim;
1132
- const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
1133
- const testColor = tests.testFiles > 0 ? c.brightGreen : c.dim;
1134
-
1135
- // Show ADR compliance % if from real data, otherwise show count
1136
- const adrDisplay = adrs.compliance > 0
1137
- ? \`\${adrColor}●\${adrs.compliance}%\${c.reset}\`
1138
- : \`\${adrColor}●\${adrs.implemented}/\${adrs.count}\${c.reset}\`;
651
+ const adrDisplay = adrs.compliance > 0 ? adrColor + '\\u25CF' + adrs.compliance + '%' + c.reset : adrColor + '\\u25CF' + adrs.implemented + '/' + adrs.count + c.reset;
1139
652
 
1140
653
  lines.push(
1141
- \`\${c.brightPurple}🔧 Architecture\${c.reset} \` +
1142
- \`\${c.cyan}ADRs\${c.reset} \${adrDisplay} \${c.dim}│\${c.reset} \` +
1143
- \`\${c.cyan}DDD\${c.reset} \${dddColor}●\${String(progress.dddProgress).padStart(3)}%\${c.reset} \${c.dim}│\${c.reset} \` +
1144
- \`\${c.cyan}Security\${c.reset} \${securityColor}●\${security.status}\${c.reset}\`
654
+ c.brightPurple + '\\uD83D\\uDD27 Architecture' + c.reset + ' ' +
655
+ c.cyan + 'ADRs' + c.reset + ' ' + adrDisplay + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
656
+ c.cyan + 'DDD' + c.reset + ' ' + dddColor + '\\u25CF' + String(progress.dddProgress).padStart(3) + '%' + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
657
+ c.cyan + 'Security' + c.reset + ' ' + secColor + '\\u25CF' + security.status + c.reset
1145
658
  );
1146
659
 
1147
- // Line 4: Memory, Vectors, Tests
1148
- const hnswIndicator = agentdb.hasHnsw ? \`\${c.brightGreen}⚡\${c.reset}\` : '';
1149
- const sizeDisplay = agentdb.dbSizeKB >= 1024
1150
- ? \`\${(agentdb.dbSizeKB / 1024).toFixed(1)}MB\`
1151
- : \`\${agentdb.dbSizeKB}KB\`;
1152
- // Build integration status string
1153
- let integrationStr = '';
660
+ // Line 4: AgentDB, Tests, Integration
661
+ const hnswInd = agentdb.hasHnsw ? c.brightGreen + '\\u26A1' + c.reset : '';
662
+ const sizeDisp = agentdb.dbSizeKB >= 1024 ? (agentdb.dbSizeKB / 1024).toFixed(1) + 'MB' : agentdb.dbSizeKB + 'KB';
663
+ const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
664
+ const testColor = tests.testFiles > 0 ? c.brightGreen : c.dim;
665
+
666
+ let integStr = '';
1154
667
  if (integration.mcpServers.total > 0) {
1155
- const mcpColor = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen :
1156
- integration.mcpServers.enabled > 0 ? c.brightYellow : c.red;
1157
- integrationStr += \`\${c.cyan}MCP\${c.reset} \${mcpColor}●\${integration.mcpServers.enabled}/\${integration.mcpServers.total}\${c.reset}\`;
1158
- }
1159
- if (integration.hasDatabase) {
1160
- integrationStr += (integrationStr ? ' ' : '') + \`\${c.brightGreen}◆\${c.reset}DB\`;
1161
- }
1162
- if (integration.hasApi) {
1163
- integrationStr += (integrationStr ? ' ' : '') + \`\${c.brightGreen}◆\${c.reset}API\`;
1164
- }
1165
- if (!integrationStr) {
1166
- integrationStr = \`\${c.dim}●none\${c.reset}\`;
668
+ const mcpCol = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen :
669
+ integration.mcpServers.enabled > 0 ? c.brightYellow : c.red;
670
+ integStr += c.cyan + 'MCP' + c.reset + ' ' + mcpCol + '\\u25CF' + integration.mcpServers.enabled + '/' + integration.mcpServers.total + c.reset;
1167
671
  }
672
+ if (integration.hasDatabase) integStr += (integStr ? ' ' : '') + c.brightGreen + '\\u25C6' + c.reset + 'DB';
673
+ if (integration.hasApi) integStr += (integStr ? ' ' : '') + c.brightGreen + '\\u25C6' + c.reset + 'API';
674
+ if (!integStr) integStr = c.dim + '\\u25CF none' + c.reset;
1168
675
 
1169
676
  lines.push(
1170
- \`\${c.brightCyan}📊 AgentDB\${c.reset} \` +
1171
- \`\${c.cyan}Vectors\${c.reset} \${vectorColor}●\${agentdb.vectorCount}\${hnswIndicator}\${c.reset} \${c.dim}│\${c.reset} \` +
1172
- \`\${c.cyan}Size\${c.reset} \${c.brightWhite}\${sizeDisplay}\${c.reset} \${c.dim}│\${c.reset} \` +
1173
- \`\${c.cyan}Tests\${c.reset} \${testColor}●\${tests.testFiles}\${c.reset} \${c.dim}(\${tests.testCases} cases)\${c.reset} \${c.dim}│\${c.reset} \` +
1174
- integrationStr
677
+ c.brightCyan + '\\uD83D\\uDCCA AgentDB' + c.reset + ' ' +
678
+ c.cyan + 'Vectors' + c.reset + ' ' + vectorColor + '\\u25CF' + agentdb.vectorCount + hnswInd + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
679
+ c.cyan + 'Size' + c.reset + ' ' + c.brightWhite + sizeDisp + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
680
+ c.cyan + 'Tests' + c.reset + ' ' + testColor + '\\u25CF' + tests.testFiles + c.reset + ' ' + c.dim + '(~' + tests.testCases + ' cases)' + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
681
+ integStr
1175
682
  );
1176
683
 
1177
684
  return lines.join('\\n');
1178
685
  }
1179
686
 
1180
- // Generate JSON data
687
+ // JSON output
1181
688
  function generateJSON() {
689
+ const git = getGitInfo();
1182
690
  return {
1183
- user: getUserInfo(),
691
+ user: { name: git.name, gitBranch: git.gitBranch, modelName: getModelName() },
1184
692
  v3Progress: getV3Progress(),
1185
693
  security: getSecurityStatus(),
1186
694
  swarm: getSwarmStatus(),
@@ -1189,16 +697,81 @@ function generateJSON() {
1189
697
  hooks: getHooksStatus(),
1190
698
  agentdb: getAgentDBStats(),
1191
699
  tests: getTestStats(),
1192
- performance: {
1193
- flashAttentionTarget: '2.49x-7.47x',
1194
- searchImprovement: '150x-12,500x',
1195
- memoryReduction: '50-75%',
1196
- },
700
+ git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind },
1197
701
  lastUpdated: new Date().toISOString(),
1198
702
  };
1199
703
  }
1200
704
 
1201
- // Main
705
+ // ─── Stdin reader (Claude Code pipes session JSON) ──────────────
706
+
707
+ // Claude Code sends session JSON via stdin (model, context, cost, etc.)
708
+ // Read it synchronously so the script works both:
709
+ // 1. When invoked by Claude Code (stdin has JSON)
710
+ // 2. When invoked manually from terminal (stdin is empty/tty)
711
+ let _stdinData = null;
712
+ function getStdinData() {
713
+ if (_stdinData !== undefined && _stdinData !== null) return _stdinData;
714
+ try {
715
+ // Check if stdin is a TTY (manual run) — skip reading
716
+ if (process.stdin.isTTY) { _stdinData = null; return null; }
717
+ // Read stdin synchronously via fd 0
718
+ const chunks = [];
719
+ const buf = Buffer.alloc(4096);
720
+ let bytesRead;
721
+ try {
722
+ while ((bytesRead = fs.readSync(0, buf, 0, buf.length, null)) > 0) {
723
+ chunks.push(buf.slice(0, bytesRead));
724
+ }
725
+ } catch { /* EOF or read error */ }
726
+ const raw = Buffer.concat(chunks).toString('utf-8').trim();
727
+ if (raw && raw.startsWith('{')) {
728
+ _stdinData = JSON.parse(raw);
729
+ } else {
730
+ _stdinData = null;
731
+ }
732
+ } catch {
733
+ _stdinData = null;
734
+ }
735
+ return _stdinData;
736
+ }
737
+
738
+ // Override model detection to prefer stdin data from Claude Code
739
+ function getModelFromStdin() {
740
+ const data = getStdinData();
741
+ if (data && data.model && data.model.display_name) return data.model.display_name;
742
+ return null;
743
+ }
744
+
745
+ // Get context window info from Claude Code session
746
+ function getContextFromStdin() {
747
+ const data = getStdinData();
748
+ if (data && data.context_window) {
749
+ return {
750
+ usedPct: Math.floor(data.context_window.used_percentage || 0),
751
+ remainingPct: Math.floor(data.context_window.remaining_percentage || 100),
752
+ };
753
+ }
754
+ return null;
755
+ }
756
+
757
+ // Get cost info from Claude Code session
758
+ function getCostFromStdin() {
759
+ const data = getStdinData();
760
+ if (data && data.cost) {
761
+ const durationMs = data.cost.total_duration_ms || 0;
762
+ const mins = Math.floor(durationMs / 60000);
763
+ const secs = Math.floor((durationMs % 60000) / 1000);
764
+ return {
765
+ costUsd: data.cost.total_cost_usd || 0,
766
+ duration: mins > 0 ? mins + 'm' + secs + 's' : secs + 's',
767
+ linesAdded: data.cost.total_lines_added || 0,
768
+ linesRemoved: data.cost.total_lines_removed || 0,
769
+ };
770
+ }
771
+ return null;
772
+ }
773
+
774
+ // ─── Main ───────────────────────────────────────────────────────
1202
775
  if (process.argv.includes('--json')) {
1203
776
  console.log(JSON.stringify(generateJSON(), null, 2));
1204
777
  } else if (process.argv.includes('--compact')) {
@@ -1213,10 +786,11 @@ if (process.argv.includes('--json')) {
1213
786
  */
1214
787
  export function generateStatuslineHook(options) {
1215
788
  if (!options.statusline.enabled) {
1216
- return '# Statusline disabled';
789
+ return '#!/bin/bash\n# Statusline disabled\n';
1217
790
  }
1218
- return `# Claude Flow V3 Statusline Hook
1219
- # Add to your shell RC file (.bashrc, .zshrc, etc.)
791
+ return `#!/bin/bash
792
+ # Claude Flow V3 Statusline Hook
793
+ # Source this in your .bashrc/.zshrc for terminal statusline
1220
794
 
1221
795
  # Function to get statusline
1222
796
  claude_flow_statusline() {
@@ -1226,16 +800,18 @@ claude_flow_statusline() {
1226
800
  fi
1227
801
  }
1228
802
 
1229
- # For bash PS1
803
+ # Bash: Add to PS1
1230
804
  # export PS1='$(claude_flow_statusline) \\n\\$ '
1231
805
 
1232
- # For zsh RPROMPT
806
+ # Zsh: Add to RPROMPT
1233
807
  # export RPROMPT='$(claude_flow_statusline)'
1234
808
 
1235
- # For starship (add to starship.toml)
1236
- # [custom.claude_flow]
1237
- # command = "node .claude/helpers/statusline.cjs 2>/dev/null"
1238
- # when = "test -f .claude/helpers/statusline.cjs"
809
+ # Claude Code: Add to .claude/settings.json
810
+ # "statusLine": {
811
+ # "type": "command",
812
+ # "command": "node .claude/helpers/statusline.cjs 2>/dev/null"
813
+ # "when": "test -f .claude/helpers/statusline.cjs"
814
+ # }
1239
815
  `;
1240
816
  }
1241
817
  //# sourceMappingURL=statusline-generator.js.map