@claude-flow/cli 3.33.0 → 3.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.proven-config-version +1 -0
- package/.claude/helpers/.helpers-version +1 -1
- package/.claude/helpers/helpers.manifest.json +2 -2
- package/.claude/helpers/statusline.cjs +0 -0
- package/.claude/proven-config.json +42 -0
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/agent.js +2 -1
- package/dist/src/commands/agntcy/index.d.ts +64 -0
- package/dist/src/commands/agntcy/index.js +64 -0
- package/dist/src/commands/agntcy/publish.d.ts +43 -0
- package/dist/src/commands/agntcy/publish.js +121 -0
- package/dist/src/commands/agntcy/runtime.d.ts +90 -0
- package/dist/src/commands/agntcy/runtime.js +115 -0
- package/dist/src/commands/agntcy/swarm-join.d.ts +28 -0
- package/dist/src/commands/agntcy/swarm-join.js +85 -0
- package/dist/src/commands/agntcy/transport.d.ts +24 -0
- package/dist/src/commands/agntcy/transport.js +95 -0
- package/dist/src/commands/daemon.js +12 -7
- package/dist/src/commands/doctor.js +72 -1
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.js +5 -0
- package/dist/src/commands/metaharness.js +37 -2
- package/dist/src/commands/swarm.js +2 -1
- package/dist/src/log-filters.d.ts +3 -3
- package/dist/src/mcp-tools/metaharness-tools.js +35 -2
- package/dist/src/memory/memory-bridge.d.ts +25 -0
- package/dist/src/memory/memory-bridge.js +61 -8
- package/dist/src/memory/memory-initializer.d.ts +9 -3
- package/dist/src/memory/memory-initializer.js +344 -277
- package/dist/src/services/daemon-autostart.d.ts +31 -3
- package/dist/src/services/daemon-autostart.js +45 -3
- package/dist/src/services/distill-oracle.d.ts +1 -1
- package/dist/src/services/distill-oracle.js +2 -2
- package/dist/src/services/evolve-proof.d.ts +40 -1
- package/dist/src/services/evolve-proof.js +76 -14
- package/dist/src/services/flywheel-receipt.d.ts +15 -0
- package/dist/src/services/flywheel-receipt.js +22 -0
- package/dist/src/services/flywheel-sequential-evidence.d.ts +102 -0
- package/dist/src/services/flywheel-sequential-evidence.js +148 -0
- package/dist/src/services/flywheel-transaction.d.ts +71 -0
- package/dist/src/services/flywheel-transaction.js +121 -0
- package/dist/src/services/harness-flywheel-generations.d.ts +14 -0
- package/dist/src/services/harness-flywheel-generations.js +76 -4
- package/dist/src/services/harness-flywheel.d.ts +13 -0
- package/dist/src/services/harness-flywheel.js +31 -1
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/package.json +5 -9
- package/plugins/ruflo-metaharness/scripts/smoke.sh +4 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI `ruflo swarm join <namespace>` — ADR-380 §2.
|
|
3
|
+
*
|
|
4
|
+
* Joins a SLIM group-membership channel scoped to a Cognitum tenant/project
|
|
5
|
+
* namespace (e.g. `cognitum/research/security`). This is an AGNTCY/SLIM
|
|
6
|
+
* concept layered on top of — never a replacement for — this repo's own
|
|
7
|
+
* `swarm_init`/`hive-mind_*` MCP-tool coordination, which remains the
|
|
8
|
+
* default for single-host swarms per ADR-380 §2.
|
|
9
|
+
*
|
|
10
|
+
* NOT WIRED INTO THE MAIN CLI ROUTER YET. This file is exported for a
|
|
11
|
+
* later integration pass to attach as a new subcommand of the existing
|
|
12
|
+
* `swarm` command (`v3/@claude-flow/cli/src/commands/swarm.ts`).
|
|
13
|
+
*/
|
|
14
|
+
import { output } from '../../output.js';
|
|
15
|
+
import { AGNTCY_NOT_CONFIGURED_MESSAGE, AGNTCY_PACKAGE_NAME, detectAgntcyRuntime, } from './runtime.js';
|
|
16
|
+
/**
|
|
17
|
+
* SLIM group-membership namespaces are slash-delimited (e.g.
|
|
18
|
+
* `cognitum/research/security`), matching the ADR-380 §2 example. Validate
|
|
19
|
+
* the shape locally — deterministic, no SDK required — before ever
|
|
20
|
+
* attempting a network join.
|
|
21
|
+
*/
|
|
22
|
+
export function validateNamespace(namespace) {
|
|
23
|
+
if (!namespace || namespace.trim().length === 0) {
|
|
24
|
+
return { valid: false, error: 'namespace must be a non-empty string' };
|
|
25
|
+
}
|
|
26
|
+
// Deliberately NOT filtering empty segments — a leading/trailing/double
|
|
27
|
+
// slash (e.g. "cognitum//security") is a malformed namespace, not a
|
|
28
|
+
// cosmetic one, and should be rejected rather than silently repaired.
|
|
29
|
+
const segments = namespace.split('/');
|
|
30
|
+
if (segments.length === 0) {
|
|
31
|
+
return { valid: false, error: 'namespace must contain at least one segment' };
|
|
32
|
+
}
|
|
33
|
+
const validSegment = /^[a-zA-Z0-9_-]+$/;
|
|
34
|
+
const bad = segments.find((s) => !validSegment.test(s));
|
|
35
|
+
if (bad !== undefined) {
|
|
36
|
+
return { valid: false, error: `invalid namespace segment "${bad}" — only [a-zA-Z0-9_-] allowed per segment` };
|
|
37
|
+
}
|
|
38
|
+
return { valid: true };
|
|
39
|
+
}
|
|
40
|
+
const joinCommand = {
|
|
41
|
+
name: 'join',
|
|
42
|
+
description: 'Join a SLIM group-membership channel scoped to a namespace (ADR-380 §2)',
|
|
43
|
+
examples: [
|
|
44
|
+
{ command: 'ruflo swarm join cognitum/research/security', description: 'Join the SLIM group-membership channel for a tenant namespace' },
|
|
45
|
+
],
|
|
46
|
+
action: async (ctx) => {
|
|
47
|
+
const namespace = (ctx.args[0] || ctx.flags.namespace || '').trim();
|
|
48
|
+
if (!namespace) {
|
|
49
|
+
output.printError('Namespace is required. Usage: ruflo swarm join <namespace> (e.g. cognitum/research/security)');
|
|
50
|
+
return { success: false, exitCode: 1 };
|
|
51
|
+
}
|
|
52
|
+
const nsCheck = validateNamespace(namespace);
|
|
53
|
+
if (!nsCheck.valid) {
|
|
54
|
+
output.printError(`Invalid namespace "${namespace}": ${nsCheck.error}`);
|
|
55
|
+
return { success: false, exitCode: 1 };
|
|
56
|
+
}
|
|
57
|
+
const status = await detectAgntcyRuntime();
|
|
58
|
+
if (!status.configured) {
|
|
59
|
+
output.printInfo(AGNTCY_NOT_CONFIGURED_MESSAGE);
|
|
60
|
+
output.printInfo(`Namespace "${namespace}" was not joined via SLIM. Local swarm/hive-mind coordination ` +
|
|
61
|
+
'(swarm_init / hive-mind_* MCP tools) remains available and unaffected.');
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
data: { joined: false, namespace, configured: false, reason: status.reason },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const mod = (await import(AGNTCY_PACKAGE_NAME));
|
|
69
|
+
if (typeof mod.joinGroup !== 'function') {
|
|
70
|
+
throw new Error(`"${AGNTCY_PACKAGE_NAME}" is installed but does not export joinGroup()`);
|
|
71
|
+
}
|
|
72
|
+
const result = await mod.joinGroup({ endpoint: status.endpoint, namespace });
|
|
73
|
+
output.printSuccess(`Joined SLIM group "${namespace}"${typeof result?.members === 'number' ? ` (${result.members} members)` : ''}.`);
|
|
74
|
+
return { success: true, data: { joined: true, namespace, members: result?.members } };
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
78
|
+
output.printError(`SLIM group join failed: ${message}`);
|
|
79
|
+
return { success: true, data: { joined: false, namespace, error: message } };
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
export { joinCommand as swarmJoinCommand };
|
|
84
|
+
export default joinCommand;
|
|
85
|
+
//# sourceMappingURL=swarm-join.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI `ruflo transport use slim` — ADR-380 §2.
|
|
3
|
+
*
|
|
4
|
+
* Switches the active swarm/hive-mind coordination transport from today's
|
|
5
|
+
* in-process/local-hooks routing to SLIM (secure messaging for MCP/A2A,
|
|
6
|
+
* hierarchical routing, group membership, MLS encryption) for agents
|
|
7
|
+
* coordinating across hosts or across a tenant boundary.
|
|
8
|
+
*
|
|
9
|
+
* Local transport stays the default. This command is a NO-OP on the local
|
|
10
|
+
* path unless an operator has both set {@link AGNTCY_ENDPOINT_ENV} AND
|
|
11
|
+
* installed the (not-yet-published) optional runtime package — see
|
|
12
|
+
* runtime.ts for the full graceful-degradation contract.
|
|
13
|
+
*
|
|
14
|
+
* NOT WIRED INTO THE MAIN CLI ROUTER YET. This file is exported for a
|
|
15
|
+
* later integration pass to attach as a new top-level `transport` command
|
|
16
|
+
* (or a subcommand of one, per the open question in ADR-380 §"Open
|
|
17
|
+
* Questions" about per-swarm vs. global scope).
|
|
18
|
+
*/
|
|
19
|
+
import type { Command } from '../../types.js';
|
|
20
|
+
declare const useCommand: Command;
|
|
21
|
+
export declare const transportCommand: Command;
|
|
22
|
+
export { useCommand };
|
|
23
|
+
export default transportCommand;
|
|
24
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI `ruflo transport use slim` — ADR-380 §2.
|
|
3
|
+
*
|
|
4
|
+
* Switches the active swarm/hive-mind coordination transport from today's
|
|
5
|
+
* in-process/local-hooks routing to SLIM (secure messaging for MCP/A2A,
|
|
6
|
+
* hierarchical routing, group membership, MLS encryption) for agents
|
|
7
|
+
* coordinating across hosts or across a tenant boundary.
|
|
8
|
+
*
|
|
9
|
+
* Local transport stays the default. This command is a NO-OP on the local
|
|
10
|
+
* path unless an operator has both set {@link AGNTCY_ENDPOINT_ENV} AND
|
|
11
|
+
* installed the (not-yet-published) optional runtime package — see
|
|
12
|
+
* runtime.ts for the full graceful-degradation contract.
|
|
13
|
+
*
|
|
14
|
+
* NOT WIRED INTO THE MAIN CLI ROUTER YET. This file is exported for a
|
|
15
|
+
* later integration pass to attach as a new top-level `transport` command
|
|
16
|
+
* (or a subcommand of one, per the open question in ADR-380 §"Open
|
|
17
|
+
* Questions" about per-swarm vs. global scope).
|
|
18
|
+
*/
|
|
19
|
+
import { output } from '../../output.js';
|
|
20
|
+
import { AGNTCY_NOT_CONFIGURED_MESSAGE, AGNTCY_PACKAGE_NAME, detectAgntcyRuntime, } from './runtime.js';
|
|
21
|
+
/** Transport names this command currently recognizes. Only 'slim' per ADR-380 §2. */
|
|
22
|
+
const SUPPORTED_TRANSPORTS = new Set(['slim']);
|
|
23
|
+
const useCommand = {
|
|
24
|
+
name: 'use',
|
|
25
|
+
description: 'Switch the active swarm/hive-mind transport (e.g. slim) — ADR-380 §2',
|
|
26
|
+
examples: [
|
|
27
|
+
{ command: 'ruflo transport use slim', description: 'Switch coordination transport to SLIM (opt-in, degrades to local)' },
|
|
28
|
+
],
|
|
29
|
+
action: async (ctx) => {
|
|
30
|
+
const requested = (ctx.args[0] || ctx.flags.transport || '').trim().toLowerCase();
|
|
31
|
+
if (!requested) {
|
|
32
|
+
output.printError('Transport name required. Usage: ruflo transport use <name> (e.g. slim)');
|
|
33
|
+
return { success: false, exitCode: 1 };
|
|
34
|
+
}
|
|
35
|
+
if (!SUPPORTED_TRANSPORTS.has(requested)) {
|
|
36
|
+
output.printError(`Unknown transport "${requested}". Supported: ${Array.from(SUPPORTED_TRANSPORTS).join(', ')}.`);
|
|
37
|
+
output.printInfo('Local transport remains the default and needs no explicit "use".');
|
|
38
|
+
return { success: false, exitCode: 1 };
|
|
39
|
+
}
|
|
40
|
+
const status = await detectAgntcyRuntime();
|
|
41
|
+
if (!status.configured) {
|
|
42
|
+
output.printInfo(AGNTCY_NOT_CONFIGURED_MESSAGE);
|
|
43
|
+
output.printInfo('Active transport remains: local (in-process hooks routing).');
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
data: { transport: 'local', requested, configured: false, reason: status.reason },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Reachable only once the optional package is actually installed AND
|
|
50
|
+
// an endpoint is configured — not possible today (package unpublished).
|
|
51
|
+
try {
|
|
52
|
+
const mod = (await import(AGNTCY_PACKAGE_NAME));
|
|
53
|
+
if (typeof mod.createSlimTransport !== 'function') {
|
|
54
|
+
throw new Error(`"${AGNTCY_PACKAGE_NAME}" is installed but does not export createSlimTransport()`);
|
|
55
|
+
}
|
|
56
|
+
await mod.createSlimTransport({ endpoint: status.endpoint });
|
|
57
|
+
output.printSuccess(`Active transport switched to: slim (${status.endpoint})`);
|
|
58
|
+
return { success: true, data: { transport: 'slim', endpoint: status.endpoint, configured: true } };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62
|
+
output.printError(`Failed to activate SLIM transport: ${message}`);
|
|
63
|
+
output.printInfo('Falling back to local transport.');
|
|
64
|
+
return { success: true, data: { transport: 'local', requested, configured: false, error: message } };
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
export const transportCommand = {
|
|
69
|
+
name: 'transport',
|
|
70
|
+
description: 'Manage the active swarm/hive-mind coordination transport (ADR-380 §2)',
|
|
71
|
+
subcommands: [useCommand],
|
|
72
|
+
examples: [
|
|
73
|
+
{ command: 'ruflo transport use slim', description: 'Switch coordination transport to SLIM' },
|
|
74
|
+
],
|
|
75
|
+
action: async () => {
|
|
76
|
+
output.writeln();
|
|
77
|
+
output.writeln(output.bold('AGNTCY/SLIM Transport'));
|
|
78
|
+
output.writeln(output.dim('='.repeat(60)));
|
|
79
|
+
output.writeln();
|
|
80
|
+
output.printBox([
|
|
81
|
+
'Local transport (in-process hooks routing) is the default.',
|
|
82
|
+
'',
|
|
83
|
+
'Subcommands:',
|
|
84
|
+
'',
|
|
85
|
+
' use <name> Switch the active transport (currently: slim)',
|
|
86
|
+
'',
|
|
87
|
+
`See ADR-380 for setup: v3/docs/adr/ADR-380-agntcy-outshift-runtime-integration.md`,
|
|
88
|
+
].join('\n'), 'AGNTCY/SLIM Transport');
|
|
89
|
+
output.writeln();
|
|
90
|
+
return { success: true };
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
export { useCommand };
|
|
94
|
+
export default transportCommand;
|
|
95
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../output.js';
|
|
6
6
|
import { getDaemon, startDaemon, stopDaemon } from '../services/worker-daemon.js';
|
|
7
|
+
import { resolveDaemonProjectRoot } from '../services/daemon-autostart.js';
|
|
7
8
|
import { fork } from 'child_process';
|
|
8
9
|
import { fileURLToPath } from 'url';
|
|
9
10
|
import { dirname, join, resolve } from 'path';
|
|
@@ -61,7 +62,11 @@ const startCommand = {
|
|
|
61
62
|
}
|
|
62
63
|
// #1914: a forked daemon child receives --workspace <root>; the launcher
|
|
63
64
|
// and interactive invocations have no flag and fall back to cwd.
|
|
64
|
-
|
|
65
|
+
// #2877: that cwd fallback is normalized to the owning project root, so
|
|
66
|
+
// `daemon start` from a subdirectory contends for the SAME lock/PID file
|
|
67
|
+
// as one from the root instead of silently keying its own pair.
|
|
68
|
+
const projectRoot = resolveWorkspaceFlag(ctx.flags.workspace)
|
|
69
|
+
?? resolveDaemonProjectRoot(process.cwd());
|
|
65
70
|
const isDaemonProcess = process.env.CLAUDE_FLOW_DAEMON === '1';
|
|
66
71
|
// Parse resource threshold overrides from CLI flags
|
|
67
72
|
const config = {};
|
|
@@ -605,7 +610,7 @@ const stopCommand = {
|
|
|
605
610
|
],
|
|
606
611
|
action: async (ctx) => {
|
|
607
612
|
const quiet = ctx.flags.quiet;
|
|
608
|
-
const projectRoot = process.cwd();
|
|
613
|
+
const projectRoot = resolveDaemonProjectRoot(process.cwd());
|
|
609
614
|
// #2661: `stop --all` — the containment lever for daemon fanout across
|
|
610
615
|
// Git worktrees. Only processes positively identified as ruflo daemons
|
|
611
616
|
// (via their self-identifying argv) are touched; each receives SIGTERM
|
|
@@ -657,7 +662,7 @@ async function stopAllDaemons(quiet) {
|
|
|
657
662
|
await stopDaemon();
|
|
658
663
|
}
|
|
659
664
|
catch { /* not running in-process */ }
|
|
660
|
-
await killBackgroundDaemon(process.cwd());
|
|
665
|
+
await killBackgroundDaemon(resolveDaemonProjectRoot(process.cwd()));
|
|
661
666
|
const daemons = await scanRunningDaemons();
|
|
662
667
|
if (daemons.length === 0) {
|
|
663
668
|
if (!quiet) {
|
|
@@ -1160,7 +1165,7 @@ const statusCommand = {
|
|
|
1160
1165
|
if (ctx.flags.all) {
|
|
1161
1166
|
return renderAllDaemonsStatus();
|
|
1162
1167
|
}
|
|
1163
|
-
const projectRoot = process.cwd();
|
|
1168
|
+
const projectRoot = resolveDaemonProjectRoot(process.cwd());
|
|
1164
1169
|
try {
|
|
1165
1170
|
const daemon = getDaemon(projectRoot);
|
|
1166
1171
|
const status = daemon.getStatus();
|
|
@@ -1304,7 +1309,7 @@ const triggerCommand = {
|
|
|
1304
1309
|
// #2661: an explicit `trigger --headless` is user consent for AI
|
|
1305
1310
|
// execution of THIS run (still governed by the global AI budget).
|
|
1306
1311
|
// Without the flag, config.json / env opt-in still applies.
|
|
1307
|
-
const daemon = getDaemon(process.cwd(), ctx.flags.headless === true ? { aiWorkersEnabled: true } : undefined);
|
|
1312
|
+
const daemon = getDaemon(resolveDaemonProjectRoot(process.cwd()), ctx.flags.headless === true ? { aiWorkersEnabled: true } : undefined);
|
|
1308
1313
|
const spinner = output.createSpinner({ text: `Running ${workerType} worker...`, spinner: 'dots' });
|
|
1309
1314
|
spinner.start();
|
|
1310
1315
|
const result = await daemon.triggerWorker(workerType);
|
|
@@ -1347,7 +1352,7 @@ const enableCommand = {
|
|
|
1347
1352
|
return { success: false, exitCode: 1 };
|
|
1348
1353
|
}
|
|
1349
1354
|
try {
|
|
1350
|
-
const daemon = getDaemon(process.cwd());
|
|
1355
|
+
const daemon = getDaemon(resolveDaemonProjectRoot(process.cwd()));
|
|
1351
1356
|
daemon.setWorkerEnabled(workerType, !disable);
|
|
1352
1357
|
output.printSuccess(`Worker ${workerType} ${disable ? 'disabled' : 'enabled'}`);
|
|
1353
1358
|
return { success: true };
|
|
@@ -1402,7 +1407,7 @@ const installSupervisorCommand = {
|
|
|
1402
1407
|
const force = ctx.flags.force === true;
|
|
1403
1408
|
const load = ctx.flags.load !== false;
|
|
1404
1409
|
const dryRun = ctx.flags['dry-run'] === true || ctx.flags.dryRun === true;
|
|
1405
|
-
const projectRoot = process.cwd();
|
|
1410
|
+
const projectRoot = resolveDaemonProjectRoot(process.cwd());
|
|
1406
1411
|
const platform = process.platform;
|
|
1407
1412
|
if (platform === 'win32') {
|
|
1408
1413
|
output.printError('Windows scheduled-task installer is not yet implemented.');
|
|
@@ -1766,6 +1766,76 @@ async function checkMetaharnessIntegration() {
|
|
|
1766
1766
|
};
|
|
1767
1767
|
}
|
|
1768
1768
|
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Dependency-contract check: every `@metaharness/*` package this CLI DECLARES
|
|
1771
|
+
* in its own optionalDependencies must actually resolve at runtime. Declared
|
|
1772
|
+
* packages are advertised integration surfaces (`ruflo metaharness evolve`,
|
|
1773
|
+
* flywheel receipt interop, radio coordination) — a declared-but-absent
|
|
1774
|
+
* package means the install dropped an optional dep (or the declaration
|
|
1775
|
+
* regressed to peer-only), so the advertised integration silently degrades.
|
|
1776
|
+
* That is a FAIL, not a warn: warn is reserved for surfaces that were never
|
|
1777
|
+
* advertised as installed (the `npx metaharness` umbrella path above).
|
|
1778
|
+
*/
|
|
1779
|
+
async function checkMetaharnessDeclaredPackages() {
|
|
1780
|
+
const NAME = 'MetaHarness declared packages (ADR-150)';
|
|
1781
|
+
try {
|
|
1782
|
+
// Walk up from this module to the CLI package root (works for npx cache,
|
|
1783
|
+
// global install, project-local install, and monorepo dev alike).
|
|
1784
|
+
let root = null;
|
|
1785
|
+
let q = dirname(fileURLToPath(import.meta.url));
|
|
1786
|
+
for (let i = 0; i < 8; i++) {
|
|
1787
|
+
const pj = join(q, 'package.json');
|
|
1788
|
+
if (existsSync(pj)) {
|
|
1789
|
+
try {
|
|
1790
|
+
if (JSON.parse(readFileSync(pj, 'utf-8')).name === '@claude-flow/cli') {
|
|
1791
|
+
root = q;
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
catch { /* keep walking */ }
|
|
1796
|
+
}
|
|
1797
|
+
q = dirname(q);
|
|
1798
|
+
}
|
|
1799
|
+
if (!root)
|
|
1800
|
+
return { name: NAME, status: 'warn', message: 'could not locate the @claude-flow/cli package root' };
|
|
1801
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8'));
|
|
1802
|
+
const declared = Object.keys(pkg.optionalDependencies ?? {}).filter((n) => n === 'metaharness' || n.startsWith('@metaharness/'));
|
|
1803
|
+
if (declared.length === 0) {
|
|
1804
|
+
return {
|
|
1805
|
+
name: NAME,
|
|
1806
|
+
status: 'fail',
|
|
1807
|
+
message: 'no @metaharness/* packages declared in optionalDependencies — the dependency contract regressed (peer-only declarations are never installed)',
|
|
1808
|
+
fix: 'Restore @metaharness/darwin, @metaharness/flywheel, @metaharness/radio to optionalDependencies in @claude-flow/cli',
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
// Node resolution: nearest node_modules wins, walking upward from the CLI root.
|
|
1812
|
+
const resolves = (name) => {
|
|
1813
|
+
let d = root;
|
|
1814
|
+
for (let i = 0; i < 10; i++) {
|
|
1815
|
+
if (existsSync(join(d, 'node_modules', name, 'package.json')))
|
|
1816
|
+
return true;
|
|
1817
|
+
const parent = dirname(d);
|
|
1818
|
+
if (parent === d)
|
|
1819
|
+
break;
|
|
1820
|
+
d = parent;
|
|
1821
|
+
}
|
|
1822
|
+
return false;
|
|
1823
|
+
};
|
|
1824
|
+
const missing = declared.filter((n) => !resolves(n));
|
|
1825
|
+
if (missing.length > 0) {
|
|
1826
|
+
return {
|
|
1827
|
+
name: NAME,
|
|
1828
|
+
status: 'fail',
|
|
1829
|
+
message: `declared but not installed: ${missing.join(', ')} — advertised MetaHarness surfaces are degraded`,
|
|
1830
|
+
fix: 'npm install --include=optional # optional deps were skipped or pruned',
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
return { name: NAME, status: 'pass', message: `${declared.length} declared package(s) resolve: ${declared.join(', ')}` };
|
|
1834
|
+
}
|
|
1835
|
+
catch (err) {
|
|
1836
|
+
return { name: NAME, status: 'warn', message: `check failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1769
1839
|
async function checkMetaharness() {
|
|
1770
1840
|
try {
|
|
1771
1841
|
const version = await runCommand('npx -y metaharness@latest --version 2>&1', 15000);
|
|
@@ -2116,6 +2186,7 @@ export const doctorCommand = {
|
|
|
2116
2186
|
checkEncryptionAtRest, // ADR-096 Phase 5
|
|
2117
2187
|
checkFederationBreaker, // ADR-097 Phase 4
|
|
2118
2188
|
checkMetaharness, // ADR-150 — MetaHarness upstream package
|
|
2189
|
+
checkMetaharnessDeclaredPackages, // dependency contract — declared optional deps must resolve
|
|
2119
2190
|
checkMetaharnessIntegration, // iter 45 — ruflo-side integration layer
|
|
2120
2191
|
checkFunnel, // ADR-305 — effective funnel state + deciding precedence source
|
|
2121
2192
|
checkProxySponsoredConsent, // ADR-313 — Meta LLM Proxy sponsored-downtime health
|
|
@@ -2158,7 +2229,7 @@ export const doctorCommand = {
|
|
|
2158
2229
|
'agentic-flow': checkAgenticFlow,
|
|
2159
2230
|
'encryption': checkEncryptionAtRest, // ADR-096 Phase 5
|
|
2160
2231
|
'federation': checkFederationBreaker, // ADR-097 Phase 4
|
|
2161
|
-
'metaharness': checkMetaharness, // ADR-150 — upstream
|
|
2232
|
+
'metaharness': [checkMetaharness, checkMetaharnessDeclaredPackages, checkMetaharnessIntegration], // ADR-150 — upstream + declared deps + ruflo-side
|
|
2162
2233
|
'metaharness-integration': checkMetaharnessIntegration, // iter 45 — ruflo-side
|
|
2163
2234
|
'funnel': checkFunnel, // ADR-305
|
|
2164
2235
|
// ADR-307 — deep-dive array, same pattern as 'memory' above: the cheap
|
|
@@ -45,6 +45,7 @@ export declare function getGuidanceCommand(): Promise<Command | undefined>;
|
|
|
45
45
|
export declare function getApplianceCommand(): Promise<Command | undefined>;
|
|
46
46
|
export declare function getCleanupCommand(): Promise<Command | undefined>;
|
|
47
47
|
export declare function getAutopilotCommand(): Promise<Command | undefined>;
|
|
48
|
+
export declare function getTransportCommand(): Promise<Command | undefined>;
|
|
48
49
|
/**
|
|
49
50
|
* Core commands loaded synchronously (available immediately)
|
|
50
51
|
* Advanced commands loaded on-demand for faster startup
|
|
@@ -90,6 +90,10 @@ const commandLoaders = {
|
|
|
90
90
|
spinner: () => import('./spinner.js'),
|
|
91
91
|
// Ruflo entries in Claude Code's companyAnnouncements startup rotation (ADR-319)
|
|
92
92
|
announcements: () => import('./announcements.js'),
|
|
93
|
+
// AGNTCY/Outshift runtime transport selection (ADR-324 §2) — optional,
|
|
94
|
+
// removable augmentation; no-ops to local transport when AGNTCY/SLIM is
|
|
95
|
+
// not configured (RUFLO_AGNTCY_SLIM_ENDPOINT unset).
|
|
96
|
+
transport: () => import('./agntcy/transport.js'),
|
|
93
97
|
};
|
|
94
98
|
// Cache for loaded commands
|
|
95
99
|
const loadedCommands = new Map();
|
|
@@ -188,6 +192,7 @@ export async function getGuidanceCommand() { return loadCommand('guidance'); }
|
|
|
188
192
|
export async function getApplianceCommand() { return loadCommand('appliance'); }
|
|
189
193
|
export async function getCleanupCommand() { return loadCommand('cleanup'); }
|
|
190
194
|
export async function getAutopilotCommand() { return loadCommand('autopilot'); }
|
|
195
|
+
export async function getTransportCommand() { return loadCommand('transport'); }
|
|
191
196
|
/**
|
|
192
197
|
* Core commands loaded synchronously (available immediately)
|
|
193
198
|
* Advanced commands loaded on-demand for faster startup
|
|
@@ -39,7 +39,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
39
39
|
import { join, dirname, resolve } from 'path';
|
|
40
40
|
import { fileURLToPath } from 'url';
|
|
41
41
|
import { runFlywheelWorker } from '../services/harness-flywheel-runtime.js';
|
|
42
|
-
import { listFlywheelReceipts, promoteFlywheelCandidate, readFlywheelTransactionState, verifyFlywheelLedger, } from '../services/flywheel-transaction.js';
|
|
42
|
+
import { listFlywheelReceipts, promoteFlywheelCandidate, readFlywheelTransactionState, resetSequentialEvidence, verifyFlywheelLedger, } from '../services/flywheel-transaction.js';
|
|
43
43
|
import { evaluatePolicyRequest } from '../services/policy-runtime.js';
|
|
44
44
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
45
45
|
// Subcommand → plugin script filename
|
|
@@ -124,6 +124,37 @@ async function dispatchFlywheel(operation, positional, flags) {
|
|
|
124
124
|
const state = readFlywheelTransactionState(projectRoot);
|
|
125
125
|
data = { ledgerHead: state.ledgerHead, commits: state.commits };
|
|
126
126
|
}
|
|
127
|
+
else if (operation === 'evidence-reset') {
|
|
128
|
+
// ADR-381 §2 — start a new evidence epoch. Same policy gate as promote:
|
|
129
|
+
// this reopens the family-wise alpha budget, so it is a governance action.
|
|
130
|
+
const reason = String(flywheelFlag(flags, 'reason', '')).trim();
|
|
131
|
+
if (!reason) {
|
|
132
|
+
data = { success: false, reason: 'evidence-reset requires --reason "<why>" — the reset audit trail records intent' };
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const policy = await evaluatePolicyRequest({
|
|
136
|
+
identity: { id: process.env.CLAUDE_FLOW_PRINCIPAL_ID ?? 'metaharness-local', type: 'agent', roles: ['optimizer'] },
|
|
137
|
+
action: {
|
|
138
|
+
type: 'metaharness.evidence.reset',
|
|
139
|
+
resource: projectRoot,
|
|
140
|
+
environment: 'production',
|
|
141
|
+
destructive: true,
|
|
142
|
+
},
|
|
143
|
+
context: {
|
|
144
|
+
approvalIds: String(flywheelFlag(flags, 'approvalId', '')).split(',').filter(Boolean),
|
|
145
|
+
},
|
|
146
|
+
}, projectRoot);
|
|
147
|
+
if (policy.enforcedOutcome !== 'allowed') {
|
|
148
|
+
data = { success: false, reason: `policy-${policy.enforcedOutcome}:${policy.reason}`, policyReceiptId: policy.receiptId };
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
data = await resetSequentialEvidence(projectRoot, {
|
|
152
|
+
confirm: flywheelFlag(flags, 'confirm', false) === true,
|
|
153
|
+
reason,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
127
158
|
else if (operation === 'promote') {
|
|
128
159
|
const receiptId = positional[0];
|
|
129
160
|
const publicKeyPath = flywheelFlag(flags, 'publicKey');
|
|
@@ -151,6 +182,10 @@ async function dispatchFlywheel(operation, positional, flags) {
|
|
|
151
182
|
data = await promoteFlywheelCandidate(projectRoot, receiptId, {
|
|
152
183
|
confirm: flywheelFlag(flags, 'confirm', false) === true,
|
|
153
184
|
trustedPublicKeys: new Set([publicKey]),
|
|
185
|
+
// --allow-aggregate-evidence: explicit migration escape hatch for
|
|
186
|
+
// pre-upgrade receipts without task-level pairedOutcomes. Default
|
|
187
|
+
// is strict — aggregate-only evidence is refused.
|
|
188
|
+
requirePairedEvidence: flywheelFlag(flags, 'allowAggregateEvidence', false) !== true,
|
|
154
189
|
});
|
|
155
190
|
}
|
|
156
191
|
}
|
|
@@ -288,7 +323,7 @@ export const metaharnessCommand = {
|
|
|
288
323
|
output.writeln(' redblue adversarial red/blue LLM testing (init|run|patch|attack|report)');
|
|
289
324
|
output.writeln(' evolve Darwin candidate evolution');
|
|
290
325
|
output.writeln(' bench create or verify a stable benchmark suite');
|
|
291
|
-
output.writeln(' flywheel receipt loop: run | status | receipts | history | promote');
|
|
326
|
+
output.writeln(' flywheel receipt loop: run | status | receipts | history | promote | evidence-reset');
|
|
292
327
|
output.writeln('');
|
|
293
328
|
output.writeln('Each subcommand accepts --format json|table and --help.');
|
|
294
329
|
output.writeln('');
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { output } from '../output.js';
|
|
6
6
|
import { select, confirm } from '../prompt.js';
|
|
7
7
|
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
8
|
+
import { swarmJoinCommand } from './agntcy/swarm-join.js';
|
|
8
9
|
import * as fs from 'fs';
|
|
9
10
|
import * as path from 'path';
|
|
10
11
|
// Get dynamic swarm status from memory/session files
|
|
@@ -1009,7 +1010,7 @@ const pheromoneCommand = {
|
|
|
1009
1010
|
export const swarmCommand = {
|
|
1010
1011
|
name: 'swarm',
|
|
1011
1012
|
description: 'Swarm coordination commands',
|
|
1012
|
-
subcommands: [initCommand, startCommand, statusCommand, stopCommand, scaleCommand, coordinateCommand, compressMessageCommand, pheromoneCommand],
|
|
1013
|
+
subcommands: [initCommand, startCommand, statusCommand, stopCommand, scaleCommand, coordinateCommand, compressMessageCommand, pheromoneCommand, swarmJoinCommand],
|
|
1013
1014
|
options: [],
|
|
1014
1015
|
examples: [
|
|
1015
1016
|
{ command: 'claude-flow swarm init --v3-mode', description: 'Initialize V3 swarm' },
|
|
@@ -40,7 +40,7 @@ declare const STDERR_REDIRECT_PREFIXES: string[];
|
|
|
40
40
|
declare const AGENTDB_MOCK_FALLBACK_DROP_PREFIXES: string[];
|
|
41
41
|
declare const shouldRedirectToStderr: (msg: unknown) => boolean;
|
|
42
42
|
declare const isAgentdbMockFallbackNoise: (msg: unknown) => boolean;
|
|
43
|
-
declare const origWarn: (
|
|
44
|
-
declare const origLog: (
|
|
45
|
-
declare const origError: (
|
|
43
|
+
declare const origWarn: (...data: any[]) => void;
|
|
44
|
+
declare const origLog: (...data: any[]) => void;
|
|
45
|
+
declare const origError: (...data: any[]) => void;
|
|
46
46
|
//# sourceMappingURL=log-filters.d.ts.map
|
|
@@ -53,7 +53,7 @@ import { join, dirname, resolve } from 'node:path';
|
|
|
53
53
|
import { fileURLToPath } from 'node:url';
|
|
54
54
|
import { runFlywheelWorker } from '../services/harness-flywheel-runtime.js';
|
|
55
55
|
import { evaluatePolicyRequest } from '../services/policy-runtime.js';
|
|
56
|
-
import { listFlywheelReceipts, promoteFlywheelCandidate, readFlywheelTransactionState, verifyFlywheelLedger, } from '../services/flywheel-transaction.js';
|
|
56
|
+
import { listFlywheelReceipts, promoteFlywheelCandidate, readFlywheelTransactionState, resetSequentialEvidence, verifyFlywheelLedger, } from '../services/flywheel-transaction.js';
|
|
57
57
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
58
58
|
/**
|
|
59
59
|
* Walk up from this module to find plugins/ruflo-metaharness/scripts/.
|
|
@@ -690,7 +690,7 @@ export const metaharnessTools = [
|
|
|
690
690
|
inputSchema: {
|
|
691
691
|
type: 'object',
|
|
692
692
|
properties: {
|
|
693
|
-
operation: { type: 'string', enum: ['run', 'status', 'receipts', 'history', 'promote'], description: 'Flywheel operation', default: 'status' },
|
|
693
|
+
operation: { type: 'string', enum: ['run', 'status', 'receipts', 'history', 'promote', 'evidence-reset'], description: 'Flywheel operation', default: 'status' },
|
|
694
694
|
projectRoot: { type: 'string', description: 'Target project root (default: active project cwd)' },
|
|
695
695
|
receiptId: { type: 'string', description: 'Receipt content ID for promote' },
|
|
696
696
|
sample: { type: 'number', description: 'Maximum harvested evaluation sample', default: 40 },
|
|
@@ -704,6 +704,8 @@ export const metaharnessTools = [
|
|
|
704
704
|
anchorHash: { type: 'string', description: 'Pinned sha256 of canonical anchor tasks; requires anchorPath' },
|
|
705
705
|
anchorManifestPath: { type: 'string', description: 'Project-contained anchor manifest path (default .claude/eval/flywheel-anchor.manifest.json)' },
|
|
706
706
|
approvalIds: { type: 'array', items: { type: 'string' }, description: 'Scoped ADR-324 approval IDs for privileged promotion' },
|
|
707
|
+
allowAggregateEvidence: { type: 'boolean', description: 'Migration escape hatch: accept a pre-upgrade receipt without task-level pairedOutcomes. Default false — aggregate-only evidence is refused by the strict sequential-evidence gate.', default: false },
|
|
708
|
+
reason: { type: 'string', description: 'Required for evidence-reset (ADR-381): the human intent recorded in the append-only reset audit trail.' },
|
|
707
709
|
},
|
|
708
710
|
required: ['operation'],
|
|
709
711
|
},
|
|
@@ -758,6 +760,36 @@ export const metaharnessTools = [
|
|
|
758
760
|
});
|
|
759
761
|
return { success: data.ran, data, degraded: false, exitCode: data.ran ? 0 : 1 };
|
|
760
762
|
}
|
|
763
|
+
if (operation === 'evidence-reset') {
|
|
764
|
+
// ADR-381 §2 — reopens the family-wise alpha budget; same policy
|
|
765
|
+
// gate as promote, plus a mandatory recorded reason.
|
|
766
|
+
const reason = String(input.reason ?? '').trim();
|
|
767
|
+
if (!reason) {
|
|
768
|
+
return { success: false, data: { reason: 'evidence-reset requires a non-empty reason — the reset audit trail records intent' }, degraded: false, exitCode: 2 };
|
|
769
|
+
}
|
|
770
|
+
const policy = await evaluatePolicyRequest({
|
|
771
|
+
identity: { id: 'metaharness-mcp', type: 'agent', roles: ['optimizer'] },
|
|
772
|
+
action: {
|
|
773
|
+
type: 'metaharness.evidence.reset',
|
|
774
|
+
resource: projectRoot,
|
|
775
|
+
environment: 'production',
|
|
776
|
+
destructive: true,
|
|
777
|
+
},
|
|
778
|
+
context: {
|
|
779
|
+
approvalIds: Array.isArray(input.approvalIds) ? input.approvalIds.map(String) : undefined,
|
|
780
|
+
},
|
|
781
|
+
}, projectRoot);
|
|
782
|
+
if (policy.enforcedOutcome !== 'allowed') {
|
|
783
|
+
return {
|
|
784
|
+
success: false,
|
|
785
|
+
data: { reason: `policy-${policy.enforcedOutcome}:${policy.reason}`, policyReceiptId: policy.receiptId },
|
|
786
|
+
degraded: false,
|
|
787
|
+
exitCode: 1,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
const data = await resetSequentialEvidence(projectRoot, { confirm: input.confirm === true, reason });
|
|
791
|
+
return { success: data.success, data, degraded: false, exitCode: data.success ? 0 : 1 };
|
|
792
|
+
}
|
|
761
793
|
if (operation === 'promote') {
|
|
762
794
|
if (!input.receiptId || !input.publicKeyPath) {
|
|
763
795
|
return { success: false, data: { reason: 'receiptId and publicKeyPath are required' }, degraded: false, exitCode: 2 };
|
|
@@ -786,6 +818,7 @@ export const metaharnessTools = [
|
|
|
786
818
|
const data = await promoteFlywheelCandidate(projectRoot, String(input.receiptId), {
|
|
787
819
|
confirm: input.confirm === true,
|
|
788
820
|
trustedPublicKeys: new Set([publicKey]),
|
|
821
|
+
requirePairedEvidence: input.allowAggregateEvidence !== true,
|
|
789
822
|
});
|
|
790
823
|
return { success: data.success, data, degraded: false, exitCode: data.success ? 0 : 1 };
|
|
791
824
|
}
|
|
@@ -487,6 +487,14 @@ export declare function bridgeHealthCheck(dbPath?: string): Promise<{
|
|
|
487
487
|
hits: number;
|
|
488
488
|
misses: number;
|
|
489
489
|
};
|
|
490
|
+
hierarchicalMemory?: {
|
|
491
|
+
controller: string;
|
|
492
|
+
durable: boolean;
|
|
493
|
+
persistence: string;
|
|
494
|
+
/** Real row count in the backing table — null when nothing is on disk. */
|
|
495
|
+
persistedRows: number | null;
|
|
496
|
+
fallbackFrom?: string;
|
|
497
|
+
};
|
|
490
498
|
} | null>;
|
|
491
499
|
/**
|
|
492
500
|
* Store to hierarchical memory with tier.
|
|
@@ -506,6 +514,23 @@ export declare function bridgeHealthCheck(dbPath?: string): Promise<{
|
|
|
506
514
|
* temporal fields are stored in metadata, and supersede is reported as
|
|
507
515
|
* unsupported (no public update API) rather than silently dropped.
|
|
508
516
|
*/
|
|
517
|
+
/**
|
|
518
|
+
* Describe which store `agentdb_hierarchical-*` actually landed on and
|
|
519
|
+
* whether its writes survive the process (#2887).
|
|
520
|
+
*
|
|
521
|
+
* agentdb removed its `HierarchicalMemory` export at 3.0.0-alpha.17, so the
|
|
522
|
+
* @claude-flow/memory TieredMemoryStore fallback is the live path. It is only
|
|
523
|
+
* durable when it was handed a SQLite connection — callers must not report a
|
|
524
|
+
* volatile write as a success.
|
|
525
|
+
*/
|
|
526
|
+
export declare function describeHierarchicalStore(hm: any, fallback: {
|
|
527
|
+
reason: string;
|
|
528
|
+
} | null): {
|
|
529
|
+
controller: string;
|
|
530
|
+
fallbackFrom?: string;
|
|
531
|
+
durable: boolean;
|
|
532
|
+
persistence: string;
|
|
533
|
+
};
|
|
509
534
|
export declare function bridgeHierarchicalStore(params: {
|
|
510
535
|
key: string;
|
|
511
536
|
value: string;
|