@claude-flow/cli 3.54.1 → 3.55.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.
Files changed (60) hide show
  1. package/.claude/helpers/helpers.manifest.json +2 -2
  2. package/bin/cli.js +1 -1
  3. package/bin/mcp-server.js +1 -1
  4. package/catalog-manifest.json +2 -2
  5. package/dist/src/commands/autopilot.js +2 -2
  6. package/dist/src/commands/daemon.js +21 -1
  7. package/dist/src/commands/hive-mind.js +7 -0
  8. package/dist/src/commands/mcp.js +35 -1
  9. package/dist/src/commands/policy.js +10 -2
  10. package/dist/src/mcp-server.d.ts +33 -0
  11. package/dist/src/mcp-server.js +96 -4
  12. package/dist/src/mcp-tools/hive-mind-tools.d.ts +2 -4
  13. package/dist/src/mcp-tools/hive-mind-tools.js +214 -40
  14. package/dist/src/missions/schemas.d.ts +125 -125
  15. package/dist/src/mods/command-registry/schema.d.ts +199 -199
  16. package/dist/src/parser.d.ts +3 -0
  17. package/dist/src/parser.js +64 -13
  18. package/dist/src/production/retry.js +1 -1
  19. package/dist/src/services/bounded-worker-pool.js +3 -0
  20. package/dist/src/services/config-file-manager.d.ts +3 -0
  21. package/dist/src/services/config-file-manager.js +24 -7
  22. package/dist/src/services/policy-ledger-anchor.d.ts +42 -0
  23. package/dist/src/services/policy-ledger-anchor.js +163 -0
  24. package/dist/src/services/policy-runtime.d.ts +11 -5
  25. package/dist/src/services/policy-runtime.js +70 -15
  26. package/dist/src/services/worker-daemon.d.ts +1 -0
  27. package/dist/src/services/worker-daemon.js +12 -0
  28. package/dist/src/services/worker-queue.js +11 -2
  29. package/node_modules/@claude-flow/mcp/dist/connection-pool.d.ts.map +1 -1
  30. package/node_modules/@claude-flow/mcp/dist/connection-pool.js +5 -2
  31. package/node_modules/@claude-flow/mcp/dist/connection-pool.js.map +1 -1
  32. package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts +7 -1
  33. package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts.map +1 -1
  34. package/node_modules/@claude-flow/mcp/dist/transport/http.js +60 -47
  35. package/node_modules/@claude-flow/mcp/dist/transport/http.js.map +1 -1
  36. package/node_modules/@claude-flow/mcp/package.json +1 -1
  37. package/node_modules/@claude-flow/plugin-agent-federation/dist/application/federation-coordinator.js +1 -1
  38. package/node_modules/@claude-flow/plugin-agent-federation/dist/application/federation-coordinator.js.map +1 -1
  39. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  40. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.d.ts +3 -0
  41. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.d.ts.map +1 -1
  42. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.js +4 -1
  43. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.js.map +1 -1
  44. package/node_modules/@claude-flow/plugin-agent-federation/dist/plugin.d.ts.map +1 -1
  45. package/node_modules/@claude-flow/plugin-agent-federation/dist/plugin.js +1 -0
  46. package/node_modules/@claude-flow/plugin-agent-federation/dist/plugin.js.map +1 -1
  47. package/node_modules/@claude-flow/plugin-agent-federation/package.json +1 -1
  48. package/node_modules/@claude-flow/security/dist/policy/engine.d.ts +11 -1
  49. package/node_modules/@claude-flow/security/dist/policy/engine.d.ts.map +1 -1
  50. package/node_modules/@claude-flow/security/dist/policy/engine.js +19 -3
  51. package/node_modules/@claude-flow/security/dist/policy/engine.js.map +1 -1
  52. package/node_modules/@claude-flow/security/dist/policy/types.d.ts +4 -2
  53. package/node_modules/@claude-flow/security/dist/policy/types.d.ts.map +1 -1
  54. package/node_modules/@claude-flow/security/package.json +1 -1
  55. package/package.json +7 -7
  56. package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +1 -1
  57. package/plugins/ruflo-metaharness/scripts/_invoke.mjs +80 -7
  58. package/plugins/ruflo-metaharness/scripts/smoke.sh +4 -4
  59. package/plugins/ruflo-metaharness/scripts/test-cli-resolution-3558.mjs +141 -0
  60. package/plugins/ruflo-metaharness/scripts/test-no-tool-time-npx.mjs +5 -0
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ // test-cli-resolution-3558.mjs — regression test for #3558: in a Claude Code
3
+ // marketplace install, metaharness memory calls must use the ruflo CLI that
4
+ // is already installed on PATH, not `npx @claude-flow/cli@latest`.
5
+ //
6
+ // #3366 taught resolveRufloCli() (_invoke.mjs) two local layouts: the
7
+ // @claude-flow/cli that ships the plugin, and a repo checkout. A marketplace
8
+ // install is neither — the plugin is copied to
9
+ // ~/.claude/plugins/cache/<marketplace>/ruflo-metaharness/<version>/ — so it
10
+ // still fell through to npx @latest on a machine with ruflo installed.
11
+ //
12
+ // Hermetic: the plugin is copied into a temp plugin-cache layout; PATH holds
13
+ // only temp dirs with fake `ruflo` / `claude-flow` / `npx` scripts that log
14
+ // their argv. No network, no real CLI. The end-to-end cases use POSIX sh
15
+ // fakes and are skipped on win32; the win32 shim-mapping case runs everywhere.
16
+ //
17
+ // USAGE
18
+ // node --test plugins/ruflo-metaharness/scripts/test-cli-resolution-3558.mjs
19
+ import { test, before, after } from 'node:test';
20
+ import assert from 'node:assert/strict';
21
+ import { spawnSync } from 'node:child_process';
22
+ import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
23
+ import { tmpdir } from 'node:os';
24
+ import { delimiter, dirname, join } from 'node:path';
25
+ import { fileURLToPath, pathToFileURL } from 'node:url';
26
+
27
+ const PLUGIN_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
28
+ const posixOnly = { skip: process.platform === 'win32' ? 'fake CLIs are POSIX sh scripts' : false };
29
+
30
+ const write = (file, body, mode) => {
31
+ mkdirSync(dirname(file), { recursive: true });
32
+ writeFileSync(file, body);
33
+ if (mode) chmodSync(file, mode);
34
+ };
35
+
36
+ let TMP, CACHE, LOG, WITH_RUFLO, WITH_CLAUDE_FLOW, NPX_ONLY, PROJECT, baseEnv;
37
+ before(() => {
38
+ // realpath: macOS tmpdir() is /var/… while resolved paths come back as /private/var/…
39
+ TMP = realpathSync(mkdtempSync(join(tmpdir(), 'metaharness-cli-resolution-')));
40
+ const home = join(TMP, 'home');
41
+ CACHE = join(home, '.claude', 'plugins', 'cache', 'ruflo', 'ruflo-metaharness', '0.0.0-test');
42
+ cpSync(PLUGIN_ROOT, CACHE, { recursive: true });
43
+ LOG = join(TMP, 'calls.log');
44
+ const fake = (dir, name) => write(join(dir, name),
45
+ `#!/bin/sh\nprintf '%s %s\\n' "${name}" "$*" >> "${LOG}"\ncase "$*" in *"memory list"*) echo '[]' ;; *) echo '[OK] done' ;; esac\n`, 0o755);
46
+ WITH_RUFLO = join(TMP, 'bin-ruflo');
47
+ fake(WITH_RUFLO, 'ruflo');
48
+ fake(WITH_RUFLO, 'claude-flow');
49
+ WITH_CLAUDE_FLOW = join(TMP, 'bin-claude-flow');
50
+ fake(WITH_CLAUDE_FLOW, 'claude-flow');
51
+ NPX_ONLY = join(TMP, 'bin-npx');
52
+ fake(NPX_ONLY, 'npx');
53
+ PROJECT = join(TMP, 'project');
54
+ mkdirSync(PROJECT);
55
+ baseEnv = { ...process.env, HOME: home, USERPROFILE: home, RUFLO_METAHARNESS_CACHE_BASE: join(TMP, 'empty-cache-base') };
56
+ for (const k of ['CLI_CORE', 'RUFLO_PLUGIN_SKIP_LOCAL_CLI', 'RUFLO_PLUGIN_SKIP_PATH_CLI', 'NODE_OPTIONS']) delete baseEnv[k];
57
+ });
58
+ after(() => rmSync(TMP, { recursive: true, force: true }));
59
+
60
+ const calls = () => (existsSync(LOG) ? readFileSync(LOG, 'utf-8').trim().split('\n').filter(Boolean) : []);
61
+ /** resolveRufloCli() as seen from `scriptsDir`, in a fresh process. */
62
+ function resolved(env, scriptsDir = join(CACHE, 'scripts')) {
63
+ const src = `import { resolveRufloCli } from ${JSON.stringify(pathToFileURL(join(scriptsDir, '_invoke.mjs')).href)};\nconsole.log(JSON.stringify(resolveRufloCli()));`;
64
+ const r = spawnSync(process.execPath, ['--input-type=module', '-e', src], { cwd: PROJECT, encoding: 'utf-8', env: { ...baseEnv, ...env } });
65
+ assert.equal(r.status, 0, r.stderr);
66
+ return JSON.parse(r.stdout);
67
+ }
68
+ function auditList(env, scriptsDir = join(CACHE, 'scripts')) {
69
+ rmSync(LOG, { force: true });
70
+ const r = spawnSync(process.execPath, [join(scriptsDir, 'audit-list.mjs'), '--format', 'json'], { cwd: PROJECT, encoding: 'utf-8', timeout: 30_000, env: { ...baseEnv, ...env } });
71
+ assert.equal(r.status, 0, r.stderr);
72
+ return calls();
73
+ }
74
+ const pathOf = (...dirs) => dirs.join(delimiter);
75
+
76
+ test('win32: npm\'s ruflo.cmd shim maps to `node <package bin>`, never cmd.exe', async () => {
77
+ const { findRufloOnPath } = await import(pathToFileURL(join(PLUGIN_ROOT, 'scripts', '_invoke.mjs')).href);
78
+ const prefix = join(TMP, 'win-prefix');
79
+ write(join(prefix, 'ruflo.cmd'), '@ECHO off\r\n');
80
+ write(join(prefix, 'node_modules', 'ruflo', 'package.json'), JSON.stringify({ name: 'ruflo', bin: { ruflo: 'bin/ruflo.js' } }));
81
+ write(join(prefix, 'node_modules', 'ruflo', 'bin', 'ruflo.js'), '');
82
+ assert.deepEqual(findRufloOnPath({ Path: prefix }, 'win32'),
83
+ { command: process.execPath, args: [realpathSync(join(prefix, 'node_modules', 'ruflo', 'bin', 'ruflo.js'))], shell: false, source: 'path' });
84
+ const bare = join(TMP, 'win-bare');
85
+ write(join(bare, 'ruflo.cmd'), '@ECHO off\r\n');
86
+ assert.equal(findRufloOnPath({ PATH: bare }, 'win32'), null, 'a ruflo.cmd that is not an npm package shim is skipped, not run through cmd.exe');
87
+ });
88
+
89
+ test('plugin-cache install: the installed `ruflo` on PATH serves memory calls, not npx', posixOnly, () => {
90
+ const env = { PATH: pathOf(WITH_RUFLO, NPX_ONLY) };
91
+ assert.deepEqual(resolved(env), { command: join(WITH_RUFLO, 'ruflo'), args: [], shell: false, source: 'path' });
92
+ const c = auditList(env);
93
+ assert.ok(c.some((l) => l.startsWith('ruflo memory list --namespace metaharness-audit')), c.join(' | ') || 'no CLI called');
94
+ assert.deepEqual(c.filter((l) => l.startsWith('npx ')), []);
95
+ });
96
+
97
+ test('no `ruflo` but `claude-flow` on PATH → claude-flow', posixOnly, () => {
98
+ const c = auditList({ PATH: pathOf(WITH_CLAUDE_FLOW, NPX_ONLY) });
99
+ assert.ok(c.some((l) => l.startsWith('claude-flow memory list')), c.join(' | '));
100
+ assert.deepEqual(c.filter((l) => l.startsWith('npx ')), []);
101
+ });
102
+
103
+ test('no installed CLI → still `npx @claude-flow/cli@latest` (unchanged fallback)', posixOnly, () => {
104
+ const env = { PATH: pathOf(NPX_ONLY) };
105
+ assert.equal(resolved(env).source, 'npx');
106
+ const c = auditList(env);
107
+ assert.ok(c.some((l) => l.startsWith('npx @claude-flow/cli@latest memory list')), c.join(' | '));
108
+ });
109
+
110
+ test('CLI_CORE=1 still opts into `npx @claude-flow/cli-core@alpha` (ADR-100)', posixOnly, () => {
111
+ const c = auditList({ PATH: pathOf(WITH_RUFLO, NPX_ONLY), CLI_CORE: '1' });
112
+ assert.ok(c.some((l) => l.startsWith('npx @claude-flow/cli-core@alpha memory list')), c.join(' | '));
113
+ assert.deepEqual(c.filter((l) => l.startsWith('ruflo ')), []);
114
+ });
115
+
116
+ test('RUFLO_PLUGIN_SKIP_PATH_CLI=1 skips the PATH lookup (test seam)', posixOnly, () => {
117
+ assert.equal(resolved({ PATH: pathOf(WITH_RUFLO, NPX_ONLY), RUFLO_PLUGIN_SKIP_PATH_CLI: '1' }).source, 'npx');
118
+ });
119
+
120
+ test('a `ruflo` in the working directory is never run via a relative PATH entry', posixOnly, () => {
121
+ write(join(PROJECT, 'ruflo'), '#!/bin/sh\nexit 0\n', 0o755);
122
+ try {
123
+ assert.equal(resolved({ PATH: ['', '.', NPX_ONLY].join(delimiter) }).source, 'npx');
124
+ } finally {
125
+ rmSync(join(PROJECT, 'ruflo'));
126
+ }
127
+ });
128
+
129
+ test('published layout: the @claude-flow/cli that ships the plugin still wins over PATH (#3366)', posixOnly, () => {
130
+ const cli = join(TMP, 'published', 'node_modules', '@claude-flow', 'cli');
131
+ const scripts = join(cli, 'plugins', 'ruflo-metaharness', 'scripts');
132
+ cpSync(join(PLUGIN_ROOT, 'scripts'), scripts, { recursive: true });
133
+ write(join(cli, 'package.json'), JSON.stringify({ name: '@claude-flow/cli', version: '0.0.0-test', type: 'module' }));
134
+ write(join(cli, 'dist', 'src', 'index.js'), 'export {};\n');
135
+ write(join(cli, 'bin', 'cli.js'), `import { appendFileSync } from 'node:fs';\nappendFileSync(${JSON.stringify(LOG)}, 'shipping-cli ' + process.argv.slice(2).join(' ') + '\\n');\nconsole.log('[]');\n`);
136
+ const env = { PATH: pathOf(WITH_RUFLO, NPX_ONLY) };
137
+ assert.equal(resolved(env, scripts).source, 'local');
138
+ const c = auditList(env, scripts);
139
+ assert.ok(c.some((l) => l.startsWith('shipping-cli memory list')), c.join(' | '));
140
+ assert.deepEqual(c.filter((l) => !l.startsWith('shipping-cli ')), []);
141
+ });
@@ -156,6 +156,11 @@ else process.exit(2);
156
156
  const baseEnv = { ...process.env, PATH: `${TRAP}:${process.env.PATH}`, RUFLO_METAHARNESS_CACHE_BASE: join(TMP, 'empty-cache-base') };
157
157
  delete baseEnv.RUFLO_METAHARNESS_SKIP_LOCAL;
158
158
  delete baseEnv.CLI_CORE;
159
+ delete baseEnv.RUFLO_PLUGIN_SKIP_LOCAL_CLI;
160
+ // #3558: Phase 4 asserts the npx fallback; an installed `ruflo` on the
161
+ // developer's PATH would now be preferred over it. PATH resolution has its
162
+ // own test (test-cli-resolution-3558.mjs).
163
+ baseEnv.RUFLO_PLUGIN_SKIP_PATH_CLI = '1';
159
164
  const trapped = () => (existsSync(TRAP_LOG) ? readFileSync(TRAP_LOG, 'utf-8').trim() : '');
160
165
  const resetTrap = () => rmSync(TRAP_LOG, { force: true });
161
166
  const run = (script, args, env = baseEnv, scriptsDir = PLUGIN_SCRIPTS, cwd = PROJECT) => {