@claude-flow/cli 3.71.2 → 3.71.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/auto-memory-hook.mjs +224 -53
- package/.claude/helpers/helpers.manifest.json +4 -4
- package/.claude/helpers/intelligence.cjs +23 -1
- package/.claude/settings.json +2 -1
- package/README.md +2 -0
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/daemon.js +20 -1
- package/dist/src/commands/hive-mind-worker.d.ts +10 -0
- package/dist/src/commands/hive-mind-worker.js +20 -0
- package/dist/src/commands/hive-mind.js +27 -4
- package/dist/src/commands/memory.js +4 -3
- package/dist/src/init/helpers-generator.js +16 -1
- package/dist/src/init/settings-generator.js +4 -0
- package/dist/src/mcp-tools/agent-tools.js +4 -4
- package/dist/src/mcp-tools/agentbbs-federation.d.ts +18 -1
- package/dist/src/mcp-tools/agentbbs-federation.js +23 -7
- package/dist/src/mcp-tools/agentdb-tools.js +3 -3
- package/dist/src/mcp-tools/browser-argv.d.ts +18 -0
- package/dist/src/mcp-tools/browser-argv.js +59 -0
- package/dist/src/mcp-tools/browser-tools.d.ts +1 -1
- package/dist/src/mcp-tools/browser-tools.js +13 -6
- package/dist/src/mcp-tools/claims-tools.js +16 -9
- package/dist/src/mcp-tools/hooks-tools.js +11 -1
- package/dist/src/mcp-tools/memory-tools.js +49 -32
- package/dist/src/mcp-tools/neural-tools.js +8 -2
- package/dist/src/mcp-tools/path-containment.d.ts +34 -0
- package/dist/src/mcp-tools/path-containment.js +133 -0
- package/dist/src/mcp-tools/record-store.d.ts +7 -0
- package/dist/src/mcp-tools/record-store.js +19 -2
- package/dist/src/mcp-tools/session-tools.js +4 -38
- package/dist/src/mcp-tools/swarm-tools.js +4 -0
- package/dist/src/mcp-tools/task-tools.js +11 -0
- package/dist/src/mcp-tools/terminal-tools.js +7 -2
- package/dist/src/mcp-tools/validate-input.d.ts +2 -0
- package/dist/src/mcp-tools/validate-input.js +14 -0
- package/dist/src/missions/schemas.d.ts +10 -10
- package/dist/src/mods/headless-plugins.d.ts +16 -0
- package/dist/src/mods/headless-plugins.js +30 -1
- package/dist/src/services/daemon-autostart.d.ts +24 -0
- package/dist/src/services/daemon-autostart.js +55 -1
- package/dist/src/services/headless-worker-executor.d.ts +15 -0
- package/dist/src/services/headless-worker-executor.js +28 -0
- package/package.json +1 -1
- package/plugins/ruflo-console/dist/tui.mjs +132 -132
|
@@ -14,10 +14,20 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Profile override: `RUFLO_HEADLESS_PLUGINS` is a comma list of plugin names;
|
|
16
16
|
* `none` (or an empty value) turns plugin loading off.
|
|
17
|
+
*
|
|
18
|
+
* Permission mode: Claude Code 2.1.302 starts a `claude -p` with no
|
|
19
|
+
* permission mode in `manual` mode on third-party providers, with telemetry
|
|
20
|
+
* off, or on a fresh install. A headless worker has nobody to answer a prompt,
|
|
21
|
+
* so a print-mode argv without its own `--permission-mode` or
|
|
22
|
+
* `--dangerously-skip-permissions` gets `--permission-mode auto` — the mode
|
|
23
|
+
* first-party installs already started in. `RUFLO_HEADLESS_PERMISSION_MODE`
|
|
24
|
+
* picks another mode; `none` (or an empty value) passes no flag.
|
|
17
25
|
*/
|
|
18
26
|
import { type ResolveOptions, type PluginPaths } from './plugin-paths.js';
|
|
19
27
|
export declare const DEFAULT_WORKER_PLUGINS: readonly string[];
|
|
20
28
|
export declare const PROFILE_ENV = "RUFLO_HEADLESS_PLUGINS";
|
|
29
|
+
export declare const PERMISSION_MODE_ENV = "RUFLO_HEADLESS_PERMISSION_MODE";
|
|
30
|
+
export declare const DEFAULT_HEADLESS_PERMISSION_MODE = "auto";
|
|
21
31
|
export interface HeadlessPluginOptions extends ResolveOptions {
|
|
22
32
|
/** Plugin names to load; defaults to the env profile, else DEFAULT_WORKER_PLUGINS. */
|
|
23
33
|
profile?: readonly string[];
|
|
@@ -40,5 +50,11 @@ export declare function resetHeadlessPluginWarnings(): void;
|
|
|
40
50
|
export declare function workerProfile(env?: NodeJS.ProcessEnv, explicit?: readonly string[]): string[];
|
|
41
51
|
/** Every `--plugin-dir` value already in argv (both `--plugin-dir x` and `--plugin-dir=x`). */
|
|
42
52
|
export declare function existingPluginDirs(argv: readonly string[]): string[];
|
|
53
|
+
/**
|
|
54
|
+
* The `--permission-mode` args to append to a headless argv: none when the
|
|
55
|
+
* argv is not print mode or already sets a mode, else the env choice
|
|
56
|
+
* (default `auto`). An unknown mode is ignored with the default used instead.
|
|
57
|
+
*/
|
|
58
|
+
export declare function headlessPermissionArgs(argv: readonly string[], env?: NodeJS.ProcessEnv): string[];
|
|
43
59
|
export declare function withHeadlessPlugins<E extends Record<string, string | undefined>>(argv: readonly string[], env: E, opts?: HeadlessPluginOptions): HeadlessPluginResult<E>;
|
|
44
60
|
//# sourceMappingURL=headless-plugins.d.ts.map
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Profile override: `RUFLO_HEADLESS_PLUGINS` is a comma list of plugin names;
|
|
16
16
|
* `none` (or an empty value) turns plugin loading off.
|
|
17
|
+
*
|
|
18
|
+
* Permission mode: Claude Code 2.1.302 starts a `claude -p` with no
|
|
19
|
+
* permission mode in `manual` mode on third-party providers, with telemetry
|
|
20
|
+
* off, or on a fresh install. A headless worker has nobody to answer a prompt,
|
|
21
|
+
* so a print-mode argv without its own `--permission-mode` or
|
|
22
|
+
* `--dangerously-skip-permissions` gets `--permission-mode auto` — the mode
|
|
23
|
+
* first-party installs already started in. `RUFLO_HEADLESS_PERMISSION_MODE`
|
|
24
|
+
* picks another mode; `none` (or an empty value) passes no flag.
|
|
17
25
|
*/
|
|
18
26
|
import { realpathSync } from 'node:fs';
|
|
19
27
|
import { basename, resolve } from 'node:path';
|
|
@@ -21,6 +29,9 @@ import { ENABLE_ENV } from './install.js';
|
|
|
21
29
|
import { resolvePluginPaths } from './plugin-paths.js';
|
|
22
30
|
export const DEFAULT_WORKER_PLUGINS = ['ruflo-mods'];
|
|
23
31
|
export const PROFILE_ENV = 'RUFLO_HEADLESS_PLUGINS';
|
|
32
|
+
export const PERMISSION_MODE_ENV = 'RUFLO_HEADLESS_PERMISSION_MODE';
|
|
33
|
+
export const DEFAULT_HEADLESS_PERMISSION_MODE = 'auto';
|
|
34
|
+
const PERMISSION_MODES = new Set(['acceptEdits', 'auto', 'bypassPermissions', 'manual', 'default', 'dontAsk', 'plan']);
|
|
24
35
|
const warned = new Set();
|
|
25
36
|
/** For tests: forget which missing plugins were already logged. */
|
|
26
37
|
export function resetHeadlessPluginWarnings() {
|
|
@@ -56,9 +67,27 @@ export function existingPluginDirs(argv) {
|
|
|
56
67
|
}
|
|
57
68
|
return dirs;
|
|
58
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* The `--permission-mode` args to append to a headless argv: none when the
|
|
72
|
+
* argv is not print mode or already sets a mode, else the env choice
|
|
73
|
+
* (default `auto`). An unknown mode is ignored with the default used instead.
|
|
74
|
+
*/
|
|
75
|
+
export function headlessPermissionArgs(argv, env = process.env) {
|
|
76
|
+
const isPrint = argv.some((a) => a === '-p' || a === '--print');
|
|
77
|
+
if (!isPrint)
|
|
78
|
+
return [];
|
|
79
|
+
const hasMode = argv.some((a) => a === '--permission-mode' || a.startsWith('--permission-mode=') || a === '--dangerously-skip-permissions');
|
|
80
|
+
if (hasMode)
|
|
81
|
+
return [];
|
|
82
|
+
const raw = env[PERMISSION_MODE_ENV];
|
|
83
|
+
if (raw !== undefined && (raw.trim() === '' || raw.trim().toLowerCase() === 'none'))
|
|
84
|
+
return [];
|
|
85
|
+
const mode = raw !== undefined && PERMISSION_MODES.has(raw.trim()) ? raw.trim() : DEFAULT_HEADLESS_PERMISSION_MODE;
|
|
86
|
+
return ['--permission-mode', mode];
|
|
87
|
+
}
|
|
59
88
|
export function withHeadlessPlugins(argv, env, opts = {}) {
|
|
60
89
|
const profile = workerProfile(opts.env ?? env, opts.profile);
|
|
61
|
-
const outArgv = [...argv];
|
|
90
|
+
const outArgv = [...argv, ...headlessPermissionArgs(argv, opts.env ?? env)];
|
|
62
91
|
const outEnv = { ...env };
|
|
63
92
|
if (profile.length === 0)
|
|
64
93
|
return { argv: outArgv, env: outEnv, added: [], missing: [] };
|
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
/** True if a live daemon already holds the project's pidfile. */
|
|
2
2
|
export declare function isDaemonAlive(projectRoot: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* #4654: an explicit `daemon stop` is an opt-out that must survive the next
|
|
5
|
+
* hook. Every ruflo-core plugin hook (PreToolUse/PostToolUse, PreCompact, Stop)
|
|
6
|
+
* runs the CLI, and the CLI auto-starts a daemon whenever none is alive, so a
|
|
7
|
+
* stopped daemon came back within minutes. `daemon stop` now writes this
|
|
8
|
+
* marker, auto-start honours it, and only an explicit `daemon start` clears it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DAEMON_STOP_MARKER = "daemon.stopped";
|
|
11
|
+
/**
|
|
12
|
+
* Set on the daemon that auto-start spawns, so that spawned `daemon start` does
|
|
13
|
+
* not clear a stop marker written after auto-start checked it (a `daemon stop`
|
|
14
|
+
* racing a hook). Only a user-driven start clears the marker.
|
|
15
|
+
*/
|
|
16
|
+
export declare const AUTOSTART_ENV = "RUFLO_DAEMON_AUTOSTARTED";
|
|
17
|
+
/** True if the user explicitly stopped the daemon for this project (#4654). */
|
|
18
|
+
export declare function isDaemonStopMarked(projectRoot: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Record an explicit `daemon stop` (#4654). Writes only into a directory that is
|
|
21
|
+
* already a Ruflo project or already has `.claude-flow/`, so `daemon stop` in an
|
|
22
|
+
* unrelated directory does not litter it. Best-effort; never throws.
|
|
23
|
+
*/
|
|
24
|
+
export declare function writeDaemonStopMarker(projectRoot: string): boolean;
|
|
25
|
+
/** Clear the stop marker. Called by an explicit `daemon start` (#4654). Never throws. */
|
|
26
|
+
export declare function clearDaemonStopMarker(projectRoot: string): void;
|
|
3
27
|
/**
|
|
4
28
|
* Return true only when the directory contains a durable Ruflo project marker.
|
|
5
29
|
*
|
|
@@ -81,6 +81,57 @@ function autostartDisabledByProjectConfig(projectRoot) {
|
|
|
81
81
|
}
|
|
82
82
|
return false;
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* #4654: an explicit `daemon stop` is an opt-out that must survive the next
|
|
86
|
+
* hook. Every ruflo-core plugin hook (PreToolUse/PostToolUse, PreCompact, Stop)
|
|
87
|
+
* runs the CLI, and the CLI auto-starts a daemon whenever none is alive, so a
|
|
88
|
+
* stopped daemon came back within minutes. `daemon stop` now writes this
|
|
89
|
+
* marker, auto-start honours it, and only an explicit `daemon start` clears it.
|
|
90
|
+
*/
|
|
91
|
+
export const DAEMON_STOP_MARKER = 'daemon.stopped';
|
|
92
|
+
/**
|
|
93
|
+
* Set on the daemon that auto-start spawns, so that spawned `daemon start` does
|
|
94
|
+
* not clear a stop marker written after auto-start checked it (a `daemon stop`
|
|
95
|
+
* racing a hook). Only a user-driven start clears the marker.
|
|
96
|
+
*/
|
|
97
|
+
export const AUTOSTART_ENV = 'RUFLO_DAEMON_AUTOSTARTED';
|
|
98
|
+
function stopMarkerPath(projectRoot) {
|
|
99
|
+
return path.join(projectRoot, '.claude-flow', DAEMON_STOP_MARKER);
|
|
100
|
+
}
|
|
101
|
+
/** True if the user explicitly stopped the daemon for this project (#4654). */
|
|
102
|
+
export function isDaemonStopMarked(projectRoot) {
|
|
103
|
+
try {
|
|
104
|
+
return fs.existsSync(stopMarkerPath(projectRoot));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Record an explicit `daemon stop` (#4654). Writes only into a directory that is
|
|
112
|
+
* already a Ruflo project or already has `.claude-flow/`, so `daemon stop` in an
|
|
113
|
+
* unrelated directory does not litter it. Best-effort; never throws.
|
|
114
|
+
*/
|
|
115
|
+
export function writeDaemonStopMarker(projectRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const dir = path.join(projectRoot, '.claude-flow');
|
|
118
|
+
if (!fs.existsSync(dir) && !isRufloProject(projectRoot))
|
|
119
|
+
return false;
|
|
120
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
121
|
+
fs.writeFileSync(stopMarkerPath(projectRoot), JSON.stringify({ stoppedAt: new Date().toISOString(), pid: process.pid }) + '\n');
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Clear the stop marker. Called by an explicit `daemon start` (#4654). Never throws. */
|
|
129
|
+
export function clearDaemonStopMarker(projectRoot) {
|
|
130
|
+
try {
|
|
131
|
+
fs.unlinkSync(stopMarkerPath(projectRoot));
|
|
132
|
+
}
|
|
133
|
+
catch { /* absent */ }
|
|
134
|
+
}
|
|
84
135
|
function autostartDisabled(projectRoot) {
|
|
85
136
|
if (/^(0|false|no|off)$/i.test(process.env.RUFLO_DAEMON_AUTOSTART ?? ''))
|
|
86
137
|
return true;
|
|
@@ -166,7 +217,7 @@ const defaultSpawn = (projectRoot) => {
|
|
|
166
217
|
cwd: projectRoot,
|
|
167
218
|
detached: true,
|
|
168
219
|
stdio: 'ignore',
|
|
169
|
-
env: { ...process.env },
|
|
220
|
+
env: { ...process.env, [AUTOSTART_ENV]: '1' },
|
|
170
221
|
});
|
|
171
222
|
child.unref();
|
|
172
223
|
};
|
|
@@ -186,6 +237,9 @@ export function ensureDaemonRunning(startDir, opts = {}) {
|
|
|
186
237
|
if (!isRufloProject(projectRoot)) {
|
|
187
238
|
return { started: false, reason: 'not a ruflo project' };
|
|
188
239
|
}
|
|
240
|
+
if (isDaemonStopMarked(projectRoot)) {
|
|
241
|
+
return { started: false, reason: 'stopped by user (daemon stop); run `daemon start` to re-enable' };
|
|
242
|
+
}
|
|
189
243
|
const alive = (opts.isAlive ?? isDaemonAlive)(projectRoot);
|
|
190
244
|
if (alive)
|
|
191
245
|
return { started: false, reason: 'already running' };
|
|
@@ -123,6 +123,12 @@ export interface HeadlessExecutionResult {
|
|
|
123
123
|
inputTokens?: number;
|
|
124
124
|
outputTokens?: number;
|
|
125
125
|
costUsd?: number;
|
|
126
|
+
/**
|
|
127
|
+
* When the stored claude.ai sign-in the worker ran under ends (ISO 8601),
|
|
128
|
+
* from `credential_expires_at` in the `claude -p` JSON result (Claude Code
|
|
129
|
+
* 2.1.318+). Absent for API-key auth and older CLIs.
|
|
130
|
+
*/
|
|
131
|
+
credentialExpiresAt?: string;
|
|
126
132
|
/** Model used for execution */
|
|
127
133
|
model: string;
|
|
128
134
|
/** Sandbox mode used */
|
|
@@ -202,7 +208,16 @@ export interface ClaudePrintEnvelope {
|
|
|
202
208
|
outputTokens?: number;
|
|
203
209
|
costUsd?: number;
|
|
204
210
|
durationMs?: number;
|
|
211
|
+
/** ISO 8601 end of the stored claude.ai sign-in (Claude Code 2.1.318+). */
|
|
212
|
+
credentialExpiresAt?: string;
|
|
205
213
|
}
|
|
214
|
+
/** Days before a sign-in ends at which the executor emits 'credential-expiring' (matches Claude Code's own stderr warning). */
|
|
215
|
+
export declare const CREDENTIAL_EXPIRY_WARN_DAYS = 10;
|
|
216
|
+
/**
|
|
217
|
+
* Normalise `credential_expires_at` to ISO 8601. Accepts an ISO string or an
|
|
218
|
+
* epoch number (seconds or milliseconds); anything else is undefined.
|
|
219
|
+
*/
|
|
220
|
+
export declare function normalizeCredentialExpiry(v: unknown): string | undefined;
|
|
206
221
|
/**
|
|
207
222
|
* #2661 root-fix — best-effort parse of `claude --print --output-format
|
|
208
223
|
* json`'s response envelope. Deliberately lenient: probes a couple of
|
|
@@ -337,6 +337,23 @@ export function isLocalWorker(type) {
|
|
|
337
337
|
export function getModelId(model) {
|
|
338
338
|
return MODEL_IDS[model];
|
|
339
339
|
}
|
|
340
|
+
/** Days before a sign-in ends at which the executor emits 'credential-expiring' (matches Claude Code's own stderr warning). */
|
|
341
|
+
export const CREDENTIAL_EXPIRY_WARN_DAYS = 10;
|
|
342
|
+
/**
|
|
343
|
+
* Normalise `credential_expires_at` to ISO 8601. Accepts an ISO string or an
|
|
344
|
+
* epoch number (seconds or milliseconds); anything else is undefined.
|
|
345
|
+
*/
|
|
346
|
+
export function normalizeCredentialExpiry(v) {
|
|
347
|
+
let ms;
|
|
348
|
+
if (typeof v === 'number' && Number.isFinite(v) && v > 0)
|
|
349
|
+
ms = v < 1e12 ? v * 1000 : v;
|
|
350
|
+
else if (typeof v === 'string' && v.trim()) {
|
|
351
|
+
const t = Date.parse(v);
|
|
352
|
+
if (Number.isFinite(t))
|
|
353
|
+
ms = t;
|
|
354
|
+
}
|
|
355
|
+
return ms === undefined ? undefined : new Date(ms).toISOString();
|
|
356
|
+
}
|
|
340
357
|
/**
|
|
341
358
|
* #2661 root-fix — best-effort parse of `claude --print --output-format
|
|
342
359
|
* json`'s response envelope. Deliberately lenient: probes a couple of
|
|
@@ -371,6 +388,7 @@ export function parseClaudePrintJsonEnvelope(raw) {
|
|
|
371
388
|
outputTokens: numOrUndef(usage?.output_tokens ?? usage?.outputTokens),
|
|
372
389
|
costUsd: numOrUndef(obj.total_cost_usd ?? obj.cost_usd ?? obj.totalCostUsd),
|
|
373
390
|
durationMs: numOrUndef(obj.duration_ms ?? obj.durationMs),
|
|
391
|
+
credentialExpiresAt: normalizeCredentialExpiry(obj.credential_expires_at ?? obj.credentialExpiresAt),
|
|
374
392
|
};
|
|
375
393
|
}
|
|
376
394
|
/**
|
|
@@ -779,7 +797,16 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
779
797
|
executionId,
|
|
780
798
|
error: result.error,
|
|
781
799
|
...(result.missingPlugins ? { missingPlugins: result.missingPlugins } : {}),
|
|
800
|
+
...(result.credentialExpiresAt ? { credentialExpiresAt: result.credentialExpiresAt } : {}),
|
|
782
801
|
};
|
|
802
|
+
// Claude Code 2.1.318: surface a claude.ai sign-in that ends soon, so the
|
|
803
|
+
// daemon can say so before workers start failing on auth.
|
|
804
|
+
if (result.credentialExpiresAt) {
|
|
805
|
+
const daysLeft = (Date.parse(result.credentialExpiresAt) - Date.now()) / 86_400_000;
|
|
806
|
+
if (daysLeft <= CREDENTIAL_EXPIRY_WARN_DAYS) {
|
|
807
|
+
this.emit('credential-expiring', { expiresAt: result.credentialExpiresAt, daysLeft: Math.max(0, Math.floor(daysLeft)), workerType });
|
|
808
|
+
}
|
|
809
|
+
}
|
|
783
810
|
// Log result
|
|
784
811
|
this.logExecution(executionId, 'result', JSON.stringify(executionResult, null, 2));
|
|
785
812
|
// #2661 root-fix — structured per-launch telemetry, receipted
|
|
@@ -1182,6 +1209,7 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
1182
1209
|
: undefined,
|
|
1183
1210
|
costUsd: envelope?.costUsd,
|
|
1184
1211
|
apiDurationMs: envelope?.durationMs,
|
|
1212
|
+
credentialExpiresAt: envelope?.credentialExpiresAt,
|
|
1185
1213
|
error: code !== 0 ? stderr || `Process exited with code ${code}` : undefined,
|
|
1186
1214
|
});
|
|
1187
1215
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.71.
|
|
3
|
+
"version": "3.71.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",
|