claude-flow 3.50.0 → 3.51.1
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 +4 -3
- package/.claude/proven-config.json +1 -1
- package/.claude/settings.json +11 -2
- package/.claude-plugin/marketplace.json +5 -0
- package/README.md +3 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/README.md +0 -2
- package/v3/@claude-flow/cli/catalog-manifest.json +4 -4
- package/v3/@claude-flow/cli/dist/src/commands/catalog.d.ts +18 -0
- package/v3/@claude-flow/cli/dist/src/commands/catalog.js +89 -0
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +8 -3
- package/v3/@claude-flow/cli/dist/src/commands/federation.js +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/index.js +3 -0
- package/v3/@claude-flow/cli/dist/src/commands/init.js +50 -11
- package/v3/@claude-flow/cli/dist/src/commands/mission.d.ts +24 -0
- package/v3/@claude-flow/cli/dist/src/commands/mission.js +116 -0
- package/v3/@claude-flow/cli/dist/src/commands/mods.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/mods.js +67 -23
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +4 -2
- package/v3/@claude-flow/cli/dist/src/mcp-client.js +3 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/capability-brain.js +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/index.d.ts +1 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/index.js +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/mission-tools.d.ts +22 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/mission-tools.js +75 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/seraphina-tools.js +15 -12
- package/v3/@claude-flow/cli/dist/src/mcp-tools/service-url.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/service-url.js +34 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-tools.js +5 -4
- package/v3/@claude-flow/cli/dist/src/missions/fold.d.ts +19 -0
- package/v3/@claude-flow/cli/dist/src/missions/fold.js +238 -0
- package/v3/@claude-flow/cli/dist/src/missions/index.d.ts +10 -0
- package/v3/@claude-flow/cli/dist/src/missions/index.js +10 -0
- package/v3/@claude-flow/cli/dist/src/missions/observation.d.ts +70 -0
- package/v3/@claude-flow/cli/dist/src/missions/observation.js +77 -0
- package/v3/@claude-flow/cli/dist/src/missions/schemas.d.ts +735 -0
- package/v3/@claude-flow/cli/dist/src/missions/schemas.js +150 -0
- package/v3/@claude-flow/cli/dist/src/missions/service.d.ts +88 -0
- package/v3/@claude-flow/cli/dist/src/missions/service.js +278 -0
- package/v3/@claude-flow/cli/dist/src/missions/store.d.ts +68 -0
- package/v3/@claude-flow/cli/dist/src/missions/store.js +209 -0
- package/v3/@claude-flow/cli/dist/src/missions/transitions.d.ts +81 -0
- package/v3/@claude-flow/cli/dist/src/missions/transitions.js +178 -0
- package/v3/@claude-flow/cli/dist/src/mods/apply.d.ts +50 -0
- package/v3/@claude-flow/cli/dist/src/mods/apply.js +186 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/catalog.d.ts +45 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/catalog.js +234 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/engine-report.d.ts +66 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/engine-report.js +119 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/generate.d.ts +51 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/generate.js +166 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/index.d.ts +9 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/index.js +9 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/inventory.d.ts +63 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/inventory.js +230 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/schema.d.ts +836 -0
- package/v3/@claude-flow/cli/dist/src/mods/command-registry/schema.js +137 -0
- package/v3/@claude-flow/cli/dist/src/mods/install.d.ts +60 -22
- package/v3/@claude-flow/cli/dist/src/mods/install.js +91 -41
- package/v3/@claude-flow/cli/dist/src/mods/plugin-repair.d.ts +81 -0
- package/v3/@claude-flow/cli/dist/src/mods/plugin-repair.js +120 -0
- package/v3/@claude-flow/cli/dist/src/mods/plugin-resolve.d.ts +63 -0
- package/v3/@claude-flow/cli/dist/src/mods/plugin-resolve.js +182 -0
- package/v3/@claude-flow/cli/dist/src/mods/probe.d.ts +5 -0
- package/v3/@claude-flow/cli/dist/src/mods/probe.js +28 -3
- package/v3/@claude-flow/cli/dist/src/services/policy-runtime.d.ts +5 -0
- package/v3/@claude-flow/cli/dist/src/services/policy-runtime.js +5 -1
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -465,7 +465,8 @@ const handlers = {
|
|
|
465
465
|
for (const d of dangerous) {
|
|
466
466
|
if (cmd.includes(d)) {
|
|
467
467
|
console.error(`[BLOCKED] Dangerous command detected: ${d}`);
|
|
468
|
-
|
|
468
|
+
// Claude Code PreToolUse: exit 2 blocks execution; exit 1 is non-blocking.
|
|
469
|
+
process.exit(2);
|
|
469
470
|
}
|
|
470
471
|
}
|
|
471
472
|
console.log('[OK] Command validated');
|
|
@@ -598,8 +599,8 @@ const handlers = {
|
|
|
598
599
|
}
|
|
599
600
|
}
|
|
600
601
|
|
|
601
|
-
//
|
|
602
|
-
//
|
|
602
|
+
// Non-blocking hooks exit 0; pre-bash denials exit 2 to block PreToolUse.
|
|
603
|
+
// A denial exits immediately, before the normal completion path below.
|
|
603
604
|
//
|
|
604
605
|
// Only dispatch when run directly (node hook-handler.cjs <cmd>). When
|
|
605
606
|
// require()'d by a test, expose the internals instead of reading stdin and
|
package/.claude/settings.json
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
"env": {
|
|
6
6
|
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
|
|
7
7
|
"CLAUDE_FLOW_V3_ENABLED": "true",
|
|
8
|
-
"CLAUDE_FLOW_HOOKS_ENABLED": "true"
|
|
8
|
+
"CLAUDE_FLOW_HOOKS_ENABLED": "true",
|
|
9
|
+
"CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1"
|
|
9
10
|
},
|
|
10
11
|
"permissions": {
|
|
11
12
|
"allow": [
|
|
@@ -280,5 +281,13 @@
|
|
|
280
281
|
},
|
|
281
282
|
"enabledMcpjsonServers": [
|
|
282
283
|
"claude-flow"
|
|
283
|
-
]
|
|
284
|
+
],
|
|
285
|
+
"enabledPlugins": {
|
|
286
|
+
"ruflo-mods@ruflo": true,
|
|
287
|
+
"ruflo-swarm@ruflo": true,
|
|
288
|
+
"ruflo-console@ruflo": true
|
|
289
|
+
},
|
|
290
|
+
"extraKnownMarketplaces": {
|
|
291
|
+
"ruflo": { "source": { "source": "github", "repo": "ruvnet/ruflo" } }
|
|
292
|
+
}
|
|
284
293
|
}
|
|
@@ -225,6 +225,11 @@
|
|
|
225
225
|
"name": "ruflo-ruos",
|
|
226
226
|
"source": "./plugins/ruflo-ruos",
|
|
227
227
|
"description": "Run ruflo swarm agents on your own ruOS cloud desktops via the tenant-authenticated fleet MCP or per-tenant SSH; optional, removable"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"name": "ruflo-console",
|
|
231
|
+
"source": "./plugins/ruflo-console",
|
|
232
|
+
"description": "Early access: ruflo's cockpit as a Claude Code mod (function hooks) and the one /ruflo command for every ruflo mod \u2014 overview with health alerts, swarm topology, claims flow, federation map, plugin health, learning pipeline, MetaHarness radar, memory, cost gauge, agent timeline, approvals and events; a command palette for confirm-gated actions through the ruflo CLI; auto-opens where it docks; no network by default"
|
|
228
233
|
}
|
|
229
234
|
]
|
|
230
235
|
}
|
package/README.md
CHANGED
|
@@ -50,7 +50,9 @@ User --> Ruflo (CLI/MCP) --> Router --> Swarm --> Agents --> Memory --> LLM Prov
|
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
53
|
-

|
|
54
|
+
|
|
55
|
+
<sub>The <code>/ruflo</code> console running in Claude Code — <code>npx ruflo init</code>, restart Claude Code, then <code>/ruflo</code>.</sub>
|
|
54
56
|
|
|
55
57
|
## Quick Start
|
|
56
58
|
|
package/package.json
CHANGED
|
@@ -16,8 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
# Ruflo
|
|
18
18
|
|
|
19
|
-
[English](README.md) · [简体中文](README.zh-CN.md)
|
|
20
|
-
|
|
21
19
|
**An agent meta-harness for Claude Code and Codex.**
|
|
22
20
|
|
|
23
21
|
[](docs/ruflo-explained.md)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generation":
|
|
4
|
-
"generatedAt": "2026-
|
|
5
|
-
"gitSha": "
|
|
3
|
+
"generation": 6,
|
|
4
|
+
"generatedAt": "2026-09-17T21:28:47.530Z",
|
|
5
|
+
"gitSha": "702d1461",
|
|
6
6
|
"catalog": {
|
|
7
|
-
"agents":
|
|
7
|
+
"agents": 167,
|
|
8
8
|
"tools": 418,
|
|
9
9
|
"skills": 34
|
|
10
10
|
},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ruflo catalog` — ADR-406 P0/P1 command inventory and canonical catalog.
|
|
3
|
+
*
|
|
4
|
+
* Interim home: ADR-406 §14 places these under `ruflo mods catalog`, but
|
|
5
|
+
* `commands/mods.ts` belongs to the install-fix lane (PR #3612) until it
|
|
6
|
+
* merges. This file moves there as a follow-up; the operations stay the same.
|
|
7
|
+
*
|
|
8
|
+
* catalog generate [--check] inventory + engine probe + deterministic catalog
|
|
9
|
+
* catalog verify [--file] schema, duplicate ids/names, digest parity
|
|
10
|
+
* catalog engine engine capability report only
|
|
11
|
+
*
|
|
12
|
+
* Reads command files; writes only the catalog and engine report under
|
|
13
|
+
* src/mods/command-registry/. Never touches a command file.
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from '../types.js';
|
|
16
|
+
export declare const catalogCommand: Command;
|
|
17
|
+
export default catalogCommand;
|
|
18
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ruflo catalog` — ADR-406 P0/P1 command inventory and canonical catalog.
|
|
3
|
+
*
|
|
4
|
+
* Interim home: ADR-406 §14 places these under `ruflo mods catalog`, but
|
|
5
|
+
* `commands/mods.ts` belongs to the install-fix lane (PR #3612) until it
|
|
6
|
+
* merges. This file moves there as a follow-up; the operations stay the same.
|
|
7
|
+
*
|
|
8
|
+
* catalog generate [--check] inventory + engine probe + deterministic catalog
|
|
9
|
+
* catalog verify [--file] schema, duplicate ids/names, digest parity
|
|
10
|
+
* catalog engine engine capability report only
|
|
11
|
+
*
|
|
12
|
+
* Reads command files; writes only the catalog and engine report under
|
|
13
|
+
* src/mods/command-registry/. Never touches a command file.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { homedir } from 'node:os';
|
|
17
|
+
import { join, resolve } from 'node:path';
|
|
18
|
+
import { output } from '../output.js';
|
|
19
|
+
import { CATALOG_RELATIVE_PATH, ENGINE_REPORT_RELATIVE_PATH, generate, serializeCatalog, summarize, verifyCatalog, writeCatalog, } from '../mods/command-registry/index.js';
|
|
20
|
+
import { canonicalJson, sha256Hex } from '../mods/command-registry/index.js';
|
|
21
|
+
import { writeFileSync } from 'node:fs';
|
|
22
|
+
function engineFrom(flags) {
|
|
23
|
+
const explicit = typeof flags.engine === 'string' ? flags.engine : undefined;
|
|
24
|
+
const candidate = explicit ?? join(homedir(), '.local', 'bin', 'claude');
|
|
25
|
+
return existsSync(candidate) ? candidate : null;
|
|
26
|
+
}
|
|
27
|
+
function finish(data, ok, json, text) {
|
|
28
|
+
if (json)
|
|
29
|
+
output.writeln(JSON.stringify(data, null, 2));
|
|
30
|
+
else
|
|
31
|
+
output.writeln(text);
|
|
32
|
+
return { success: ok, exitCode: ok ? 0 : 1, data };
|
|
33
|
+
}
|
|
34
|
+
export const catalogCommand = {
|
|
35
|
+
name: 'catalog',
|
|
36
|
+
description: 'ADR-406 command inventory and canonical command catalog (generate, verify, engine report)',
|
|
37
|
+
options: [
|
|
38
|
+
{ name: 'repo-root', type: 'string', description: 'Repository root (default: cwd)' },
|
|
39
|
+
{ name: 'engine', type: 'string', description: 'Claude Code executable to probe (default ~/.local/bin/claude)' },
|
|
40
|
+
{ name: 'check', type: 'boolean', description: 'generate: compare with the committed catalog instead of writing' },
|
|
41
|
+
{ name: 'file', type: 'string', description: 'verify: catalog file to verify' },
|
|
42
|
+
{ name: 'json', type: 'boolean', description: 'Machine-readable output' },
|
|
43
|
+
],
|
|
44
|
+
examples: [
|
|
45
|
+
{ command: 'ruflo catalog generate', description: 'Regenerate the catalog and engine report' },
|
|
46
|
+
{ command: 'ruflo catalog generate --check', description: 'Fail if the committed catalog is stale' },
|
|
47
|
+
{ command: 'ruflo catalog verify --file catalog.generated.json', description: 'Verify a copied catalog (parity)' },
|
|
48
|
+
],
|
|
49
|
+
async action(ctx) {
|
|
50
|
+
const flags = ctx.flags;
|
|
51
|
+
const op = ctx.args[0] ?? 'generate';
|
|
52
|
+
const json = flags.json === true;
|
|
53
|
+
const root = resolve(String(flags.repoRoot ?? ctx.cwd ?? process.cwd()));
|
|
54
|
+
try {
|
|
55
|
+
if (op === 'verify') {
|
|
56
|
+
const file = resolve(String(flags.file ?? join(root, CATALOG_RELATIVE_PATH)));
|
|
57
|
+
const result = verifyCatalog(JSON.parse(readFileSync(file, 'utf8')));
|
|
58
|
+
return finish({ file, ...result }, result.valid, json, result.valid ? `catalog valid: ${file}` : `catalog INVALID:\n${result.errors.join('\n')}`);
|
|
59
|
+
}
|
|
60
|
+
if (op !== 'generate' && op !== 'engine') {
|
|
61
|
+
return finish({ error: `unknown operation ${op}` }, false, json, `unknown operation: ${op} (generate | verify | engine)`);
|
|
62
|
+
}
|
|
63
|
+
const result = generate({ repoRoot: root, engineExecutable: engineFrom(flags) });
|
|
64
|
+
if (op === 'engine')
|
|
65
|
+
return finish(result.engine, true, json, JSON.stringify(result.engine, null, 2));
|
|
66
|
+
const catalogPath = join(root, CATALOG_RELATIVE_PATH);
|
|
67
|
+
const summary = summarize(result);
|
|
68
|
+
if (flags.check === true) {
|
|
69
|
+
const committed = existsSync(catalogPath) ? readFileSync(catalogPath, 'utf8') : '';
|
|
70
|
+
const fresh = serializeCatalog(result.catalog);
|
|
71
|
+
const same = committed === fresh;
|
|
72
|
+
return finish({ catalogPath, upToDate: same, summary }, same, json, same ? 'catalog up to date' : `catalog is stale: run \`ruflo catalog generate\` (${catalogPath})`);
|
|
73
|
+
}
|
|
74
|
+
const previous = existsSync(catalogPath) ? sha256Hex(readFileSync(catalogPath)) : null;
|
|
75
|
+
const write = writeCatalog(catalogPath, result.catalog, previous);
|
|
76
|
+
// The engine report is host evidence (engine build, binary digest), kept
|
|
77
|
+
// beside the catalog but outside its digest, which must stay host-free.
|
|
78
|
+
writeFileSync(join(root, ENGINE_REPORT_RELATIVE_PATH), `${JSON.stringify(JSON.parse(canonicalJson(result.engine)), null, 2)}\n`);
|
|
79
|
+
return finish({ catalogPath, written: write.written, sha256: write.sha256, summary }, true, json, `${write.written ? 'wrote' : 'unchanged'} ${catalogPath}\n${JSON.stringify(summary, null, 2)}`);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
output.printError(message);
|
|
84
|
+
return { success: false, exitCode: 1, message };
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
export default catalogCommand;
|
|
89
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -2308,15 +2308,20 @@ async function checkMetaharness() {
|
|
|
2308
2308
|
}
|
|
2309
2309
|
// ADR-404 — ruflo as a Claude Code mod (function hooks, early access). One
|
|
2310
2310
|
// line in a bare `doctor`: whether the mod is enabled, can load (function
|
|
2311
|
-
// hooks on, not refused by allowManagedModsOnly) and has started
|
|
2312
|
-
//
|
|
2313
|
-
//
|
|
2311
|
+
// hooks on, not refused by allowManagedModsOnly) and has started. Only an
|
|
2312
|
+
// opted-in project whose plugin Claude Code cannot resolve (stale ruflo
|
|
2313
|
+
// marketplace clone, nothing installed) fails: the classic hooks still run,
|
|
2314
|
+
// but the opt-in silently does nothing. `ruflo mods doctor` prints every finding.
|
|
2314
2315
|
async function checkMods() {
|
|
2315
2316
|
const { probeMods } = await import('../mods/probe.js');
|
|
2316
2317
|
const findings = probeMods({ projectRoot: process.cwd() });
|
|
2317
2318
|
const enabled = findings.find((f) => f.name === 'ruflo-mods plugin')?.status === 'pass';
|
|
2318
2319
|
if (!enabled)
|
|
2319
2320
|
return { name: 'ruflo mods (ADR-404)', status: 'pass', message: 'not enabled; classic hooks handle every event' };
|
|
2321
|
+
const failures = findings.filter((f) => f.status === 'fail');
|
|
2322
|
+
if (failures.length > 0) {
|
|
2323
|
+
return { name: 'ruflo mods (ADR-404)', status: 'fail', message: failures.map((f) => `${f.name}: ${f.message}`).join('; '), fix: failures[0].fix ?? 'ruflo mods doctor' };
|
|
2324
|
+
}
|
|
2320
2325
|
const warnings = findings.filter((f) => f.status !== 'pass');
|
|
2321
2326
|
return warnings.length === 0
|
|
2322
2327
|
? { name: 'ruflo mods (ADR-404)', status: 'pass', message: findings.find((f) => f.name === 'last mod start')?.message ?? 'enabled' }
|
|
@@ -10,7 +10,7 @@ function printJsonOrTable(ctx, data, title) {
|
|
|
10
10
|
}
|
|
11
11
|
async function run(ctx, tool, args, title) {
|
|
12
12
|
try {
|
|
13
|
-
//
|
|
13
|
+
// Credential-bearing and gateway-read tools require --gateway to match trusted server configuration.
|
|
14
14
|
const data = await callMCPTool(tool, { gatewayUrl: ctx.flags.gateway, ...args });
|
|
15
15
|
printJsonOrTable(ctx, data, title);
|
|
16
16
|
if (data && typeof data === 'object' && (('ok' in data && data.ok === false) || ('degraded' in data && data.degraded === true)))
|
|
@@ -27,7 +27,7 @@ export const federationCommand = {
|
|
|
27
27
|
description: 'Open swarm federation via x.ruv.io — sync messages, roster, claims, registry, invites (Nostr, signed, membership-gated)',
|
|
28
28
|
options: [
|
|
29
29
|
{ name: 'format', short: 'f', description: 'Output format (json|text)', type: 'string', default: 'text' },
|
|
30
|
-
{ name: 'gateway', description: 'Gateway
|
|
30
|
+
{ name: 'gateway', description: 'Gateway URL; reads/admin writes require it to match RUFLO_X_GATEWAY_URL (default https://x.ruv.io); join selects its registration origin', type: 'string' },
|
|
31
31
|
],
|
|
32
32
|
subcommands: [
|
|
33
33
|
{ name: 'join', description: 'Join the open swarm with YOUR OWN key without an invite on open relays (generates ~/.ruflo/nostr.key if absent, registers via NIP-98, verifies via NIP-42)',
|
|
@@ -90,6 +90,9 @@ const commandLoaders = {
|
|
|
90
90
|
spinner: () => import('./spinner.js'),
|
|
91
91
|
// ruflo as a Claude Code mod: function hooks, early access (ADR-404)
|
|
92
92
|
mods: () => import('./mods.js'),
|
|
93
|
+
// ADR-406 command catalog and mission control (interim homes until mods.ts follow-up)
|
|
94
|
+
catalog: () => import('./catalog.js'),
|
|
95
|
+
mission: () => import('./mission.js'),
|
|
93
96
|
// Ruflo entries in Claude Code's companyAnnouncements startup rotation (ADR-319)
|
|
94
97
|
announcements: () => import('./announcements.js'),
|
|
95
98
|
// AGNTCY/Outshift runtime transport selection (ADR-324 §2) — optional,
|
|
@@ -775,17 +775,18 @@ const initClaudeAction = async (ctx) => {
|
|
|
775
775
|
output.writeln(output.warning(' Embedding initialization skipped (run manually)'));
|
|
776
776
|
}
|
|
777
777
|
}
|
|
778
|
-
// ADR-404
|
|
779
|
-
//
|
|
780
|
-
|
|
778
|
+
// ADR-404 (amended: mods default-on in init) — enable the ruflo mod
|
|
779
|
+
// plugins in the committed .claude/settings.json, then install them with
|
|
780
|
+
// claude. Additive: the classic hooks just written stay the fallback.
|
|
781
|
+
// --no-mods opts out; --no-plugin-install writes settings only.
|
|
782
|
+
if (ctx.flags.mods !== false) {
|
|
781
783
|
output.writeln();
|
|
782
784
|
try {
|
|
783
|
-
const {
|
|
784
|
-
|
|
785
|
-
output.writeln(output.success(` ✓ ruflo-mods enabled in ${installed.settingsFile} (early access; run "ruflo mods doctor")`));
|
|
785
|
+
const { applyMods } = await import('../mods/apply.js');
|
|
786
|
+
await applyMods(ctx.cwd, { scope: 'project', pluginInstall: ctx.flags.pluginInstall !== false && ctx.flags['plugin-install'] !== false, skipInTestEnv: true });
|
|
786
787
|
}
|
|
787
788
|
catch (err) {
|
|
788
|
-
output.writeln(output.warning(` ruflo
|
|
789
|
+
output.writeln(output.warning(` ruflo mods not enabled: ${err instanceof Error ? err.message : String(err)}`));
|
|
789
790
|
}
|
|
790
791
|
}
|
|
791
792
|
if (!startDaemon && !startAll) {
|
|
@@ -1062,6 +1063,17 @@ const wizardCommand = {
|
|
|
1062
1063
|
output.printInfo(` ... and ${result.warnings.length - 5} more`);
|
|
1063
1064
|
}
|
|
1064
1065
|
}
|
|
1066
|
+
// ADR-404 amendment — the wizard enables the mods as `ruflo init` does.
|
|
1067
|
+
if (ctx.flags.mods !== false) {
|
|
1068
|
+
output.writeln();
|
|
1069
|
+
try {
|
|
1070
|
+
const { applyMods } = await import('../mods/apply.js');
|
|
1071
|
+
await applyMods(ctx.cwd, { scope: 'project', pluginInstall: ctx.flags.pluginInstall !== false && ctx.flags['plugin-install'] !== false, skipInTestEnv: true });
|
|
1072
|
+
}
|
|
1073
|
+
catch (err) {
|
|
1074
|
+
output.writeln(output.warning(` ruflo mods not enabled: ${err instanceof Error ? err.message : String(err)}`));
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1065
1077
|
// Initialize embeddings if enabled
|
|
1066
1078
|
let embeddingsInitialized = false;
|
|
1067
1079
|
if (enableEmbeddings) {
|
|
@@ -1322,14 +1334,27 @@ const upgradeCommand = {
|
|
|
1322
1334
|
{
|
|
1323
1335
|
name: 'settings',
|
|
1324
1336
|
short: 's',
|
|
1325
|
-
description: 'Merge new settings (Agent Teams, hooks) into existing settings.json',
|
|
1337
|
+
description: 'Merge new settings (Agent Teams, hooks, ruflo mods) into existing settings.json',
|
|
1326
1338
|
type: 'boolean',
|
|
1327
1339
|
default: false,
|
|
1328
1340
|
},
|
|
1341
|
+
{
|
|
1342
|
+
// ADR-404 amendment — the same merge + plugin install as `ruflo init`.
|
|
1343
|
+
name: 'mods',
|
|
1344
|
+
description: 'Merge the ruflo mod plugins into .claude/settings.json and install them (also implied by --add-missing and --settings; --no-mods to skip)',
|
|
1345
|
+
type: 'boolean',
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
name: 'plugin-install',
|
|
1349
|
+
description: 'With mods: run claude plugin install (--no-plugin-install to merge settings only)',
|
|
1350
|
+
type: 'boolean',
|
|
1351
|
+
default: true,
|
|
1352
|
+
},
|
|
1329
1353
|
],
|
|
1330
1354
|
action: async (ctx) => {
|
|
1331
1355
|
const addMissing = (ctx.flags['add-missing'] || ctx.flags.addMissing);
|
|
1332
1356
|
const upgradeSettings = (ctx.flags.settings);
|
|
1357
|
+
const upgradeMods = ctx.flags.mods === true || (ctx.flags.mods !== false && (addMissing || upgradeSettings));
|
|
1333
1358
|
output.writeln();
|
|
1334
1359
|
output.writeln(output.bold('Upgrading RuFlo'));
|
|
1335
1360
|
if (addMissing && upgradeSettings) {
|
|
@@ -1363,6 +1388,14 @@ const upgradeCommand = {
|
|
|
1363
1388
|
}
|
|
1364
1389
|
spinner.succeed('Upgrade complete!');
|
|
1365
1390
|
output.writeln();
|
|
1391
|
+
// ADR-404 amendment — merge the mod plugins without clobbering: only
|
|
1392
|
+
// keys that are absent are added, and each is reported.
|
|
1393
|
+
if (upgradeMods) {
|
|
1394
|
+
const { applyMods, describeAdded } = await import('../mods/apply.js');
|
|
1395
|
+
const mods = await applyMods(ctx.cwd, { scope: 'project', pluginInstall: ctx.flags.pluginInstall !== false && ctx.flags['plugin-install'] !== false, skipInTestEnv: true });
|
|
1396
|
+
result.settingsUpdated = [...(result.settingsUpdated ?? []), ...describeAdded(mods.install.added)];
|
|
1397
|
+
output.writeln();
|
|
1398
|
+
}
|
|
1366
1399
|
// Show what was updated
|
|
1367
1400
|
if (result.updated.length > 0) {
|
|
1368
1401
|
output.printBox(result.updated.map(f => `✓ ${f}`).join('\n'), 'Updated (latest version)');
|
|
@@ -1514,11 +1547,17 @@ export const initCommand = {
|
|
|
1514
1547
|
default: false,
|
|
1515
1548
|
},
|
|
1516
1549
|
{
|
|
1517
|
-
// ADR-404 —
|
|
1550
|
+
// ADR-404 amendment — on by default; nothing loads where function hooks are off.
|
|
1518
1551
|
name: 'mods',
|
|
1519
|
-
description: '
|
|
1552
|
+
description: 'Enable the ruflo mod plugins in .claude/settings.json (default on; --no-mods to skip); classic hooks stay as fallback',
|
|
1520
1553
|
type: 'boolean',
|
|
1521
|
-
default:
|
|
1554
|
+
default: true,
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
name: 'plugin-install',
|
|
1558
|
+
description: 'With mods: refresh the ruflo marketplace and claude plugin install the mods (--no-plugin-install to write settings only)',
|
|
1559
|
+
type: 'boolean',
|
|
1560
|
+
default: true,
|
|
1522
1561
|
},
|
|
1523
1562
|
{
|
|
1524
1563
|
name: 'with-embeddings',
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ruflo mission` — ADR-406 §19.7 mission operations from the CLI.
|
|
3
|
+
*
|
|
4
|
+
* mission create --objective <text> [--request-id <id>]
|
|
5
|
+
* mission plan --mission <id> --expected-revision <n> --plan-file <json> [--request-id <id>]
|
|
6
|
+
* mission get [--mission <id>]
|
|
7
|
+
* mission events --mission <id> [--after <seq>] [--limit <n>]
|
|
8
|
+
* mission action --mission <id> --expected-revision <n> --action <requestAuthorization|pause|cancel|admit|resume> [--reason <text>]
|
|
9
|
+
*
|
|
10
|
+
* Flags map one-to-one onto the raw input the MCP tools receive; the shared
|
|
11
|
+
* `MissionService` validates both. Output is JSON. A missing request id gets a
|
|
12
|
+
* fresh one: a new id is a new intentional request (ADR-406 §9); pass the same
|
|
13
|
+
* id to retry the same request safely.
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from '../types.js';
|
|
16
|
+
import type { MissionPort } from '../mcp-tools/mission-tools.js';
|
|
17
|
+
export type MissionPortFactoryCli = (projectRoot: string) => MissionPort;
|
|
18
|
+
/** Test seam (London school): substitute the service the command talks to. */
|
|
19
|
+
export declare function setMissionPortFactory(next: MissionPortFactoryCli | null): void;
|
|
20
|
+
/** Raw operation input from flags; undefined fields are dropped so defaults apply. */
|
|
21
|
+
export declare function inputFromFlags(op: string, flags: Record<string, unknown>, cwd: string): Record<string, unknown>;
|
|
22
|
+
export declare const missionCommand: Command;
|
|
23
|
+
export default missionCommand;
|
|
24
|
+
//# sourceMappingURL=mission.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ruflo mission` — ADR-406 §19.7 mission operations from the CLI.
|
|
3
|
+
*
|
|
4
|
+
* mission create --objective <text> [--request-id <id>]
|
|
5
|
+
* mission plan --mission <id> --expected-revision <n> --plan-file <json> [--request-id <id>]
|
|
6
|
+
* mission get [--mission <id>]
|
|
7
|
+
* mission events --mission <id> [--after <seq>] [--limit <n>]
|
|
8
|
+
* mission action --mission <id> --expected-revision <n> --action <requestAuthorization|pause|cancel|admit|resume> [--reason <text>]
|
|
9
|
+
*
|
|
10
|
+
* Flags map one-to-one onto the raw input the MCP tools receive; the shared
|
|
11
|
+
* `MissionService` validates both. Output is JSON. A missing request id gets a
|
|
12
|
+
* fresh one: a new id is a new intentional request (ADR-406 §9); pass the same
|
|
13
|
+
* id to retry the same request safely.
|
|
14
|
+
*/
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
16
|
+
import { lstatSync, readFileSync } from 'node:fs';
|
|
17
|
+
import { resolve } from 'node:path';
|
|
18
|
+
import { output } from '../output.js';
|
|
19
|
+
import { MissionService } from '../missions/service.js';
|
|
20
|
+
const PLAN_FILE_MAX_BYTES = 256 * 1024;
|
|
21
|
+
let factory = (projectRoot) => new MissionService({ projectRoot, channel: 'cli' });
|
|
22
|
+
/** Test seam (London school): substitute the service the command talks to. */
|
|
23
|
+
export function setMissionPortFactory(next) {
|
|
24
|
+
factory = next ?? ((projectRoot) => new MissionService({ projectRoot, channel: 'cli' }));
|
|
25
|
+
}
|
|
26
|
+
function int(value) {
|
|
27
|
+
if (value === undefined || value === null || value === '')
|
|
28
|
+
return undefined;
|
|
29
|
+
const n = Number(value);
|
|
30
|
+
return Number.isFinite(n) ? n : Number.NaN;
|
|
31
|
+
}
|
|
32
|
+
function readPlanFile(path) {
|
|
33
|
+
const stat = lstatSync(path);
|
|
34
|
+
if (!stat.isFile())
|
|
35
|
+
throw new Error('plan-file must be a regular file');
|
|
36
|
+
if (stat.size > PLAN_FILE_MAX_BYTES)
|
|
37
|
+
throw new Error(`plan-file exceeds ${PLAN_FILE_MAX_BYTES} bytes`);
|
|
38
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
39
|
+
}
|
|
40
|
+
/** Raw operation input from flags; undefined fields are dropped so defaults apply. */
|
|
41
|
+
export function inputFromFlags(op, flags, cwd) {
|
|
42
|
+
const raw = {};
|
|
43
|
+
const set = (key, value) => { if (value !== undefined)
|
|
44
|
+
raw[key] = value; };
|
|
45
|
+
const requestId = typeof flags.requestId === 'string' ? flags.requestId : randomUUID();
|
|
46
|
+
if (op === 'create') {
|
|
47
|
+
set('requestId', requestId);
|
|
48
|
+
set('objective', flags.objective);
|
|
49
|
+
}
|
|
50
|
+
else if (op === 'plan') {
|
|
51
|
+
set('requestId', requestId);
|
|
52
|
+
set('missionId', flags.mission);
|
|
53
|
+
set('expectedRevision', int(flags.expectedRevision));
|
|
54
|
+
if (typeof flags.planFile === 'string')
|
|
55
|
+
set('plan', readPlanFile(resolve(cwd, flags.planFile)));
|
|
56
|
+
}
|
|
57
|
+
else if (op === 'get') {
|
|
58
|
+
set('missionId', flags.mission);
|
|
59
|
+
}
|
|
60
|
+
else if (op === 'events') {
|
|
61
|
+
set('missionId', flags.mission);
|
|
62
|
+
set('afterSequence', int(flags.after));
|
|
63
|
+
set('limit', int(flags.limit));
|
|
64
|
+
}
|
|
65
|
+
else if (op === 'action') {
|
|
66
|
+
set('requestId', requestId);
|
|
67
|
+
set('missionId', flags.mission);
|
|
68
|
+
set('expectedRevision', int(flags.expectedRevision));
|
|
69
|
+
set('action', flags.action);
|
|
70
|
+
set('reason', flags.reason);
|
|
71
|
+
}
|
|
72
|
+
return raw;
|
|
73
|
+
}
|
|
74
|
+
export const missionCommand = {
|
|
75
|
+
name: 'mission',
|
|
76
|
+
description: 'ADR-406 mission control: create, plan, get, events, action (shared runtime with the mission_* MCP tools)',
|
|
77
|
+
options: [
|
|
78
|
+
{ name: 'objective', type: 'string', description: 'create: mission objective' },
|
|
79
|
+
{ name: 'mission', type: 'string', description: 'Mission id (msn_…)' },
|
|
80
|
+
{ name: 'request-id', type: 'string', description: 'Idempotency key; reuse only to retry the same request' },
|
|
81
|
+
{ name: 'expected-revision', type: 'number', description: 'Revision the request was prepared against' },
|
|
82
|
+
{ name: 'plan-file', type: 'string', description: 'plan: JSON file with { tasks, acceptance, budget, scope }' },
|
|
83
|
+
{ name: 'after', type: 'number', description: 'events: cursor (last sequence seen)' },
|
|
84
|
+
{ name: 'limit', type: 'number', description: 'events: page size (max 500)' },
|
|
85
|
+
{ name: 'action', type: 'string', description: 'action: requestAuthorization | pause | cancel | admit | resume' },
|
|
86
|
+
{ name: 'reason', type: 'string', description: 'action: reason' },
|
|
87
|
+
{ name: 'project-root', type: 'string', description: 'Project root (default: cwd)' },
|
|
88
|
+
],
|
|
89
|
+
examples: [
|
|
90
|
+
{ command: 'ruflo mission create --objective "Ship the report" --request-id r1', description: 'Create a draft mission' },
|
|
91
|
+
{ command: 'ruflo mission plan --mission msn_… --expected-revision 1 --plan-file plan.json', description: 'Submit a plan' },
|
|
92
|
+
{ command: 'ruflo mission events --mission msn_… --after 0', description: 'Read events from a cursor' },
|
|
93
|
+
],
|
|
94
|
+
async action(ctx) {
|
|
95
|
+
const flags = ctx.flags;
|
|
96
|
+
const op = ctx.args[0] ?? 'get';
|
|
97
|
+
const root = resolve(String(flags.projectRoot ?? ctx.cwd ?? process.cwd()));
|
|
98
|
+
const ops = { create: 'create', plan: 'plan', get: 'get', events: 'events', action: 'requestAction' };
|
|
99
|
+
const method = ops[op];
|
|
100
|
+
if (!method) {
|
|
101
|
+
output.printError(`unknown mission operation: ${op} (create | plan | get | events | action)`);
|
|
102
|
+
return { success: false, exitCode: 1 };
|
|
103
|
+
}
|
|
104
|
+
let result;
|
|
105
|
+
try {
|
|
106
|
+
result = await factory(root)[method](inputFromFlags(op, flags, root));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
result = { ok: false, code: 'invalid-input', message: error instanceof Error ? error.message : String(error) };
|
|
110
|
+
}
|
|
111
|
+
output.writeln(JSON.stringify(result, null, 2));
|
|
112
|
+
return { success: result.ok, exitCode: result.ok ? 0 : result.code === 'revision-conflict' ? 3 : 1, data: result };
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
export default missionCommand;
|
|
116
|
+
//# sourceMappingURL=mission.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `ruflo mods` — opt into running ruflo as a Claude Code mod (ADR-404,
|
|
3
3
|
* Claude Code function hooks, early access).
|
|
4
4
|
*
|
|
5
|
-
* install/uninstall edit
|
|
5
|
+
* install/uninstall edit settings files and record what they added;
|
|
6
6
|
* status/doctor report what can be known from outside a session; sync-policy
|
|
7
7
|
* rewrites the policy projection the mod's tool check reads. Classic hooks
|
|
8
8
|
* are never removed: they stay the default and the fallback.
|