@claude-flow/cli 3.1.0-alpha.9 → 3.5.1
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/agents/core/coder.md +1 -1
- package/.claude/agents/core/planner.md +2 -2
- package/.claude/agents/core/researcher.md +1 -1
- package/.claude/agents/core/reviewer.md +1 -1
- package/.claude/agents/core/tester.md +1 -1
- package/.claude/agents/data/data-ml-model.md +4 -4
- package/.claude/agents/development/dev-backend-api.md +4 -4
- package/.claude/agents/documentation/docs-api-openapi.md +4 -4
- package/.claude/agents/github/code-review-swarm.md +2 -2
- package/.claude/agents/github/issue-tracker.md +2 -2
- package/.claude/agents/github/pr-manager.md +2 -2
- package/.claude/agents/github/release-manager.md +2 -2
- package/.claude/agents/github/workflow-automation.md +2 -2
- package/.claude/agents/sparc/architecture.md +3 -3
- package/.claude/agents/sparc/pseudocode.md +2 -2
- package/.claude/agents/sparc/refinement.md +3 -3
- package/.claude/agents/sparc/specification.md +2 -2
- package/.claude/agents/swarm/adaptive-coordinator.md +1 -1
- package/.claude/agents/swarm/hierarchical-coordinator.md +1 -1
- package/.claude/agents/swarm/mesh-coordinator.md +1 -1
- package/.claude/agents/templates/base-template-generator.md +3 -3
- package/.claude/agents/templates/sparc-coordinator.md +3 -3
- package/.claude/helpers/auto-memory-hook.mjs +350 -0
- package/.claude/helpers/hook-handler.cjs +232 -0
- package/.claude/helpers/intelligence.cjs +916 -0
- package/.claude/helpers/session.js +8 -0
- package/.claude/helpers/statusline.cjs +96 -28
- package/.claude/settings.json +86 -141
- package/.claude/skills/reasoningbank-intelligence/SKILL.md +2 -2
- package/.claude/skills/swarm-orchestration/SKILL.md +1 -1
- package/README.md +910 -475
- package/bin/preinstall.cjs +2 -0
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +45 -2
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +292 -82
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +48 -4
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/neural.js.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/executor.d.ts +8 -2
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +315 -43
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/helpers-generator.d.ts +18 -0
- package/dist/src/init/helpers-generator.d.ts.map +1 -1
- package/dist/src/init/helpers-generator.js +498 -0
- package/dist/src/init/helpers-generator.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts +0 -1
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +32 -16
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/settings-generator.d.ts.map +1 -1
- package/dist/src/init/settings-generator.js +138 -95
- package/dist/src/init/settings-generator.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts +16 -8
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +506 -930
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/init/types.d.ts +8 -0
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js +7 -0
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +4 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/agentdb-tools.d.ts +30 -0
- package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/agentdb-tools.js +557 -0
- package/dist/src/mcp-tools/agentdb-tools.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +184 -32
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/neural-tools.js +32 -27
- package/dist/src/mcp-tools/neural-tools.js.map +1 -1
- package/dist/src/memory/intelligence.d.ts.map +1 -1
- package/dist/src/memory/intelligence.js +34 -6
- package/dist/src/memory/intelligence.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts +407 -0
- package/dist/src/memory/memory-bridge.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge.js +1493 -0
- package/dist/src/memory/memory-bridge.js.map +1 -0
- package/dist/src/memory/memory-initializer.d.ts +3 -0
- package/dist/src/memory/memory-initializer.d.ts.map +1 -1
- package/dist/src/memory/memory-initializer.js +125 -1
- package/dist/src/memory/memory-initializer.js.map +1 -1
- package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
- package/dist/src/ruvector/enhanced-model-router.js +25 -15
- package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
- package/dist/src/services/agentic-flow-bridge.d.ts +50 -0
- package/dist/src/services/agentic-flow-bridge.d.ts.map +1 -0
- package/dist/src/services/agentic-flow-bridge.js +95 -0
- package/dist/src/services/agentic-flow-bridge.js.map +1 -0
- package/dist/src/services/ruvector-training.d.ts +2 -1
- package/dist/src/services/ruvector-training.d.ts.map +1 -1
- package/dist/src/services/ruvector-training.js +1 -2
- package/dist/src/services/ruvector-training.js.map +1 -1
- package/dist/src/update/validator.js +1 -1
- package/dist/src/update/validator.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -6
|
@@ -1,48 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Statusline Configuration Generator
|
|
3
|
-
* Creates statusline
|
|
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
|
|
7
|
-
*
|
|
8
|
-
* ▊ Claude Flow V3 ● user │ ⎇
|
|
13
|
+
* Generate optimized statusline script
|
|
14
|
+
* Output format:
|
|
15
|
+
* ▊ Claude Flow V3 ● user │ ⎇ branch │ Opus 4.6
|
|
9
16
|
* ─────────────────────────────────────────────────────
|
|
10
|
-
* 🏗️ DDD Domains [
|
|
11
|
-
* 🤖 Swarm ◉ [
|
|
12
|
-
* 🔧 Architecture
|
|
17
|
+
* 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
|
|
18
|
+
* 🤖 Swarm ◉ [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
|
|
19
|
+
* 🔧 Architecture ADRs ●71% │ DDD ● 13% │ Security ●CLEAN
|
|
20
|
+
* 📊 AgentDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
|
|
13
21
|
*/
|
|
14
22
|
export function generateStatuslineScript(options) {
|
|
15
|
-
const
|
|
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
|
-
*
|
|
25
|
-
*
|
|
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
|
-
|
|
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
|
-
//
|
|
67
|
-
function
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
85
|
+
// Safe JSON file reader (returns null on failure)
|
|
86
|
+
function readJSON(filePath) {
|
|
80
87
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
126
|
-
//
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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(
|
|
154
|
-
path.join(
|
|
155
|
-
path.join(
|
|
156
|
-
path.join(
|
|
157
|
-
path.join(
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
sessions =
|
|
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
|
|
232
|
+
return { patterns: 0, sessions };
|
|
195
233
|
}
|
|
196
234
|
|
|
197
|
-
//
|
|
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
|
-
|
|
203
|
-
let
|
|
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
|
-
//
|
|
260
|
+
// Security status (pure file reads)
|
|
247
261
|
function getSecurityStatus() {
|
|
248
262
|
const totalCves = 3;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
//
|
|
287
|
+
// Swarm status (pure file reads, NO ps aux)
|
|
299
288
|
function getSwarmStatus() {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
//
|
|
310
|
+
// System metrics (uses process.memoryUsage() — no shell spawn)
|
|
371
311
|
function getSystemMetrics() {
|
|
372
|
-
|
|
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
|
-
//
|
|
418
|
-
const
|
|
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 (
|
|
425
|
-
intelligencePct =
|
|
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 =
|
|
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
|
-
//
|
|
329
|
+
// Maturity fallback (pure fs checks, no git exec)
|
|
435
330
|
if (intelligencePct === 0) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
//
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
-
//
|
|
358
|
+
// ADR status (count files only — don't read contents)
|
|
536
359
|
function getADRStatus() {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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
|
|
368
|
+
// Fallback: just count ADR files (don't read them)
|
|
558
369
|
const adrPaths = [
|
|
559
|
-
path.join(
|
|
560
|
-
path.join(
|
|
561
|
-
path.join(
|
|
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
|
-
|
|
573
|
-
|
|
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
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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
|
-
|
|
598
|
-
return { count, implemented, compliance };
|
|
388
|
+
return { count: 0, implemented: 0, compliance: 0 };
|
|
599
389
|
}
|
|
600
390
|
|
|
601
|
-
//
|
|
391
|
+
// Hooks status (shared settings cache)
|
|
602
392
|
function getHooksStatus() {
|
|
603
393
|
let enabled = 0;
|
|
604
|
-
|
|
394
|
+
const total = 17;
|
|
395
|
+
const settings = getSettings();
|
|
605
396
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
656
|
-
|
|
657
|
-
path.join(
|
|
658
|
-
path.join(
|
|
659
|
-
path.join(
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
783
|
-
if (
|
|
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
|
|
803
|
-
// Ignore
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
for (const dir of testDirs) {
|
|
808
|
-
countTestFiles(dir);
|
|
497
|
+
} catch { /* ignore */ }
|
|
809
498
|
}
|
|
810
499
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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
|
-
//
|
|
509
|
+
// Integration status (shared settings + file checks)
|
|
821
510
|
function getIntegrationStatus() {
|
|
822
|
-
|
|
823
|
-
|
|
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
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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,
|
|
536
|
+
return { mcpServers, hasDatabase, hasApi };
|
|
899
537
|
}
|
|
900
538
|
|
|
901
|
-
//
|
|
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
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
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(
|
|
612
|
+
lines.push(c.dim + '\\u2500'.repeat(53) + c.reset);
|
|
1088
613
|
|
|
1089
|
-
// Line 1: DDD
|
|
614
|
+
// Line 1: DDD Domains
|
|
1090
615
|
const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red;
|
|
1091
|
-
|
|
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 =
|
|
619
|
+
perfIndicator = c.brightGreen + '\\u26A1 HNSW ' + speedup + c.reset;
|
|
1097
620
|
} else if (progress.patternsLearned > 0) {
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1109
|
-
|
|
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 +
|
|
1114
|
-
const
|
|
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
|
-
|
|
1117
|
-
|
|
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
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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:
|
|
1148
|
-
const
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
let
|
|
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
|
|
1156
|
-
|
|
1157
|
-
|
|
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
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
-
//
|
|
687
|
+
// JSON output
|
|
1181
688
|
function generateJSON() {
|
|
689
|
+
const git = getGitInfo();
|
|
1182
690
|
return {
|
|
1183
|
-
user:
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
1219
|
-
#
|
|
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
|
-
#
|
|
803
|
+
# Bash: Add to PS1
|
|
1230
804
|
# export PS1='$(claude_flow_statusline) \\n\\$ '
|
|
1231
805
|
|
|
1232
|
-
#
|
|
806
|
+
# Zsh: Add to RPROMPT
|
|
1233
807
|
# export RPROMPT='$(claude_flow_statusline)'
|
|
1234
808
|
|
|
1235
|
-
#
|
|
1236
|
-
#
|
|
1237
|
-
# command
|
|
1238
|
-
#
|
|
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
|