claude-flow 3.27.3 → 3.27.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/hook-handler.cjs +0 -149
- package/.claude/helpers/intelligence.cjs +0 -10
- package/.claude/helpers/statusline.cjs +1 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/init.js +3 -1
- package/v3/@claude-flow/cli/dist/src/init/executor.js +26 -2
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -15,99 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const fs = require('fs');
|
|
18
|
-
const os = require('os');
|
|
19
18
|
|
|
20
19
|
const helpersDir = __dirname;
|
|
21
20
|
|
|
22
|
-
// Resolve an installed @claude-flow/cli (or ruflo) bin — mirrors
|
|
23
|
-
// statusline-generator.ts's resolveCliBin() candidate list. Used only to
|
|
24
|
-
// spawn the detached funnel-refresh helper below; failures are silent (no
|
|
25
|
-
// candidate found just means the refresh never fires this session).
|
|
26
|
-
//
|
|
27
|
-
// Verifies dist/src/index.js exists alongside bin/cli.js, not just the bin
|
|
28
|
-
// itself — Claude Code's own plugin marketplace mechanism installs by
|
|
29
|
-
// `git clone`/`git pull` with no build step, so `~/.claude/plugins/
|
|
30
|
-
// marketplaces/ruflo` is a SOURCE-ONLY checkout by construction: bin/cli.js
|
|
31
|
-
// is present on disk but importing dist/src/index.js throws
|
|
32
|
-
// ERR_MODULE_NOT_FOUND on every real command (confirmed live — only
|
|
33
|
-
// `--version` happens to survive it, since it reads package.json directly).
|
|
34
|
-
// Without this check, resolveCliBinForHook() picked that doomed candidate
|
|
35
|
-
// first every time and spawnDetachedFunnelRefresh() below had no fallback,
|
|
36
|
-
// so the promo/disclosure row could never populate for any marketplace
|
|
37
|
-
// install, on any OS.
|
|
38
|
-
function resolveCliBinForHook() {
|
|
39
|
-
try {
|
|
40
|
-
const home = os.homedir();
|
|
41
|
-
const cwd = process.cwd();
|
|
42
|
-
const candidates = [
|
|
43
|
-
path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'bin', 'cli.js'),
|
|
44
|
-
path.join(cwd, 'node_modules', '@claude-flow', 'cli', 'bin', 'cli.js'),
|
|
45
|
-
path.join(cwd, 'node_modules', 'ruflo', 'bin', 'cli.js'),
|
|
46
|
-
path.join(cwd, 'v3', '@claude-flow', 'cli', 'bin', 'cli.js'),
|
|
47
|
-
// helpersDir is .claude/helpers/ inside the package itself when this
|
|
48
|
-
// file is running from a real @claude-flow/cli install (not a project
|
|
49
|
-
// that merely copied the helper) — its bin/ is two levels up.
|
|
50
|
-
path.join(helpersDir, '..', '..', 'bin', 'cli.js'),
|
|
51
|
-
];
|
|
52
|
-
for (const p of candidates) {
|
|
53
|
-
try {
|
|
54
|
-
if (fs.existsSync(p) && fs.existsSync(path.join(path.dirname(p), '..', 'dist', 'src', 'index.js'))) {
|
|
55
|
-
return p;
|
|
56
|
-
}
|
|
57
|
-
} catch (e) { /* try next candidate */ }
|
|
58
|
-
}
|
|
59
|
-
} catch (e) { /* ignore */ }
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Fire-and-forget doesn't work when the CALLER is itself a short-lived
|
|
64
|
-
// subprocess (confirmed live: two consecutive statusline renders 5s apart
|
|
65
|
-
// both saw an empty funnel-messages-cache, because the async HTTPS fetch
|
|
66
|
-
// inside a `void refreshRemoteMessages()` call gets killed when the spawning
|
|
67
|
-
// process exits before the request completes). Spawning fully DETACHED +
|
|
68
|
-
// unref'd decouples the refresh's lifetime from this hook's — it keeps
|
|
69
|
-
// running (up to message-transport.ts's own 4s fetch timeout) even after
|
|
70
|
-
// session-restore's own process has already exited, so it actually gets a
|
|
71
|
-
// chance to write the cache. Never awaited here — must not add to
|
|
72
|
-
// SessionStart's own timeout budget.
|
|
73
|
-
//
|
|
74
|
-
// No usable local candidate (resolveCliBinForHook() returned null) falls
|
|
75
|
-
// back to npx: this call is detached/unref'd, so a slower npx cold-start
|
|
76
|
-
// costs nothing perceptible — unlike the statusline's own synchronous
|
|
77
|
-
// render path, where local-first exists purely for per-render latency.
|
|
78
|
-
// `--prefer-offline` avoids a registry round trip for the tarball when
|
|
79
|
-
// already cached while still resolving the current `@latest` version.
|
|
80
|
-
function spawnDetachedHookRefresh(subcommand) {
|
|
81
|
-
try {
|
|
82
|
-
const { spawn } = require('child_process');
|
|
83
|
-
const cliBin = resolveCliBinForHook();
|
|
84
|
-
const cmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
85
|
-
const spawnArgs = cliBin
|
|
86
|
-
? [process.execPath, [cliBin, 'hooks', subcommand, '--quiet']]
|
|
87
|
-
: [cmd, ['--prefer-offline', '@claude-flow/cli', 'hooks', subcommand, '--quiet']];
|
|
88
|
-
const child = spawn(spawnArgs[0], spawnArgs[1], {
|
|
89
|
-
detached: true,
|
|
90
|
-
stdio: 'ignore',
|
|
91
|
-
env: process.env,
|
|
92
|
-
});
|
|
93
|
-
child.unref();
|
|
94
|
-
} catch (e) { /* best-effort only */ }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function spawnDetachedFunnelRefresh() {
|
|
98
|
-
spawnDetachedHookRefresh('refresh-funnel');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Same fallback-aware pattern as spawnDetachedFunnelRefresh() above, for
|
|
102
|
-
// ADR-316's co-pilot advisor tip. Safe to call on EVERY session-restore:
|
|
103
|
-
// refresh-advisor's own action checks consent + a 24h TTL BEFORE spending
|
|
104
|
-
// anything, so an unconsented or already-fresh install is a fast no-op file
|
|
105
|
-
// read, never a network call. Never awaited here — must not add to
|
|
106
|
-
// SessionStart's own timeout budget.
|
|
107
|
-
function spawnDetachedAdvisorRefresh() {
|
|
108
|
-
spawnDetachedHookRefresh('refresh-advisor');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
21
|
// Safe require with stdout suppression - the helper modules have CLI
|
|
112
22
|
// sections that run unconditionally on require(), so we mute console
|
|
113
23
|
// during the require to prevent noisy output.
|
|
@@ -258,57 +168,6 @@ const handlers = {
|
|
|
258
168
|
} else {
|
|
259
169
|
console.log('[INFO] Router not available, using default routing');
|
|
260
170
|
}
|
|
261
|
-
|
|
262
|
-
// Rate-limit -> sponsored-capacity nudge (ADR-312/313). Fires here,
|
|
263
|
-
// client-side, BEFORE the API call this prompt would make — so it still
|
|
264
|
-
// reaches the transcript even if that call then fails from the rate
|
|
265
|
-
// limit. Cheap local file reads only; never a network call or a child
|
|
266
|
-
// process, so it cannot add latency to prompt submission.
|
|
267
|
-
try {
|
|
268
|
-
const rlFunnelEnv = process.env.RUFLO_FUNNEL;
|
|
269
|
-
const rlDisabledByEnv = rlFunnelEnv !== undefined && /^(0|false|off|no)$/i.test(String(rlFunnelEnv).trim());
|
|
270
|
-
const rlCiVars = ['CI', 'GITHUB_ACTIONS', 'GITLAB_CI', 'CIRCLECI', 'TRAVIS', 'BUILDKITE', 'JENKINS_URL', 'TEAMCITY_VERSION', 'TF_BUILD'];
|
|
271
|
-
const rlIsCi = rlCiVars.some((v) => {
|
|
272
|
-
const val = process.env[v];
|
|
273
|
-
return val !== undefined && val !== '' && val !== '0' && String(val).toLowerCase() !== 'false';
|
|
274
|
-
});
|
|
275
|
-
const rlHome = path.join(os.homedir(), '.ruflo');
|
|
276
|
-
let rlUserDisabled = false;
|
|
277
|
-
try {
|
|
278
|
-
const rlUserCfg = JSON.parse(fs.readFileSync(path.join(rlHome, 'funnel.json'), 'utf8'));
|
|
279
|
-
rlUserDisabled = !!(rlUserCfg && rlUserCfg.enabled === false);
|
|
280
|
-
} catch (e) { /* absent/malformed = not disabled */ }
|
|
281
|
-
let rlProjectDisabled = false;
|
|
282
|
-
try {
|
|
283
|
-
const rlProjCfg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'claude-flow.config.json'), 'utf8'));
|
|
284
|
-
rlProjectDisabled = !!(rlProjCfg && rlProjCfg.funnel && rlProjCfg.funnel.enabled === false);
|
|
285
|
-
} catch (e) { /* absent/malformed = not disabled */ }
|
|
286
|
-
|
|
287
|
-
if (!rlDisabledByEnv && !rlIsCi && !rlUserDisabled && !rlProjectDisabled) {
|
|
288
|
-
let rlStatus = null;
|
|
289
|
-
try { rlStatus = JSON.parse(fs.readFileSync(path.join(rlHome, 'rate-limit-status.json'), 'utf8')); } catch (e) { /* not flagged */ }
|
|
290
|
-
let rlIsLimited = false;
|
|
291
|
-
if (rlStatus && rlStatus.limited) {
|
|
292
|
-
if (rlStatus.since) {
|
|
293
|
-
const rlSinceMs = Date.parse(rlStatus.since);
|
|
294
|
-
rlIsLimited = isNaN(rlSinceMs) ? true : (Date.now() - rlSinceMs) < 6 * 60 * 60 * 1000;
|
|
295
|
-
} else {
|
|
296
|
-
rlIsLimited = true;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
if (rlIsLimited) {
|
|
300
|
-
let rlConsented = false;
|
|
301
|
-
try {
|
|
302
|
-
const rlConsentFile = JSON.parse(fs.readFileSync(path.join(rlHome, 'consent.json'), 'utf8'));
|
|
303
|
-
const rlReceipt = rlConsentFile && rlConsentFile['sponsored-downtime'];
|
|
304
|
-
rlConsented = !!(rlReceipt && rlReceipt.granted === true && rlReceipt.at !== null && rlReceipt.policyVersion === 1);
|
|
305
|
-
} catch (e) { /* not consented */ }
|
|
306
|
-
if (!rlConsented) {
|
|
307
|
-
console.log('[COGNITUM] Hit your Claude usage limit? Free sponsored capacity is available at cognitum.one/meta-llm — run: ruflo proxy sponsor-enable --yes');
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
} catch (e) { /* nudge must never break the hook */ }
|
|
312
171
|
},
|
|
313
172
|
|
|
314
173
|
'pre-bash': () => {
|
|
@@ -375,14 +234,6 @@ const handlers = {
|
|
|
375
234
|
console.log(`[INTELLIGENCE] Loaded ${initResult.nodes} patterns, ${initResult.edges} edges`);
|
|
376
235
|
}
|
|
377
236
|
}
|
|
378
|
-
// Warm the funnel message cache once per session (see
|
|
379
|
-
// spawnDetachedFunnelRefresh's doc comment for why this must happen
|
|
380
|
-
// here, detached, rather than as the statusline's own fire-and-forget).
|
|
381
|
-
spawnDetachedFunnelRefresh();
|
|
382
|
-
// ADR-316 co-pilot advisor tip — same detached pattern; cheap no-op
|
|
383
|
-
// when not consented or still within the 24h TTL (see refresh-advisor's
|
|
384
|
-
// own doc comment).
|
|
385
|
-
spawnDetachedAdvisorRefresh();
|
|
386
237
|
},
|
|
387
238
|
|
|
388
239
|
'session-end': async () => {
|
|
@@ -587,16 +587,6 @@ function recordEdit(file, success) {
|
|
|
587
587
|
sessionId: sessionGet('sessionId') || null,
|
|
588
588
|
});
|
|
589
589
|
fs.appendFileSync(PENDING_PATH, entry + '\n', 'utf-8');
|
|
590
|
-
// Runaway-storage guard: pending-insights is append-only and only drained by
|
|
591
|
-
// consolidation. If it grows past ~512KB (thousands of un-consolidated edits
|
|
592
|
-
// — e.g. the daemon never ran), keep only the most recent 2000 lines so it
|
|
593
|
-
// can never grow unbounded. Cheap (a statSync per edit; rewrite only when over).
|
|
594
|
-
try {
|
|
595
|
-
if (fs.statSync(PENDING_PATH).size > 512 * 1024) {
|
|
596
|
-
const lines = fs.readFileSync(PENDING_PATH, 'utf-8').split('\n').filter(Boolean);
|
|
597
|
-
if (lines.length > 2000) fs.writeFileSync(PENDING_PATH, lines.slice(-2000).join('\n') + '\n', 'utf-8');
|
|
598
|
-
}
|
|
599
|
-
} catch (e) { /* non-fatal */ }
|
|
600
590
|
}
|
|
601
591
|
|
|
602
592
|
/**
|
|
@@ -618,7 +618,7 @@ function getPkgVersion() {
|
|
|
618
618
|
// version (see generateStatuslineScript()'s doc comment) — correct even
|
|
619
619
|
// when this renders via a pure npx invocation with no local install for
|
|
620
620
|
// the candidate scan below to find.
|
|
621
|
-
let ver = "3.27.
|
|
621
|
+
let ver = "3.27.4";
|
|
622
622
|
try {
|
|
623
623
|
const home = os.homedir();
|
|
624
624
|
const pkgPaths = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.27.
|
|
3
|
+
"version": "3.27.4",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -468,7 +468,9 @@ const initAction = async (ctx) => {
|
|
|
468
468
|
output.writeln(output.bold('Next steps:'));
|
|
469
469
|
output.printList([
|
|
470
470
|
`Run ${output.highlight(`${bin} daemon start`)} to start background workers`,
|
|
471
|
-
|
|
471
|
+
// Memory is initialized automatically during init (persistent by
|
|
472
|
+
// default — see executor.ts) — no separate `memory init` step needed
|
|
473
|
+
// unless the DB was skipped (MINIMAL_INIT_OPTIONS) or needs --force.
|
|
472
474
|
`Run ${output.highlight(`${bin} swarm init`)} to initialize a swarm`,
|
|
473
475
|
`Or use ${output.highlight(`${bin} init --start-all`)} to do all of the above`,
|
|
474
476
|
options.components.settings ? `Review ${output.highlight('.claude/settings.json')} for hook configurations` : '',
|
|
@@ -212,15 +212,39 @@ export async function executeInit(options) {
|
|
|
212
212
|
// the `vector_indexes` table (older CLI / agentdb-written), self-heal it
|
|
213
213
|
// so the statusline vector count + namespace routing work. Best-effort,
|
|
214
214
|
// dynamically imported so a WASM-only host without better-sqlite3 just
|
|
215
|
-
// skips it.
|
|
215
|
+
// skips it.
|
|
216
|
+
//
|
|
217
|
+
// Persistent memory ON BY DEFAULT: `runtime.memoryBackend` already
|
|
218
|
+
// defaults to 'hybrid' in DEFAULT_INIT_OPTIONS, but that only ever
|
|
219
|
+
// configured the DECLARED backend — the actual .swarm/memory.db file
|
|
220
|
+
// was never created until something eventually called `memory store`
|
|
221
|
+
// or the user ran `memory init --force` by hand (both the generated
|
|
222
|
+
// CLAUDE.md and the quickstart docs told users to do this as a
|
|
223
|
+
// separate step). A fresh project could sit for days looking
|
|
224
|
+
// "configured for AgentDB" while genuinely capturing nothing, with no
|
|
225
|
+
// signal that the config and the on-disk reality had diverged. Fresh
|
|
226
|
+
// projects now get the DB eagerly created here, matching what
|
|
227
|
+
// `memoryBackend` already promised; MINIMAL_INIT_OPTIONS
|
|
228
|
+
// (memoryBackend: 'memory') opts out, matching its non-persistent intent.
|
|
216
229
|
try {
|
|
217
230
|
const memDbPath = path.join(targetDir, '.swarm', 'memory.db');
|
|
218
231
|
if (fs.existsSync(memDbPath)) {
|
|
219
232
|
const { repairVectorIndexes } = await import('../memory/memory-initializer.js');
|
|
220
233
|
await repairVectorIndexes(memDbPath, { autoRecover: true });
|
|
221
234
|
}
|
|
235
|
+
else if (options.runtime.memoryBackend !== 'memory') {
|
|
236
|
+
const { initializeMemoryDatabase } = await import('../memory/memory-initializer.js');
|
|
237
|
+
const initResult = await initializeMemoryDatabase({
|
|
238
|
+
backend: options.runtime.memoryBackend,
|
|
239
|
+
dbPath: memDbPath,
|
|
240
|
+
verbose: false,
|
|
241
|
+
});
|
|
242
|
+
if (initResult.success) {
|
|
243
|
+
result.created.files.push('.swarm/memory.db');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
222
246
|
}
|
|
223
|
-
catch { /* best-effort — never block init on memory
|
|
247
|
+
catch { /* best-effort — never block init on memory setup */ }
|
|
224
248
|
}
|
|
225
249
|
// Generate statusline
|
|
226
250
|
if (options.components.statusline) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.27.
|
|
3
|
+
"version": "3.27.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|