@claude-flow/cli 3.10.3 → 3.10.5
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/dist/src/benchmarks/gaia-agent-planning.smoke.d.ts +18 -0
- package/dist/src/benchmarks/gaia-agent-planning.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-agent-planning.smoke.js +253 -0
- package/dist/src/benchmarks/gaia-agent-planning.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-agent.d.ts +198 -0
- package/dist/src/benchmarks/gaia-agent.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-agent.js +651 -0
- package/dist/src/benchmarks/gaia-agent.js.map +1 -0
- package/dist/src/benchmarks/gaia-causal-memory.d.ts +133 -0
- package/dist/src/benchmarks/gaia-causal-memory.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-causal-memory.js +281 -0
- package/dist/src/benchmarks/gaia-causal-memory.js.map +1 -0
- package/dist/src/benchmarks/gaia-causal-memory.smoke.d.ts +22 -0
- package/dist/src/benchmarks/gaia-causal-memory.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-causal-memory.smoke.js +300 -0
- package/dist/src/benchmarks/gaia-causal-memory.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-convergence.d.ts +138 -0
- package/dist/src/benchmarks/gaia-convergence.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-convergence.js +260 -0
- package/dist/src/benchmarks/gaia-convergence.js.map +1 -0
- package/dist/src/benchmarks/gaia-convergence.smoke.d.ts +19 -0
- package/dist/src/benchmarks/gaia-convergence.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-convergence.smoke.js +246 -0
- package/dist/src/benchmarks/gaia-convergence.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-critic.d.ts +123 -0
- package/dist/src/benchmarks/gaia-critic.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-critic.js +312 -0
- package/dist/src/benchmarks/gaia-critic.js.map +1 -0
- package/dist/src/benchmarks/gaia-critic.smoke.d.ts +21 -0
- package/dist/src/benchmarks/gaia-critic.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-critic.smoke.js +327 -0
- package/dist/src/benchmarks/gaia-critic.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-decomposer.d.ts +125 -0
- package/dist/src/benchmarks/gaia-decomposer.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-decomposer.js +350 -0
- package/dist/src/benchmarks/gaia-decomposer.js.map +1 -0
- package/dist/src/benchmarks/gaia-decomposer.smoke.d.ts +21 -0
- package/dist/src/benchmarks/gaia-decomposer.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-decomposer.smoke.js +228 -0
- package/dist/src/benchmarks/gaia-decomposer.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-e2e-smoke.d.ts +27 -0
- package/dist/src/benchmarks/gaia-e2e-smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-e2e-smoke.js +136 -0
- package/dist/src/benchmarks/gaia-e2e-smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-extract.smoke.d.ts +45 -0
- package/dist/src/benchmarks/gaia-extract.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-extract.smoke.js +242 -0
- package/dist/src/benchmarks/gaia-extract.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/features.d.ts +46 -0
- package/dist/src/benchmarks/gaia-hardness/features.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/features.js +170 -0
- package/dist/src/benchmarks/gaia-hardness/features.js.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.d.ts +105 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.js +260 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.js.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.smoke.d.ts +20 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.smoke.js +235 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.smoke.js.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/train-data-loader.d.ts +51 -0
- package/dist/src/benchmarks/gaia-hardness/train-data-loader.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-hardness/train-data-loader.js +179 -0
- package/dist/src/benchmarks/gaia-hardness/train-data-loader.js.map +1 -0
- package/dist/src/benchmarks/gaia-judge.d.ts +88 -0
- package/dist/src/benchmarks/gaia-judge.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-judge.js +437 -0
- package/dist/src/benchmarks/gaia-judge.js.map +1 -0
- package/dist/src/benchmarks/gaia-loader.d.ts +87 -0
- package/dist/src/benchmarks/gaia-loader.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-loader.js +326 -0
- package/dist/src/benchmarks/gaia-loader.js.map +1 -0
- package/dist/src/benchmarks/gaia-tools/file_read.d.ts +35 -0
- package/dist/src/benchmarks/gaia-tools/file_read.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-tools/file_read.js +403 -0
- package/dist/src/benchmarks/gaia-tools/file_read.js.map +1 -0
- package/dist/src/benchmarks/gaia-tools/grounded_query.d.ts +126 -0
- package/dist/src/benchmarks/gaia-tools/grounded_query.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-tools/grounded_query.js +225 -0
- package/dist/src/benchmarks/gaia-tools/grounded_query.js.map +1 -0
- package/dist/src/benchmarks/gaia-tools/index.d.ts +32 -0
- package/dist/src/benchmarks/gaia-tools/index.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-tools/index.js +36 -0
- package/dist/src/benchmarks/gaia-tools/index.js.map +1 -0
- package/dist/src/benchmarks/gaia-tools/types.d.ts +62 -0
- package/dist/src/benchmarks/gaia-tools/types.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-tools/types.js +12 -0
- package/dist/src/benchmarks/gaia-tools/types.js.map +1 -0
- package/dist/src/benchmarks/gaia-tools/web_search.d.ts +30 -0
- package/dist/src/benchmarks/gaia-tools/web_search.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-tools/web_search.js +210 -0
- package/dist/src/benchmarks/gaia-tools/web_search.js.map +1 -0
- package/dist/src/benchmarks/gaia-voting.d.ts +88 -0
- package/dist/src/benchmarks/gaia-voting.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-voting.js +297 -0
- package/dist/src/benchmarks/gaia-voting.js.map +1 -0
- package/dist/src/benchmarks/gaia-voting.smoke.d.ts +20 -0
- package/dist/src/benchmarks/gaia-voting.smoke.d.ts.map +1 -0
- package/dist/src/benchmarks/gaia-voting.smoke.js +332 -0
- package/dist/src/benchmarks/gaia-voting.smoke.js.map +1 -0
- package/dist/src/commands/gaia-bench.d.ts +40 -0
- package/dist/src/commands/gaia-bench.d.ts.map +1 -0
- package/dist/src/commands/gaia-bench.js +597 -0
- package/dist/src/commands/gaia-bench.js.map +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +2 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +41 -4
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +25 -6
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +9 -4
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +287 -642
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -23,17 +23,23 @@ export function generateStatuslineScript(options) {
|
|
|
23
23
|
const maxAgents = options.runtime.maxAgents;
|
|
24
24
|
return `#!/usr/bin/env node
|
|
25
25
|
/**
|
|
26
|
-
* RuFlo V3 Statusline
|
|
27
|
-
* Displays real-time V3 implementation progress and system status
|
|
26
|
+
* RuFlo V3 Statusline — delegation build (#2195)
|
|
28
27
|
*
|
|
29
|
-
*
|
|
28
|
+
* Fix for ruvnet/ruflo#2195: the previous version re-implemented all data
|
|
29
|
+
* readers locally using fragile file probes that missed AgentDB patterns,
|
|
30
|
+
* the v3/docs/adr/ ADR directory, and the real vector count.
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* This version delegates to 'npx @claude-flow/cli hooks statusline --json'
|
|
33
|
+
* as the single source of truth. That command queries AgentDB directly,
|
|
34
|
+
* counts ADRs in both directories, and reports the real intelligence pct.
|
|
35
|
+
*
|
|
36
|
+
* ADR counting falls back to local file reads so the display still works
|
|
37
|
+
* without network access (counts both v3/docs/adr/ and v3/implementation/adrs/).
|
|
38
|
+
*
|
|
39
|
+
* Cache: JSON result is cached in /tmp for 10s so rapid prompt triggers
|
|
40
|
+
* (every keystroke in some shells) don't hammer the CLI on every call.
|
|
41
|
+
*
|
|
42
|
+
* Usage: node statusline.cjs [--json] [--compact] [--dashboard]
|
|
37
43
|
*/
|
|
38
44
|
|
|
39
45
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
@@ -49,6 +55,120 @@ const CONFIG = {
|
|
|
49
55
|
|
|
50
56
|
const CWD = process.cwd();
|
|
51
57
|
|
|
58
|
+
// ─── Delegation cache ───────────────────────────────────────────
|
|
59
|
+
// Cache the CLI JSON result for 10s so rapid prompt re-renders
|
|
60
|
+
// (e.g. every keypress in some shells) don't re-invoke npx each time.
|
|
61
|
+
const CACHE_FILE = path.join(os.tmpdir(), 'ruflo-statusline-cache-' + require('crypto').createHash('md5').update(CWD).digest('hex').slice(0, 8) + '.json');
|
|
62
|
+
const CACHE_TTL_MS = 10000;
|
|
63
|
+
|
|
64
|
+
function readCache() {
|
|
65
|
+
try {
|
|
66
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
67
|
+
const raw = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
|
|
68
|
+
if (raw && raw._ts && (Date.now() - raw._ts) < CACHE_TTL_MS) {
|
|
69
|
+
return raw.data;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch { /* ignore */ }
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function writeCache(data) {
|
|
77
|
+
try { fs.writeFileSync(CACHE_FILE, JSON.stringify({ _ts: Date.now(), data }), 'utf-8'); } catch { /* ignore */ }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Single source of truth: delegate to the CLI hooks statusline --json command.
|
|
82
|
+
* Falls back to a minimal static object on failure so the statusline still renders.
|
|
83
|
+
*
|
|
84
|
+
* Fix for ruflo#2195: the previous local readers returned 0 for AgentDB patterns
|
|
85
|
+
* (missed the .swarm/memory.db → AgentDB path), computed dddProgress wrong,
|
|
86
|
+
* and only counted ADRs in v3/implementation/adrs/ (missed v3/docs/adr/).
|
|
87
|
+
*/
|
|
88
|
+
function getStatuslineData() {
|
|
89
|
+
const cached = readCache();
|
|
90
|
+
if (cached) return cached;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const raw = execSync(
|
|
94
|
+
'npx --yes @claude-flow/cli@latest hooks statusline --json 2>/dev/null',
|
|
95
|
+
{ encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'], cwd: CWD }
|
|
96
|
+
).trim();
|
|
97
|
+
// The CLI may emit preamble lines before the JSON — find the first '{'.
|
|
98
|
+
const jsonStart = raw.indexOf('{');
|
|
99
|
+
if (jsonStart === -1) throw new Error('no JSON in CLI output');
|
|
100
|
+
const data = JSON.parse(raw.slice(jsonStart));
|
|
101
|
+
// Overlay real ADR count from both local directories (fast, no network).
|
|
102
|
+
data.adrs = getLocalADRCount();
|
|
103
|
+
writeCache(data);
|
|
104
|
+
return data;
|
|
105
|
+
} catch { /* CLI unavailable or timed out */ }
|
|
106
|
+
|
|
107
|
+
// Fallback: use local file probes only (will be less accurate, but non-zero
|
|
108
|
+
// when CLI is available and accurate when it's not).
|
|
109
|
+
return buildLocalFallback();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Count ADRs from BOTH known directories (fix for ruflo#2195: old code missed
|
|
113
|
+
// v3/docs/adr/ which holds ADR-088..ADR-137, i.e. 41 of the 128 total ADRs).
|
|
114
|
+
function getLocalADRCount() {
|
|
115
|
+
const adrDirs = [
|
|
116
|
+
path.join(CWD, 'v3', 'implementation', 'adrs'),
|
|
117
|
+
path.join(CWD, 'v3', 'docs', 'adr'),
|
|
118
|
+
path.join(CWD, 'docs', 'adrs'),
|
|
119
|
+
path.join(CWD, '.claude-flow', 'adrs'),
|
|
120
|
+
];
|
|
121
|
+
let total = 0;
|
|
122
|
+
for (const dir of adrDirs) {
|
|
123
|
+
try {
|
|
124
|
+
if (fs.existsSync(dir)) {
|
|
125
|
+
const files = fs.readdirSync(dir).filter(function(f) {
|
|
126
|
+
return f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\\d{4}-/.test(f));
|
|
127
|
+
});
|
|
128
|
+
total += files.length;
|
|
129
|
+
}
|
|
130
|
+
} catch { /* ignore */ }
|
|
131
|
+
}
|
|
132
|
+
return { count: total, implemented: total, compliance: 0 };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Minimal local fallback when the CLI is not installed or times out.
|
|
136
|
+
// Returns a structure that matches the CLI JSON schema so the renderer works.
|
|
137
|
+
function buildLocalFallback() {
|
|
138
|
+
const memMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
139
|
+
const adrs = getLocalADRCount();
|
|
140
|
+
|
|
141
|
+
// Test file count (pure directory walk, no file reads)
|
|
142
|
+
let testFiles = 0;
|
|
143
|
+
function countTests(dir, depth) {
|
|
144
|
+
if ((depth || 0) > 4) return;
|
|
145
|
+
try {
|
|
146
|
+
if (!fs.existsSync(dir)) return;
|
|
147
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
148
|
+
if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules') {
|
|
149
|
+
countTests(path.join(dir, e.name), (depth || 0) + 1);
|
|
150
|
+
} else if (e.isFile() && (e.name.includes('.test.') || e.name.includes('.spec.') || e.name.startsWith('test_') || e.name.startsWith('spec_'))) {
|
|
151
|
+
testFiles++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch { /* ignore */ }
|
|
155
|
+
}
|
|
156
|
+
for (const d of ['tests', 'test', '__tests__', 'src', 'v3']) countTests(path.join(CWD, d));
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
user: { name: 'user', gitBranch: '', modelName: 'Claude Code' },
|
|
160
|
+
v3Progress: { domainsCompleted: 0, totalDomains: 5, dddProgress: 0, patternsLearned: 0, sessionsCompleted: 0 },
|
|
161
|
+
security: { status: 'NONE', cvesFixed: 0, totalCves: 0 },
|
|
162
|
+
swarm: { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false },
|
|
163
|
+
system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0, subAgents: 0 },
|
|
164
|
+
adrs,
|
|
165
|
+
hooks: { enabled: 0, total: 0 },
|
|
166
|
+
agentdb: { vectorCount: 0, dbSizeKB: 0, hasHnsw: false },
|
|
167
|
+
tests: { testFiles, testCases: testFiles * 4 },
|
|
168
|
+
lastUpdated: new Date().toISOString(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
52
172
|
// ANSI colors
|
|
53
173
|
const c = {
|
|
54
174
|
reset: '\\x1b[0m',
|
|
@@ -70,11 +190,11 @@ const c = {
|
|
|
70
190
|
};
|
|
71
191
|
|
|
72
192
|
// Safe execSync with strict timeout (returns empty string on failure)
|
|
73
|
-
function safeExec(cmd, timeoutMs
|
|
193
|
+
function safeExec(cmd, timeoutMs) {
|
|
74
194
|
try {
|
|
75
195
|
return execSync(cmd, {
|
|
76
196
|
encoding: 'utf-8',
|
|
77
|
-
timeout: timeoutMs,
|
|
197
|
+
timeout: timeoutMs || 2000,
|
|
78
198
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
79
199
|
}).trim();
|
|
80
200
|
} catch {
|
|
@@ -92,34 +212,14 @@ function readJSON(filePath) {
|
|
|
92
212
|
return null;
|
|
93
213
|
}
|
|
94
214
|
|
|
95
|
-
//
|
|
96
|
-
function safeStat(filePath) {
|
|
97
|
-
try {
|
|
98
|
-
return fs.statSync(filePath);
|
|
99
|
-
} catch { /* ignore */ }
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
215
|
+
// ─── Git info (pure-Node / single exec — needed for branch display) ──────────
|
|
102
216
|
|
|
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
217
|
function getGitInfo() {
|
|
117
218
|
const result = {
|
|
118
219
|
name: 'user', gitBranch: '', modified: 0, untracked: 0,
|
|
119
220
|
staged: 0, ahead: 0, behind: 0,
|
|
120
221
|
};
|
|
121
222
|
|
|
122
|
-
// Single shell: get user.name, branch, porcelain status, and upstream diff
|
|
123
223
|
const script = [
|
|
124
224
|
'git config user.name 2>/dev/null || echo user',
|
|
125
225
|
'echo "---SEP---"',
|
|
@@ -133,12 +233,11 @@ function getGitInfo() {
|
|
|
133
233
|
const raw = safeExec("sh -c '" + script + "'", 3000);
|
|
134
234
|
if (!raw) return result;
|
|
135
235
|
|
|
136
|
-
const parts = raw.split('---SEP---').map(s
|
|
236
|
+
const parts = raw.split('---SEP---').map(function(s) { return s.trim(); });
|
|
137
237
|
if (parts.length >= 4) {
|
|
138
238
|
result.name = parts[0] || 'user';
|
|
139
239
|
result.gitBranch = parts[1] || '';
|
|
140
240
|
|
|
141
|
-
// Parse porcelain status
|
|
142
241
|
if (parts[2]) {
|
|
143
242
|
for (const line of parts[2].split('\\n')) {
|
|
144
243
|
if (!line || line.length < 2) continue;
|
|
@@ -149,7 +248,6 @@ function getGitInfo() {
|
|
|
149
248
|
}
|
|
150
249
|
}
|
|
151
250
|
|
|
152
|
-
// Parse ahead/behind
|
|
153
251
|
const ab = (parts[3] || '0 0').split(/\\s+/);
|
|
154
252
|
result.ahead = parseInt(ab[0]) || 0;
|
|
155
253
|
result.behind = parseInt(ab[1]) || 0;
|
|
@@ -198,450 +296,78 @@ function getModelName() {
|
|
|
198
296
|
return 'Claude Code';
|
|
199
297
|
}
|
|
200
298
|
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
if (fs.existsSync(patternStorePath)) {
|
|
210
|
-
const data = JSON.parse(fs.readFileSync(patternStorePath, 'utf-8'));
|
|
211
|
-
if (Array.isArray(data)) patterns = data.length;
|
|
212
|
-
else if (data && data.patterns) patterns = Array.isArray(data.patterns) ? data.patterns.length : Object.keys(data.patterns).length;
|
|
213
|
-
}
|
|
214
|
-
} catch { /* ignore */ }
|
|
215
|
-
|
|
216
|
-
// 2. Count patterns from auto-memory-store (real entries, not file size)
|
|
217
|
-
if (patterns === 0) {
|
|
218
|
-
const autoStorePath = path.join(CWD, '.claude-flow', 'data', 'auto-memory-store.json');
|
|
219
|
-
try {
|
|
220
|
-
if (fs.existsSync(autoStorePath)) {
|
|
221
|
-
const data = JSON.parse(fs.readFileSync(autoStorePath, 'utf-8'));
|
|
222
|
-
if (Array.isArray(data)) patterns = data.length;
|
|
223
|
-
else if (data && data.entries) patterns = data.entries.length;
|
|
224
|
-
}
|
|
225
|
-
} catch { /* ignore */ }
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// 3. Count patterns from memory.db using row count (sqlite header bytes 28-31).
|
|
229
|
-
//
|
|
230
|
-
// ruflo#1989: when encryption at rest is enabled, memory.db is no
|
|
231
|
-
// longer a SQLite database -- it is an RFE1-magicked ciphertext blob.
|
|
232
|
-
// The original code blindly read bytes 28-31 as a page count and
|
|
233
|
-
// rendered 3.3B patterns (uint32 of random ciphertext). That
|
|
234
|
-
// cascaded into fake DDD 5/5 / 100% indicators downstream.
|
|
235
|
-
//
|
|
236
|
-
// Guard with the SQLite magic ("SQLite format 3\\0", 16 bytes at
|
|
237
|
-
// offset 0). Also clamp implausible page counts (>1M pages ~= 4GB)
|
|
238
|
-
// to avoid reporting nonsense even on plaintext SQLite.
|
|
239
|
-
if (patterns === 0) {
|
|
240
|
-
const SQLITE_MAGIC = Buffer.from('SQLite format 3\\0', 'binary');
|
|
241
|
-
const memoryPaths = [
|
|
242
|
-
path.join(CWD, '.claude-flow', 'memory.db'),
|
|
243
|
-
path.join(CWD, 'data', 'memory.db'),
|
|
244
|
-
path.join(CWD, '.swarm', 'memory.db'),
|
|
245
|
-
];
|
|
246
|
-
for (const dbPath of memoryPaths) {
|
|
247
|
-
try {
|
|
248
|
-
if (!fs.existsSync(dbPath)) continue;
|
|
249
|
-
const fd = fs.openSync(dbPath, 'r');
|
|
250
|
-
const head = Buffer.alloc(16);
|
|
251
|
-
fs.readSync(fd, head, 0, 16, 0);
|
|
252
|
-
if (!head.equals(SQLITE_MAGIC)) {
|
|
253
|
-
// Not plaintext SQLite (likely RFE1 encrypted, an empty
|
|
254
|
-
// file, or some other format). Skip — let the daemon or
|
|
255
|
-
// patterns.json fallback report the real number.
|
|
256
|
-
fs.closeSync(fd);
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
const buf = Buffer.alloc(4);
|
|
260
|
-
fs.readSync(fd, buf, 0, 4, 28);
|
|
261
|
-
fs.closeSync(fd);
|
|
262
|
-
const pageCount = buf.readUInt32BE(0);
|
|
263
|
-
// Sanity: reject implausible counts (> 1M pages ≈ 4 GB DB).
|
|
264
|
-
if (pageCount > 1_000_000) continue;
|
|
265
|
-
// Each page typically holds ~10-50 rows; use page count as
|
|
266
|
-
// conservative estimate. Report 0 if only schema pages (< 3).
|
|
267
|
-
patterns = pageCount > 2 ? pageCount - 2 : 0;
|
|
268
|
-
break;
|
|
269
|
-
} catch { /* ignore */ }
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 4. Count real session files
|
|
299
|
+
// ─── Stdin reader (Claude Code pipes session JSON) ──────────────
|
|
300
|
+
// Claude Code sends session JSON via stdin. Read synchronously so the
|
|
301
|
+
// script works both when invoked by Claude Code (stdin has JSON) and
|
|
302
|
+
// when run manually from terminal (stdin is empty/tty).
|
|
303
|
+
let _stdinData = null;
|
|
304
|
+
function getStdinData() {
|
|
305
|
+
if (_stdinData !== undefined && _stdinData !== null) return _stdinData;
|
|
274
306
|
try {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
} catch { /* ignore */ }
|
|
280
|
-
|
|
281
|
-
// 5. Count session files from claude-flow
|
|
282
|
-
if (sessions === 0) {
|
|
307
|
+
if (process.stdin.isTTY) { _stdinData = null; return null; }
|
|
308
|
+
const chunks = [];
|
|
309
|
+
const buf = Buffer.alloc(4096);
|
|
310
|
+
let bytesRead;
|
|
283
311
|
try {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
sessions = fs.readdirSync(cfSessDir).filter(f => f.endsWith('.json')).length;
|
|
312
|
+
while ((bytesRead = fs.readSync(0, buf, 0, buf.length, null)) > 0) {
|
|
313
|
+
chunks.push(buf.slice(0, bytesRead));
|
|
287
314
|
}
|
|
288
|
-
} catch { /*
|
|
315
|
+
} catch { /* EOF or read error */ }
|
|
316
|
+
const raw = Buffer.concat(chunks).toString('utf-8').trim();
|
|
317
|
+
_stdinData = (raw && raw.startsWith('{')) ? JSON.parse(raw) : null;
|
|
318
|
+
} catch {
|
|
319
|
+
_stdinData = null;
|
|
289
320
|
}
|
|
321
|
+
return _stdinData;
|
|
322
|
+
}
|
|
290
323
|
|
|
291
|
-
|
|
324
|
+
function getModelFromStdin() {
|
|
325
|
+
const data = getStdinData();
|
|
326
|
+
return (data && data.model && data.model.display_name) ? data.model.display_name : null;
|
|
292
327
|
}
|
|
293
328
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const dddData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'ddd-progress.json'));
|
|
300
|
-
let dddProgress = dddData ? (dddData.progress || 0) : 0;
|
|
301
|
-
let domainsCompleted = Math.min(5, Math.floor(dddProgress / 20));
|
|
302
|
-
|
|
303
|
-
// Only derive DDD progress from real ddd-progress.json or real pattern data
|
|
304
|
-
// Don't inflate domains from pattern count — 0 means no DDD work tracked.
|
|
305
|
-
// ruflo#1989: defensively clamp learning.patterns even though
|
|
306
|
-
// getLearningStats already guards against the RFE1-encrypted case --
|
|
307
|
-
// if any future regression in the upstream reader returns a wild
|
|
308
|
-
// value, we do not want to silently inflate DDD to 5/5 / 100%.
|
|
309
|
-
const realPatterns = Number.isFinite(learning.patterns) && learning.patterns >= 0 && learning.patterns < 1_000_000
|
|
310
|
-
? learning.patterns
|
|
311
|
-
: 0;
|
|
312
|
-
if (dddProgress === 0 && realPatterns > 0) {
|
|
313
|
-
// Conservative: only count domains if we have substantial real pattern data
|
|
314
|
-
// Each domain requires ~100 real stored patterns to claim completion
|
|
315
|
-
domainsCompleted = Math.min(5, Math.floor(realPatterns / 100));
|
|
316
|
-
dddProgress = Math.floor((domainsCompleted / totalDomains) * 100);
|
|
329
|
+
function getContextFromStdin() {
|
|
330
|
+
const data = getStdinData();
|
|
331
|
+
if (data && data.context_window) {
|
|
332
|
+
return { usedPct: Math.floor(data.context_window.used_percentage || 0) };
|
|
317
333
|
}
|
|
318
|
-
|
|
319
|
-
return {
|
|
320
|
-
domainsCompleted, totalDomains, dddProgress,
|
|
321
|
-
patternsLearned: realPatterns,
|
|
322
|
-
sessionsCompleted: learning.sessions,
|
|
323
|
-
};
|
|
334
|
+
return null;
|
|
324
335
|
}
|
|
325
336
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
return { status: 'PENDING', cvesFixed: 0, totalCves: 0 };
|
|
333
|
-
}
|
|
334
|
-
const auditAge = Date.now() - new Date(auditDate).getTime();
|
|
335
|
-
const isStale = auditAge > 7 * 24 * 60 * 60 * 1000;
|
|
337
|
+
function getCostFromStdin() {
|
|
338
|
+
const data = getStdinData();
|
|
339
|
+
if (data && data.cost) {
|
|
340
|
+
const durationMs = data.cost.total_duration_ms || 0;
|
|
341
|
+
const mins = Math.floor(durationMs / 60000);
|
|
342
|
+
const secs = Math.floor((durationMs % 60000) / 1000);
|
|
336
343
|
return {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
totalCves: auditData.totalCves || 0,
|
|
344
|
+
costUsd: data.cost.total_cost_usd || 0,
|
|
345
|
+
duration: mins > 0 ? mins + 'm' + secs + 's' : secs + 's',
|
|
340
346
|
};
|
|
341
347
|
}
|
|
342
|
-
|
|
343
|
-
let scanCount = 0;
|
|
344
|
-
try {
|
|
345
|
-
const scanDir = path.join(CWD, '.claude', 'security-scans');
|
|
346
|
-
if (fs.existsSync(scanDir)) {
|
|
347
|
-
scanCount = fs.readdirSync(scanDir).filter(f => f.endsWith('.json')).length;
|
|
348
|
-
}
|
|
349
|
-
} catch { /* ignore */ }
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
status: scanCount > 0 ? 'SCANNED' : 'NONE',
|
|
353
|
-
cvesFixed: 0,
|
|
354
|
-
totalCves: 0,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Swarm status (pure file reads, NO ps aux)
|
|
359
|
-
function getSwarmStatus() {
|
|
360
|
-
const staleThresholdMs = 5 * 60 * 1000;
|
|
361
|
-
const now = Date.now();
|
|
362
|
-
|
|
363
|
-
const swarmStatePath = path.join(CWD, '.claude-flow', 'swarm', 'swarm-state.json');
|
|
364
|
-
const swarmState = readJSON(swarmStatePath);
|
|
365
|
-
if (swarmState) {
|
|
366
|
-
const updatedAt = swarmState.updatedAt || swarmState.startedAt;
|
|
367
|
-
const age = updatedAt ? now - new Date(updatedAt).getTime() : Infinity;
|
|
368
|
-
if (age < staleThresholdMs) {
|
|
369
|
-
return {
|
|
370
|
-
activeAgents: (swarmState.agents && swarmState.agents.length) || swarmState.agentCount || 0,
|
|
371
|
-
maxAgents: swarmState.maxAgents || CONFIG.maxAgents,
|
|
372
|
-
coordinationActive: true,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
|
|
378
|
-
if (activityData && activityData.swarm) {
|
|
379
|
-
const updatedAt = activityData.timestamp || (activityData.swarm && activityData.swarm.timestamp);
|
|
380
|
-
const age = updatedAt ? now - new Date(updatedAt).getTime() : Infinity;
|
|
381
|
-
if (age < staleThresholdMs) {
|
|
382
|
-
return {
|
|
383
|
-
activeAgents: activityData.swarm.agent_count || 0,
|
|
384
|
-
maxAgents: CONFIG.maxAgents,
|
|
385
|
-
coordinationActive: activityData.swarm.coordination_active || activityData.swarm.active || false,
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false };
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// System metrics (uses process.memoryUsage() — no shell spawn)
|
|
394
|
-
function getSystemMetrics() {
|
|
395
|
-
const memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
396
|
-
const learning = getLearningStats();
|
|
397
|
-
const agentdb = getAgentDBStats();
|
|
398
|
-
|
|
399
|
-
// Intelligence from learning.json
|
|
400
|
-
const learningData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'learning.json'));
|
|
401
|
-
let intelligencePct = 0;
|
|
402
|
-
let contextPct = 0;
|
|
403
|
-
|
|
404
|
-
if (learningData && learningData.intelligence && learningData.intelligence.score !== undefined) {
|
|
405
|
-
intelligencePct = Math.min(100, Math.floor(learningData.intelligence.score));
|
|
406
|
-
} else {
|
|
407
|
-
// Use real data only — patterns from actual store, vectors from actual DB.
|
|
408
|
-
// ruflo#1989: clamp patterns to a sane upper bound. A multi-billion
|
|
409
|
-
// pattern count from a buggy reader would saturate intelligencePct
|
|
410
|
-
// to 100% and silently lie about progress.
|
|
411
|
-
const realPatterns = Number.isFinite(learning.patterns) && learning.patterns >= 0 && learning.patterns < 1_000_000
|
|
412
|
-
? learning.patterns
|
|
413
|
-
: 0;
|
|
414
|
-
const fromPatterns = realPatterns > 0 ? Math.min(100, Math.floor(realPatterns / 20)) : 0;
|
|
415
|
-
const fromVectors = agentdb.vectorCount > 0 ? Math.min(100, Math.floor(agentdb.vectorCount / 20)) : 0;
|
|
416
|
-
intelligencePct = Math.max(fromPatterns, fromVectors);
|
|
417
|
-
}
|
|
418
|
-
// No fake fallback — 0% means no real learning data exists
|
|
419
|
-
|
|
420
|
-
if (learningData && learningData.sessions && learningData.sessions.total !== undefined) {
|
|
421
|
-
contextPct = Math.min(100, learningData.sessions.total * 5);
|
|
422
|
-
} else {
|
|
423
|
-
// Real session count only — no heuristic derivation from patterns
|
|
424
|
-
contextPct = learning.sessions > 0 ? Math.min(100, learning.sessions * 5) : 0;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Sub-agents from file metrics (no ps aux)
|
|
428
|
-
let subAgents = 0;
|
|
429
|
-
const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
|
|
430
|
-
if (activityData && activityData.processes && activityData.processes.estimated_agents) {
|
|
431
|
-
subAgents = activityData.processes.estimated_agents;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return { memoryMB, contextPct, intelligencePct, subAgents };
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// ADR status (count files only — don't read contents)
|
|
438
|
-
function getADRStatus() {
|
|
439
|
-
// Count actual ADR files first — compliance JSON may be stale
|
|
440
|
-
const adrPaths = [
|
|
441
|
-
path.join(CWD, 'v3', 'implementation', 'adrs'),
|
|
442
|
-
path.join(CWD, 'docs', 'adrs'),
|
|
443
|
-
path.join(CWD, '.claude-flow', 'adrs'),
|
|
444
|
-
];
|
|
445
|
-
|
|
446
|
-
for (const adrPath of adrPaths) {
|
|
447
|
-
try {
|
|
448
|
-
if (fs.existsSync(adrPath)) {
|
|
449
|
-
const files = fs.readdirSync(adrPath).filter(f =>
|
|
450
|
-
f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\\d{4}-/.test(f))
|
|
451
|
-
);
|
|
452
|
-
return { count: files.length, implemented: files.length, compliance: 0 };
|
|
453
|
-
}
|
|
454
|
-
} catch { /* ignore */ }
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return { count: 0, implemented: 0, compliance: 0 };
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Hooks status (shared settings cache)
|
|
461
|
-
function getHooksStatus() {
|
|
462
|
-
let enabled = 0;
|
|
463
|
-
let total = 0;
|
|
464
|
-
const settings = getSettings();
|
|
465
|
-
|
|
466
|
-
if (settings && settings.hooks) {
|
|
467
|
-
for (const category of Object.keys(settings.hooks)) {
|
|
468
|
-
const matchers = settings.hooks[category];
|
|
469
|
-
if (!Array.isArray(matchers)) continue;
|
|
470
|
-
for (const matcher of matchers) {
|
|
471
|
-
const hooks = matcher && matcher.hooks;
|
|
472
|
-
if (Array.isArray(hooks)) {
|
|
473
|
-
total += hooks.length;
|
|
474
|
-
enabled += hooks.length;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
try {
|
|
481
|
-
const hooksDir = path.join(CWD, '.claude', 'hooks');
|
|
482
|
-
if (fs.existsSync(hooksDir)) {
|
|
483
|
-
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh')).length;
|
|
484
|
-
total = Math.max(total, hookFiles);
|
|
485
|
-
enabled = Math.max(enabled, hookFiles);
|
|
486
|
-
}
|
|
487
|
-
} catch { /* ignore */ }
|
|
488
|
-
|
|
489
|
-
return { enabled, total };
|
|
348
|
+
return null;
|
|
490
349
|
}
|
|
491
350
|
|
|
492
|
-
//
|
|
493
|
-
function
|
|
494
|
-
let
|
|
495
|
-
let dbSizeKB = 0;
|
|
496
|
-
let namespaces = 0;
|
|
497
|
-
let hasHnsw = false;
|
|
498
|
-
|
|
499
|
-
// 1. Count real entries from auto-memory-store.json
|
|
500
|
-
const storePath = path.join(CWD, '.claude-flow', 'data', 'auto-memory-store.json');
|
|
501
|
-
const storeStat = safeStat(storePath);
|
|
502
|
-
if (storeStat) {
|
|
503
|
-
dbSizeKB += storeStat.size / 1024;
|
|
504
|
-
try {
|
|
505
|
-
const store = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
|
|
506
|
-
if (Array.isArray(store)) vectorCount += store.length;
|
|
507
|
-
else if (store && store.entries) vectorCount += store.entries.length;
|
|
508
|
-
} catch { /* fall back */ }
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// 2. Count entries from hooks memory store (.claude-flow/memory/store.json)
|
|
512
|
-
const hooksStorePath = path.join(CWD, '.claude-flow', 'memory', 'store.json');
|
|
513
|
-
const hooksStoreStat = safeStat(hooksStorePath);
|
|
514
|
-
if (hooksStoreStat) {
|
|
515
|
-
dbSizeKB += hooksStoreStat.size / 1024;
|
|
516
|
-
try {
|
|
517
|
-
const store = JSON.parse(fs.readFileSync(hooksStorePath, 'utf-8'));
|
|
518
|
-
if (store && store.entries) {
|
|
519
|
-
const entryCount = Object.keys(store.entries).length;
|
|
520
|
-
vectorCount = Math.max(vectorCount, entryCount);
|
|
521
|
-
if (entryCount > 0) namespaces++;
|
|
522
|
-
}
|
|
523
|
-
} catch { /* fall back */ }
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// 3. Count entries from ranked-context.json
|
|
351
|
+
// Read package version from the first package.json we find.
|
|
352
|
+
function getPkgVersion() {
|
|
353
|
+
let ver = '3.6';
|
|
527
354
|
try {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
path.join(CWD, 'data', 'memory.db'),
|
|
535
|
-
path.join(CWD, '.claude-flow', 'memory.db'),
|
|
536
|
-
path.join(CWD, '.swarm', 'memory.db'),
|
|
537
|
-
];
|
|
538
|
-
for (const f of dbFiles) {
|
|
539
|
-
const stat = safeStat(f);
|
|
540
|
-
if (stat) {
|
|
541
|
-
dbSizeKB += stat.size / 1024;
|
|
542
|
-
namespaces++;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// 4. Graph data size
|
|
547
|
-
const graphStat = safeStat(path.join(CWD, 'data', 'memory.graph'));
|
|
548
|
-
if (graphStat) dbSizeKB += graphStat.size / 1024;
|
|
549
|
-
|
|
550
|
-
// 5. HNSW index or memory package
|
|
551
|
-
const hnswPaths = [
|
|
552
|
-
path.join(CWD, '.swarm', 'hnsw.index'),
|
|
553
|
-
path.join(CWD, '.claude-flow', 'hnsw.index'),
|
|
554
|
-
];
|
|
555
|
-
for (const p of hnswPaths) {
|
|
556
|
-
if (safeStat(p)) { hasHnsw = true; break; }
|
|
557
|
-
}
|
|
558
|
-
if (!hasHnsw) {
|
|
559
|
-
const memPkgPaths = [
|
|
560
|
-
path.join(CWD, 'v3', '@claude-flow', 'memory', 'dist'),
|
|
561
|
-
path.join(CWD, 'node_modules', '@claude-flow', 'memory'),
|
|
355
|
+
const home = os.homedir();
|
|
356
|
+
const pkgPaths = [
|
|
357
|
+
path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'package.json'),
|
|
358
|
+
path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'package.json'),
|
|
359
|
+
path.join(CWD, 'node_modules', 'ruflo', 'package.json'),
|
|
360
|
+
path.join(CWD, 'v3', '@claude-flow', 'cli', 'package.json'),
|
|
562
361
|
];
|
|
563
|
-
for (const p of
|
|
564
|
-
if (fs.existsSync(p))
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// Test stats (count files only — NO reading file contents)
|
|
572
|
-
function getTestStats() {
|
|
573
|
-
let testFiles = 0;
|
|
574
|
-
|
|
575
|
-
function countTestFiles(dir, depth) {
|
|
576
|
-
if (depth === undefined) depth = 0;
|
|
577
|
-
if (depth > 6) return;
|
|
578
|
-
try {
|
|
579
|
-
if (!fs.existsSync(dir)) return;
|
|
580
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
581
|
-
for (const entry of entries) {
|
|
582
|
-
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
583
|
-
countTestFiles(path.join(dir, entry.name), depth + 1);
|
|
584
|
-
} else if (entry.isFile()) {
|
|
585
|
-
const n = entry.name;
|
|
586
|
-
if (n.includes('.test.') || n.includes('.spec.') || n.includes('_test.') || n.includes('_spec.')) {
|
|
587
|
-
testFiles++;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
} catch { /* ignore */ }
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
var testDirNames = ['tests', 'test', '__tests__', 'src', 'v3'];
|
|
595
|
-
for (var i = 0; i < testDirNames.length; i++) {
|
|
596
|
-
countTestFiles(path.join(CWD, testDirNames[i]));
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return { testFiles, testCases: testFiles * 4 };
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Integration status (shared settings + file checks)
|
|
603
|
-
function getIntegrationStatus() {
|
|
604
|
-
const mcpServers = { total: 0, enabled: 0 };
|
|
605
|
-
const settings = getSettings();
|
|
606
|
-
|
|
607
|
-
if (settings && settings.mcpServers && typeof settings.mcpServers === 'object') {
|
|
608
|
-
const servers = Object.keys(settings.mcpServers);
|
|
609
|
-
mcpServers.total = servers.length;
|
|
610
|
-
mcpServers.enabled = settings.enabledMcpjsonServers
|
|
611
|
-
? settings.enabledMcpjsonServers.filter(s => servers.includes(s)).length
|
|
612
|
-
: servers.length;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
if (mcpServers.total === 0) {
|
|
616
|
-
const mcpConfig = readJSON(path.join(CWD, '.mcp.json'))
|
|
617
|
-
|| readJSON(path.join(os.homedir(), '.claude', 'mcp.json'));
|
|
618
|
-
if (mcpConfig && mcpConfig.mcpServers) {
|
|
619
|
-
const s = Object.keys(mcpConfig.mcpServers);
|
|
620
|
-
mcpServers.total = s.length;
|
|
621
|
-
mcpServers.enabled = s.length;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
const hasDatabase = ['.swarm/memory.db', '.claude-flow/memory.db', 'data/memory.db']
|
|
626
|
-
.some(p => fs.existsSync(path.join(CWD, p)));
|
|
627
|
-
const hasApi = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
|
|
628
|
-
|
|
629
|
-
return { mcpServers, hasDatabase, hasApi };
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Session stats (pure file reads)
|
|
633
|
-
function getSessionStats() {
|
|
634
|
-
var sessionPaths = ['.claude-flow/session.json', '.claude/session.json'];
|
|
635
|
-
for (var i = 0; i < sessionPaths.length; i++) {
|
|
636
|
-
const data = readJSON(path.join(CWD, sessionPaths[i]));
|
|
637
|
-
if (data && data.startTime) {
|
|
638
|
-
const diffMs = Date.now() - new Date(data.startTime).getTime();
|
|
639
|
-
const mins = Math.floor(diffMs / 60000);
|
|
640
|
-
const duration = mins < 60 ? mins + 'm' : Math.floor(mins / 60) + 'h' + (mins % 60) + 'm';
|
|
641
|
-
return { duration: duration };
|
|
362
|
+
for (const p of pkgPaths) {
|
|
363
|
+
if (!fs.existsSync(p)) continue;
|
|
364
|
+
try {
|
|
365
|
+
const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
366
|
+
if (pkg && typeof pkg.version === 'string' && pkg.version.length > 0) { ver = pkg.version; break; }
|
|
367
|
+
} catch { /* ignore */ }
|
|
642
368
|
}
|
|
643
|
-
}
|
|
644
|
-
return
|
|
369
|
+
} catch { /* ignore */ }
|
|
370
|
+
return ver;
|
|
645
371
|
}
|
|
646
372
|
|
|
647
373
|
// ─── Rendering ──────────────────────────────────────────────────
|
|
@@ -649,59 +375,56 @@ function getSessionStats() {
|
|
|
649
375
|
function progressBar(current, total) {
|
|
650
376
|
const width = 5;
|
|
651
377
|
const filled = Math.round((current / total) * width);
|
|
652
|
-
return '[' + '
|
|
378
|
+
return '[' + '●'.repeat(filled) + '○'.repeat(width - filled) + ']';
|
|
653
379
|
}
|
|
654
380
|
|
|
655
381
|
function generateStatusline() {
|
|
382
|
+
const d = getStatuslineData();
|
|
656
383
|
const git = getGitInfo();
|
|
657
|
-
|
|
658
|
-
const modelName = getModelFromStdin() || getModelName();
|
|
384
|
+
const modelName = getModelFromStdin() || (d.user && d.user.modelName) || 'Claude Code';
|
|
659
385
|
const ctxInfo = getContextFromStdin();
|
|
660
386
|
const costInfo = getCostFromStdin();
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
const
|
|
664
|
-
const
|
|
665
|
-
const
|
|
666
|
-
const
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
const
|
|
670
|
-
const
|
|
387
|
+
const pkgVersion = getPkgVersion();
|
|
388
|
+
|
|
389
|
+
const progress = d.v3Progress || {};
|
|
390
|
+
const security = d.security || {};
|
|
391
|
+
const swarm = d.swarm || {};
|
|
392
|
+
const system = d.system || {};
|
|
393
|
+
const adrs = d.adrs || {};
|
|
394
|
+
const hooks = d.hooks || {};
|
|
395
|
+
const agentdb = d.agentdb || {};
|
|
396
|
+
const tests = d.tests || {};
|
|
397
|
+
|
|
398
|
+
const domainsCompleted = progress.domainsCompleted || 0;
|
|
399
|
+
const totalDomains = progress.totalDomains || 5;
|
|
400
|
+
const dddProgress = progress.dddProgress || 0;
|
|
401
|
+
const patternsLearned = progress.patternsLearned || 0;
|
|
402
|
+
const activeAgents = swarm.activeAgents || 0;
|
|
403
|
+
const maxAgents = swarm.maxAgents || CONFIG.maxAgents;
|
|
404
|
+
const coordinationActive = swarm.coordinationActive || false;
|
|
405
|
+
const intelligencePct = system.intelligencePct || 0;
|
|
406
|
+
const memoryMB = system.memoryMB || 0;
|
|
407
|
+
const subAgents = system.subAgents || 0;
|
|
408
|
+
const cvesFixed = security.cvesFixed || 0;
|
|
409
|
+
const totalCves = security.totalCves || 0;
|
|
410
|
+
const secStatus = security.status || 'NONE';
|
|
411
|
+
const adrCount = adrs.count || 0;
|
|
412
|
+
const adrImpl = adrs.implemented || 0;
|
|
413
|
+
const hooksEnabled = hooks.enabled || 0;
|
|
414
|
+
const hooksTotal = hooks.total || 0;
|
|
415
|
+
const vectorCount = agentdb.vectorCount || 0;
|
|
416
|
+
const hasHnsw = agentdb.hasHnsw || false;
|
|
417
|
+
const dbSizeKB = agentdb.dbSizeKB || 0;
|
|
418
|
+
const testFiles = tests.testFiles || 0;
|
|
419
|
+
const testCases = tests.testCases || testFiles * 4;
|
|
420
|
+
|
|
671
421
|
const lines = [];
|
|
672
422
|
|
|
673
|
-
// Header
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
// users saw the hard-coded fallback (V3.5) even on newer alphas (#1951).
|
|
677
|
-
let pkgVersion = '3.6';
|
|
678
|
-
try {
|
|
679
|
-
const home = require('os').homedir();
|
|
680
|
-
const pkgPaths = [
|
|
681
|
-
// 1. The plugin's own root (installed via /plugin install).
|
|
682
|
-
path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'package.json'),
|
|
683
|
-
// 2. Project-local @claude-flow/cli — npm-style install.
|
|
684
|
-
path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'package.json'),
|
|
685
|
-
// 3. Project-local ruflo umbrella.
|
|
686
|
-
path.join(CWD, 'node_modules', 'ruflo', 'package.json'),
|
|
687
|
-
// 4. Source-checkout location (when developing in this repo).
|
|
688
|
-
path.join(CWD, 'v3', '@claude-flow', 'cli', 'package.json'),
|
|
689
|
-
];
|
|
690
|
-
for (const p of pkgPaths) {
|
|
691
|
-
if (!fs.existsSync(p)) continue;
|
|
692
|
-
try {
|
|
693
|
-
const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
694
|
-
if (pkg && typeof pkg.version === 'string' && pkg.version.length > 0) {
|
|
695
|
-
pkgVersion = pkg.version;
|
|
696
|
-
break;
|
|
697
|
-
}
|
|
698
|
-
} catch { /* malformed package.json — try next */ }
|
|
699
|
-
}
|
|
700
|
-
} catch { /* fall through to the hardcoded default */ }
|
|
701
|
-
let header = c.bold + c.brightPurple + '\\u258A RuFlo V' + pkgVersion + ' ' + c.reset;
|
|
702
|
-
header += (swarm.coordinationActive ? c.brightCyan : c.dim) + '\\u25CF ' + c.brightCyan + git.name + c.reset;
|
|
423
|
+
// Header
|
|
424
|
+
let header = c.bold + c.brightPurple + '▊ RuFlo V' + pkgVersion + ' ' + c.reset;
|
|
425
|
+
header += (coordinationActive ? c.brightCyan : c.dim) + '● ' + c.brightCyan + git.name + c.reset;
|
|
703
426
|
if (git.gitBranch) {
|
|
704
|
-
header += ' ' + c.dim + '
|
|
427
|
+
header += ' ' + c.dim + '│' + c.reset + ' ' + c.brightBlue + '⏇ ' + git.gitBranch + c.reset;
|
|
705
428
|
const changes = git.modified + git.staged + git.untracked;
|
|
706
429
|
if (changes > 0) {
|
|
707
430
|
let ind = '';
|
|
@@ -710,185 +433,107 @@ function generateStatusline() {
|
|
|
710
433
|
if (git.untracked > 0) ind += c.dim + '?' + git.untracked + c.reset;
|
|
711
434
|
header += ' ' + ind;
|
|
712
435
|
}
|
|
713
|
-
if (git.ahead > 0) header += ' ' + c.brightGreen + '
|
|
714
|
-
if (git.behind > 0) header += ' ' + c.brightRed + '
|
|
436
|
+
if (git.ahead > 0) header += ' ' + c.brightGreen + '↑' + git.ahead + c.reset;
|
|
437
|
+
if (git.behind > 0) header += ' ' + c.brightRed + '↓' + git.behind + c.reset;
|
|
715
438
|
}
|
|
716
|
-
header += ' ' + c.dim + '
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (duration) header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.cyan + '\\u23F1 ' + duration + c.reset;
|
|
720
|
-
// Show context usage from Claude Code stdin if available
|
|
439
|
+
header += ' ' + c.dim + '│' + c.reset + ' ' + c.purple + modelName + c.reset;
|
|
440
|
+
const duration = costInfo ? costInfo.duration : '';
|
|
441
|
+
if (duration) header += ' ' + c.dim + '│' + c.reset + ' ' + c.cyan + '⏱ ' + duration + c.reset;
|
|
721
442
|
if (ctxInfo && ctxInfo.usedPct > 0) {
|
|
722
443
|
const ctxColor = ctxInfo.usedPct >= 90 ? c.brightRed : ctxInfo.usedPct >= 70 ? c.brightYellow : c.brightGreen;
|
|
723
|
-
header += ' ' + c.dim + '
|
|
444
|
+
header += ' ' + c.dim + '│' + c.reset + ' ' + ctxColor + '● ' + ctxInfo.usedPct + '% ctx' + c.reset;
|
|
724
445
|
}
|
|
725
|
-
// Show cost from Claude Code stdin if available
|
|
726
446
|
if (costInfo && costInfo.costUsd > 0) {
|
|
727
|
-
header += ' ' + c.dim + '
|
|
447
|
+
header += ' ' + c.dim + '│' + c.reset + ' ' + c.brightYellow + '$' + costInfo.costUsd.toFixed(2) + c.reset;
|
|
728
448
|
}
|
|
729
449
|
lines.push(header);
|
|
730
450
|
|
|
731
451
|
// Separator
|
|
732
|
-
lines.push(c.dim + '
|
|
452
|
+
lines.push(c.dim + '─'.repeat(53) + c.reset);
|
|
733
453
|
|
|
734
454
|
// Line 1: DDD Domains
|
|
735
|
-
const domainsColor =
|
|
455
|
+
const domainsColor = domainsCompleted >= 3 ? c.brightGreen : domainsCompleted > 0 ? c.yellow : c.red;
|
|
736
456
|
let perfIndicator;
|
|
737
|
-
if (
|
|
738
|
-
const speedup =
|
|
739
|
-
perfIndicator = c.brightGreen + '
|
|
740
|
-
} else if (
|
|
741
|
-
const pk =
|
|
742
|
-
perfIndicator = c.brightYellow + '
|
|
457
|
+
if (hasHnsw && vectorCount > 0) {
|
|
458
|
+
const speedup = vectorCount > 10000 ? '12500x' : vectorCount > 1000 ? '150x' : '10x';
|
|
459
|
+
perfIndicator = c.brightGreen + '⚡ HNSW ' + speedup + c.reset;
|
|
460
|
+
} else if (patternsLearned > 0) {
|
|
461
|
+
const pk = patternsLearned >= 1000 ? (patternsLearned / 1000).toFixed(1) + 'k' : String(patternsLearned);
|
|
462
|
+
perfIndicator = c.brightYellow + '📚 ' + pk + ' patterns' + c.reset;
|
|
743
463
|
} else {
|
|
744
|
-
perfIndicator = c.dim + '
|
|
464
|
+
perfIndicator = c.dim + '⚡ target: 150x-12500x' + c.reset;
|
|
745
465
|
}
|
|
746
466
|
lines.push(
|
|
747
|
-
c.brightCyan + '
|
|
748
|
-
domainsColor +
|
|
467
|
+
c.brightCyan + '🏗️ DDD Domains' + c.reset + ' ' + progressBar(domainsCompleted, totalDomains) + ' ' +
|
|
468
|
+
domainsColor + domainsCompleted + c.reset + '/' + c.brightWhite + totalDomains + c.reset + ' ' + perfIndicator
|
|
749
469
|
);
|
|
750
470
|
|
|
751
471
|
// Line 2: Swarm + Hooks + CVE + Memory + Intelligence
|
|
752
|
-
const swarmInd =
|
|
753
|
-
const agentsColor =
|
|
754
|
-
const secIcon =
|
|
755
|
-
const secColor =
|
|
756
|
-
const hooksColor =
|
|
757
|
-
const intellColor =
|
|
472
|
+
const swarmInd = coordinationActive ? c.brightGreen + '◉' + c.reset : c.dim + '○' + c.reset;
|
|
473
|
+
const agentsColor = activeAgents > 0 ? c.brightGreen : c.red;
|
|
474
|
+
const secIcon = secStatus === 'CLEAN' ? '🟢' : (secStatus === 'IN_PROGRESS' || secStatus === 'STALE') ? '🟡' : (secStatus === 'NONE' ? '⚪' : '🔴');
|
|
475
|
+
const secColor = secStatus === 'CLEAN' ? c.brightGreen : (secStatus === 'IN_PROGRESS' || secStatus === 'STALE') ? c.brightYellow : (secStatus === 'NONE' ? c.dim : c.brightRed);
|
|
476
|
+
const hooksColor = hooksEnabled > 0 ? c.brightGreen : c.dim;
|
|
477
|
+
const intellColor = intelligencePct >= 80 ? c.brightGreen : intelligencePct >= 40 ? c.brightYellow : c.dim;
|
|
758
478
|
|
|
759
479
|
lines.push(
|
|
760
|
-
c.brightYellow + '
|
|
761
|
-
c.brightPurple + '
|
|
762
|
-
c.brightBlue + '
|
|
763
|
-
secIcon + ' ' + secColor + 'CVE ' +
|
|
764
|
-
c.brightCyan + '
|
|
765
|
-
intellColor + '
|
|
480
|
+
c.brightYellow + '🤖 Swarm' + c.reset + ' ' + swarmInd + ' [' + agentsColor + String(activeAgents).padStart(2) + c.reset + '/' + c.brightWhite + maxAgents + c.reset + '] ' +
|
|
481
|
+
c.brightPurple + '👥 ' + subAgents + c.reset + ' ' +
|
|
482
|
+
c.brightBlue + '🪝 ' + hooksColor + hooksEnabled + c.reset + '/' + c.brightWhite + hooksTotal + c.reset + ' ' +
|
|
483
|
+
secIcon + ' ' + secColor + 'CVE ' + cvesFixed + c.reset + '/' + c.brightWhite + totalCves + c.reset + ' ' +
|
|
484
|
+
c.brightCyan + '💾 ' + memoryMB + 'MB' + c.reset + ' ' +
|
|
485
|
+
intellColor + '🧠 ' + String(intelligencePct).padStart(3) + '%' + c.reset
|
|
766
486
|
);
|
|
767
487
|
|
|
768
488
|
// Line 3: Architecture
|
|
769
|
-
const dddColor =
|
|
770
|
-
const adrColor =
|
|
771
|
-
const adrDisplay =
|
|
489
|
+
const dddColor = dddProgress >= 50 ? c.brightGreen : dddProgress > 0 ? c.yellow : c.red;
|
|
490
|
+
const adrColor = adrCount > 0 ? (adrImpl === adrCount ? c.brightGreen : c.yellow) : c.dim;
|
|
491
|
+
const adrDisplay = adrColor + '●' + adrImpl + '/' + adrCount + c.reset;
|
|
772
492
|
|
|
773
493
|
lines.push(
|
|
774
|
-
c.brightPurple + '
|
|
775
|
-
c.cyan + 'ADRs' + c.reset + ' ' + adrDisplay + ' ' + c.dim + '
|
|
776
|
-
c.cyan + 'DDD' + c.reset + ' ' + dddColor + '
|
|
777
|
-
c.cyan + 'Security' + c.reset + ' ' + secColor + '
|
|
494
|
+
c.brightPurple + '🔧 Architecture' + c.reset + ' ' +
|
|
495
|
+
c.cyan + 'ADRs' + c.reset + ' ' + adrDisplay + ' ' + c.dim + '│' + c.reset + ' ' +
|
|
496
|
+
c.cyan + 'DDD' + c.reset + ' ' + dddColor + '●' + String(dddProgress).padStart(3) + '%' + c.reset + ' ' + c.dim + '│' + c.reset + ' ' +
|
|
497
|
+
c.cyan + 'Security' + c.reset + ' ' + secColor + '●' + secStatus + c.reset
|
|
778
498
|
);
|
|
779
499
|
|
|
780
500
|
// Line 4: AgentDB, Tests, Integration
|
|
781
|
-
const hnswInd =
|
|
782
|
-
const sizeDisp =
|
|
783
|
-
const vectorColor =
|
|
784
|
-
const testColor =
|
|
785
|
-
|
|
501
|
+
const hnswInd = hasHnsw ? c.brightGreen + '⚡' + c.reset : '';
|
|
502
|
+
const sizeDisp = dbSizeKB >= 1024 ? (dbSizeKB / 1024).toFixed(1) + 'MB' : dbSizeKB + 'KB';
|
|
503
|
+
const vectorColor = vectorCount > 0 ? c.brightGreen : c.dim;
|
|
504
|
+
const testColor = testFiles > 0 ? c.brightGreen : c.dim;
|
|
505
|
+
|
|
506
|
+
// MCP / DB integration from data
|
|
507
|
+
const integration = d.integration || {};
|
|
508
|
+
const mcpServers = (integration.mcpServers) || {};
|
|
786
509
|
let integStr = '';
|
|
787
|
-
if (
|
|
788
|
-
const mcpCol =
|
|
789
|
-
|
|
790
|
-
integStr += c.cyan + 'MCP' + c.reset + ' ' + mcpCol + '\\u25CF' + integration.mcpServers.enabled + '/' + integration.mcpServers.total + c.reset;
|
|
510
|
+
if (mcpServers.total > 0) {
|
|
511
|
+
const mcpCol = mcpServers.enabled === mcpServers.total ? c.brightGreen : mcpServers.enabled > 0 ? c.brightYellow : c.red;
|
|
512
|
+
integStr += c.cyan + 'MCP' + c.reset + ' ' + mcpCol + '●' + mcpServers.enabled + '/' + mcpServers.total + c.reset;
|
|
791
513
|
}
|
|
792
|
-
if (integration.hasDatabase) integStr += (integStr ? ' ' : '') + c.brightGreen + '
|
|
793
|
-
if (
|
|
794
|
-
if (!integStr) integStr = c.dim + '\\u25CF none' + c.reset;
|
|
514
|
+
if (integration.hasDatabase) integStr += (integStr ? ' ' : '') + c.brightGreen + '◆' + c.reset + 'DB';
|
|
515
|
+
if (!integStr) integStr = c.dim + '● none' + c.reset;
|
|
795
516
|
|
|
796
517
|
lines.push(
|
|
797
|
-
c.brightCyan + '
|
|
798
|
-
c.cyan + 'Vectors' + c.reset + ' ' + vectorColor + '
|
|
799
|
-
c.cyan + 'Size' + c.reset + ' ' + c.brightWhite + sizeDisp + c.reset + ' ' + c.dim + '
|
|
800
|
-
c.cyan + 'Tests' + c.reset + ' ' + testColor + '
|
|
518
|
+
c.brightCyan + '📊 AgentDB' + c.reset + ' ' +
|
|
519
|
+
c.cyan + 'Vectors' + c.reset + ' ' + vectorColor + '●' + vectorCount + hnswInd + c.reset + ' ' + c.dim + '│' + c.reset + ' ' +
|
|
520
|
+
c.cyan + 'Size' + c.reset + ' ' + c.brightWhite + sizeDisp + c.reset + ' ' + c.dim + '│' + c.reset + ' ' +
|
|
521
|
+
c.cyan + 'Tests' + c.reset + ' ' + testColor + '●' + testFiles + c.reset + ' ' + c.dim + '(~' + testCases + ' cases)' + c.reset + ' ' + c.dim + '│' + c.reset + ' ' +
|
|
801
522
|
integStr
|
|
802
523
|
);
|
|
803
524
|
|
|
804
525
|
return lines.join('\\n');
|
|
805
526
|
}
|
|
806
527
|
|
|
807
|
-
// JSON output
|
|
528
|
+
// JSON output — delegates to CLI for accuracy; caller can use --json flag
|
|
808
529
|
function generateJSON() {
|
|
530
|
+
const d = getStatuslineData();
|
|
809
531
|
const git = getGitInfo();
|
|
810
|
-
return {
|
|
811
|
-
user: { name: git.name, gitBranch: git.gitBranch,
|
|
812
|
-
v3Progress: getV3Progress(),
|
|
813
|
-
security: getSecurityStatus(),
|
|
814
|
-
swarm: getSwarmStatus(),
|
|
815
|
-
system: getSystemMetrics(),
|
|
816
|
-
adrs: getADRStatus(),
|
|
817
|
-
hooks: getHooksStatus(),
|
|
818
|
-
agentdb: getAgentDBStats(),
|
|
819
|
-
tests: getTestStats(),
|
|
532
|
+
return Object.assign({}, d, {
|
|
533
|
+
user: Object.assign({ name: git.name, gitBranch: git.gitBranch }, d.user || {}),
|
|
820
534
|
git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind },
|
|
821
535
|
lastUpdated: new Date().toISOString(),
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// ─── Stdin reader (Claude Code pipes session JSON) ──────────────
|
|
826
|
-
|
|
827
|
-
// Claude Code sends session JSON via stdin (model, context, cost, etc.)
|
|
828
|
-
// Read it synchronously so the script works both:
|
|
829
|
-
// 1. When invoked by Claude Code (stdin has JSON)
|
|
830
|
-
// 2. When invoked manually from terminal (stdin is empty/tty)
|
|
831
|
-
let _stdinData = null;
|
|
832
|
-
function getStdinData() {
|
|
833
|
-
if (_stdinData !== undefined && _stdinData !== null) return _stdinData;
|
|
834
|
-
try {
|
|
835
|
-
// Check if stdin is a TTY (manual run) — skip reading
|
|
836
|
-
if (process.stdin.isTTY) { _stdinData = null; return null; }
|
|
837
|
-
// Read stdin synchronously via fd 0
|
|
838
|
-
const chunks = [];
|
|
839
|
-
const buf = Buffer.alloc(4096);
|
|
840
|
-
let bytesRead;
|
|
841
|
-
try {
|
|
842
|
-
while ((bytesRead = fs.readSync(0, buf, 0, buf.length, null)) > 0) {
|
|
843
|
-
chunks.push(buf.slice(0, bytesRead));
|
|
844
|
-
}
|
|
845
|
-
} catch { /* EOF or read error */ }
|
|
846
|
-
const raw = Buffer.concat(chunks).toString('utf-8').trim();
|
|
847
|
-
if (raw && raw.startsWith('{')) {
|
|
848
|
-
_stdinData = JSON.parse(raw);
|
|
849
|
-
} else {
|
|
850
|
-
_stdinData = null;
|
|
851
|
-
}
|
|
852
|
-
} catch {
|
|
853
|
-
_stdinData = null;
|
|
854
|
-
}
|
|
855
|
-
return _stdinData;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// Override model detection to prefer stdin data from Claude Code
|
|
859
|
-
function getModelFromStdin() {
|
|
860
|
-
const data = getStdinData();
|
|
861
|
-
if (data && data.model && data.model.display_name) return data.model.display_name;
|
|
862
|
-
return null;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
// Get context window info from Claude Code session
|
|
866
|
-
function getContextFromStdin() {
|
|
867
|
-
const data = getStdinData();
|
|
868
|
-
if (data && data.context_window) {
|
|
869
|
-
return {
|
|
870
|
-
usedPct: Math.floor(data.context_window.used_percentage || 0),
|
|
871
|
-
remainingPct: Math.floor(data.context_window.remaining_percentage || 100),
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
return null;
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// Get cost info from Claude Code session
|
|
878
|
-
function getCostFromStdin() {
|
|
879
|
-
const data = getStdinData();
|
|
880
|
-
if (data && data.cost) {
|
|
881
|
-
const durationMs = data.cost.total_duration_ms || 0;
|
|
882
|
-
const mins = Math.floor(durationMs / 60000);
|
|
883
|
-
const secs = Math.floor((durationMs % 60000) / 1000);
|
|
884
|
-
return {
|
|
885
|
-
costUsd: data.cost.total_cost_usd || 0,
|
|
886
|
-
duration: mins > 0 ? mins + 'm' + secs + 's' : secs + 's',
|
|
887
|
-
linesAdded: data.cost.total_lines_added || 0,
|
|
888
|
-
linesRemoved: data.cost.total_lines_removed || 0,
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
return null;
|
|
536
|
+
});
|
|
892
537
|
}
|
|
893
538
|
|
|
894
539
|
// ─── Main ───────────────────────────────────────────────────────
|