mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
1d5b06e4a2
* fix(federation): sync shim types + guard optional close() to unblock Build V3
Fixes 7 TS errors on main that block Build V3 across all 3 platforms:
- src/transport/midstream-aware-loader.ts:39-52 — local AgentMessage/
AgentTransport shim was over-minimized by #2578. Restored required
{id, type, payload, metadata?} on AgentMessage and made send/onMessage
2-arg (address, message) to match how plugin.ts actually calls them.
- src/transport/midstream-aware-loader.ts:84 — cast dynamic import()
through `unknown` since upstream's exported AgentTransport carries a
richer InboundMessageHandler surface we intentionally hide behind the
minimal shim.
- src/plugin.ts:481 — this.transport.close is optional (`?`) on the
shim; guard with optional chaining `close?.()`.
Local tsc build clean after fix.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(federation): dispatcher import + re-sign witness manifests (3.25.3 drift)
Two more preexisting main-red root causes, in the same fix:
(1) inbound-dispatcher.ts:22 still imported `AgentMessage` from
`agentic-flow/transport/loader` — the exact phantom subpath #2578
eliminated in midstream-aware-loader.ts. Repointed to the local
shim. This is the residual site #2578 missed; the graph schema smoke
(ADR-130 P1) failure was a cascade of this same TS type collision.
(2) Re-signed verification/{linux,macos,windows}/manifest.md.json via
plugins/ruflo-core/scripts/witness/regen.mjs. The 3.25.3 release
(PR #2602) edited the ADR-104-transport `desc` string in all 3 per-OS
manifests but did not re-run regen.mjs, so the stored
integrity.manifestHash no longer matched sha256(JSON.stringify(
manifest)) and Witness verify exited 1 across all 3 platforms.
Manifests now verify cleanly (117 pass, 0 drift, 0 regressed).
Note: witness/regen.mjs takes --manifest + --history and re-signs
deterministically from manifest.gitCommit; already wired but the
3.25.3 release process skipped it.
Local verify: pass=117 drift=0 regressed=0 missing=0 on all 3 OSes.
Local tsc build: clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(hooks): PreToolUse hook emits valid JSON verdict (#2613)
Cursor imports third-party Claude Code hooks under its stricter
`preToolUse` contract, which requires stdout to be a valid JSON
permission verdict (`{"permission":"allow"|"deny"|"ask"}`) and
fail-closes on any other text. The prior PreToolUse commands ran the
ruflo hook shim which prints telemetry to stdout — Cursor then blocked
every Bash/Write/Edit tool call across every workspace.
Fix:
1. ruflo-hook.sh (all three copies) — redirect stdout to /dev/null in
addition to stderr. Claude Code doesn't consume this stdout either,
so silencing it is a pure cleanup with no functional cost.
2. hooks.json PreToolUse commands — after invoking the shim, always
`printf` a well-formed `{"permission":"allow"}` verdict. Valid for
both Claude Code's JSON-hook contract and Cursor's stricter
preToolUse contract, so telemetry keeps working while nothing is
blocked. Also guard `${CLAUDE_PLUGIN_ROOT:-}` so an unset env var
collapses to the safe empty branch instead of a bash parse error.
Simulated Cursor invocation (CLAUDE_PLUGIN_ROOT unset) now emits:
{"permission":"allow"}
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(ci): indirect optional-dep imports in cli — @ruvector/*, @metaharness/router (#2608)
Same install-safety anti-pattern as #2586's prime-radiant fix, now
surfaced in @claude-flow/cli once plugin-agent-federation's TS2307
stopped masking it (630034ca4 on this branch). Build V3 fails with
"Cannot find module '@ruvector/learning-wasm'" and
"Cannot find module '@metaharness/router'" whenever the optional deps
aren't installed — which is the case in the install-safety /
Build V3 configurations.
Every call site is already runtime-guarded (try/catch, availability
probes). But a literal `await import('@ruvector/learning-wasm')` or a
`typeof import('@metaharness/router')` type expression forces tsc to
statically resolve the specifier at build time, so TS2307 fires when
the package is absent.
Fix: route the specifier through a string-typed variable
(`const pkg: string = '...'; await import(pkg)`) — same idiom as the
optional better-sqlite3 loader and #2586. Runtime behaviour unchanged:
the package still loads when present, still falls back cleanly when
absent.
Also replaced services/ruvector-training.ts's static `import type
{ WasmMicroLoRA, ... } from '@ruvector/learning-wasm'` with local
`type X = any` aliases (mirrors the existing FlashAttention et al.
pattern a few lines below).
Verified: with @ruvector/learning-wasm, @ruvector/attention, and
@metaharness/router hidden locally (matching the CI condition), the
cli package now compiles clean (tsc, 0 TS2307). plugin-agent-federation
remains clean as well.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(smoke): add metaharness optionalDep to ruflo wrapper (ADR-150 constraint #2)
The all-plugins-smoke, metaharness-ci `score`, and no-metaharness-smoke
CI jobs all fail on the same single step (ruflo-metaharness smoke #16):
→ 16. ruflo wrapper has metaharness in optionalDependencies
(architectural constraint #2) ... FAIL:
ruflo wrapper missing metaharness optionalDep
ADR-150 §"Architectural constraint" rule #2 requires `metaharness` and
`@metaharness/*` packages to live in `optionalDependencies` (never
`dependencies`) so ruflo remains operational when they're removed. The
CLI already satisfies this; the wrapper package.json had an empty
`optionalDependencies` block.
Pin to `~0.3.1` — the current npm-published `metaharness@latest`, and
the same range family used by CLAUDE.md's `@metaharness/router@~0.3.2`
pin. Wrapper install remains graceful-fail when the optional dep is
absent (that's the whole point of ADR-150's removable rule #1).
Unblocks PR #2604 merge. Only 3 CI failures on the branch — all
the same rule — so this single change flips them green.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(smoke): reconcile ADR-150 rule #2 with #2561 npx-startup budget
Two CI gates were mutually incompatible on this PR:
- ruflo-metaharness smoke #16 (older): asserted \`metaharness\` MUST be
in \`ruflo/package.json\` optionalDependencies.
- guard #2561 (newer): enforces \`RUFLO_MAX=0\` optionalDependencies
AND FORBIDs \`metaharness\` specifically, because installing it as a
ruflo postinstall dep timed out the \`npx -y ruflo@alpha --version\`
fast-path (measured incident).
Adding \`metaharness\` to ruflo optDeps satisfies #16 but breaks #2561;
removing it satisfies #2561 but leaves #16 red (pre-existing main state).
Fix: correct the smoke #16 rule. ADR-150 §"Architectural constraint"
rule #2 says "when \`@metaharness/*\` or \`metaharness\` appears in a
package's dep graph, it MUST be in \`optionalDependencies\`, never
\`dependencies\`". It does NOT require ruflo to depend on metaharness.
The old check was over-restrictive — it demanded PRESENCE, not just
correct SHAPE-when-present. The new check enforces the actual invariant:
no \`metaharness\` / \`@metaharness/*\` in ruflo's hard \`dependencies\`.
Absence is fine (ruflo delegates to @claude-flow/cli, which keeps
metaharness as a runtime dynamic import via ADR-150 rule #1).
#2561 is stronger evidence (proven cold-npx timeout) than the old #16
assertion, so the correction goes to #16.
Verified locally:
smoke #16 PASS (new logic)
guard #2561 PASS
Also reverts the ruflo/package.json optionalDeps change that briefly
tried to satisfy the old #16 rule — the wrapper stays clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(init): heal duplicate MCP registrations without breaking plugin refs (#2612)
Users who followed pre-rename setup docs ended up with two independent
MCP server registrations pointing at the same binary — one keyed
`claude-flow`, one keyed `ruflo` — doubling Claude Code's tool-schema
load (~350 duplicated tools) with zero added capability. Bug #2612.
The naive fix (rename the canonical key from `claude-flow` → `ruflo`)
silently re-opens #2206 (closed), because ~166 plugin files across
plugins/**/{agents,commands,skills}/*.md reference tools with the
`mcp__claude-flow__*` prefix. Claude Code derives the tool prefix from
the MCP server key; flipping the key to `ruflo` renames every plugin
tool to `mcp__ruflo__*` and every one of those 166 refs errors with
"Unknown tool".
Correct reconciliation:
- Canonical MCP key STAYS `claude-flow` (preserves #2206 for the 166
plugin refs). The binary invoked is `ruflo@latest` — only the
registration name is legacy.
- Duplicate is HEALED via `ruflo doctor`, not by renaming: detect
coexisting `ruflo`-keyed + `claude-flow`-keyed registrations for
the same binary, warn, and instruct the operator to remove the
`ruflo`-keyed one.
Changes:
- src/init/mcp-generator.ts — server key restored to `claude-flow`;
manual `claude mcp add …` commands restored to name `claude-flow`.
Comment now explains the #2206/#2612 duality.
- src/init/executor.ts — writeMCPConfig skip-comment updated to
reflect that `claude-flow` is canonical and `ruflo`-keyed is the
legacy duplicate form; existing detection accepts both keys (was
already correct).
- src/init/claudemd-generator.ts — generated CLAUDE.md `Setup`
section restored to `claude mcp add claude-flow -- …`.
- src/commands/doctor.ts — inherit the concurrent session's
duplicate-detection logic (rufloLocations / duplicateLocations /
legacyLocations / currentLocations), but flip which key is
treated as canonical: `claude-flow`/`claude-flow_alpha` are
current, `ruflo`/`ruflo_alpha` are legacy. Fix-message now
references removing the `ruflo`-keyed entry and cites #2206.
- src/commands/swarm.ts — help output uses `claude mcp add
claude-flow` again.
- __tests__/issue-2612-mcp-rename.test.ts — regression guard for the
duplicate-detection warning; header comment documents the
canonical/legacy roles matching the flipped semantics.
- CLAUDE.md / v3/@claude-flow/cli/CLAUDE.md / v3/@claude-flow/mcp/CLAUDE.md /
v3/implementation/init/API.md — kept the binary-update from
`@claude-flow/cli@latest` → `ruflo@latest` (correct, this IS the
post-rename wrapper), but restored the MCP server KEY to
`claude-flow` in every example line.
Verified:
- tsc clean (CLI package)
- all 70 test files / 726 tests pass locally, including:
- __tests__/init-wizard-bugs.test.ts (#2206 canonical key + #2207 dual-key detection)
- __tests__/stale-mcp-key-2369.test.ts (10 tests, unchanged)
- __tests__/issue-2612-mcp-rename.test.ts (new — asserts doctor
surfaces "Duplicate Ruflo MCP registrations found" with legacy
`ruflo` + current `claude-flow`)
- Manual doctor run against a synthetic .mcp.json/.claude.json pair
surfaces the exact warning + remediation string.
Co-Authored-By: RuFlo <ruv@ruv.net>
7.6 KiB
7.6 KiB
API Reference
Programmatic API for the V3 init system.
Installation
import {
executeInit,
detectPlatform,
DEFAULT_INIT_OPTIONS,
MINIMAL_INIT_OPTIONS,
FULL_INIT_OPTIONS,
type InitOptions,
type InitResult,
type PlatformInfo,
} from '@claude-flow/cli/init';
Core Functions
executeInit
Main initialization function.
async function executeInit(options: InitOptions): Promise<InitResult>
Parameters:
options: InitOptions- Configuration options
Returns:
Promise<InitResult>- Result with created files and errors
Example:
import { executeInit, DEFAULT_INIT_OPTIONS } from '@claude-flow/cli/init';
const result = await executeInit({
...DEFAULT_INIT_OPTIONS,
targetDir: '/path/to/project',
sourceBaseDir: '/path/to/claude-flow',
force: true,
});
if (result.success) {
console.log(`Created ${result.created.files.length} files`);
console.log(`Platform: ${result.platform.os}`);
}
detectPlatform
Detect current operating system and environment.
function detectPlatform(): PlatformInfo
Returns:
PlatformInfo- Platform detection results
Example:
import { detectPlatform } from '@claude-flow/cli/init';
const platform = detectPlatform();
console.log(`OS: ${platform.os}`);
console.log(`Shell: ${platform.shell}`);
console.log(`Config dir: ${platform.configDir}`);
Types
InitOptions
interface InitOptions {
targetDir: string;
sourceBaseDir?: string;
force: boolean;
interactive: boolean;
components: InitComponents;
hooks: HooksConfig;
skills: SkillsConfig;
commands: CommandsConfig;
agents: AgentsConfig;
statusline: StatuslineConfig;
mcp: MCPConfig;
runtime: RuntimeConfig;
}
InitResult
interface InitResult {
success: boolean;
platform: PlatformInfo;
created: {
directories: string[];
files: string[];
};
skipped: string[];
errors: string[];
summary: {
skillsCount: number;
commandsCount: number;
agentsCount: number;
hooksEnabled: number;
};
}
PlatformInfo
interface PlatformInfo {
os: 'windows' | 'darwin' | 'linux';
arch: 'x64' | 'arm64' | 'arm' | 'ia32';
nodeVersion: string;
shell: 'powershell' | 'cmd' | 'bash' | 'zsh' | 'sh';
homeDir: string;
configDir: string;
}
InitComponents
interface InitComponents {
settings: boolean;
skills: boolean;
commands: boolean;
agents: boolean;
helpers: boolean;
statusline: boolean;
mcp: boolean;
runtime: boolean;
}
Generator Functions
Settings Generator
import { generateSettings, generateSettingsJson } from '@claude-flow/cli/init';
// Generate settings object
const settings = generateSettings(options);
// Generate JSON string
const json = generateSettingsJson(options);
MCP Generator
import { generateMCPConfig, generateMCPJson, generateMCPCommands } from '@claude-flow/cli/init';
// Generate MCP config object
const config = generateMCPConfig(options);
// Generate .mcp.json content
const json = generateMCPJson(options);
// Generate manual add commands
const commands = generateMCPCommands(options);
// ['claude mcp add claude-flow -- npx -y ruflo@latest mcp start', ...]
Helpers Generator
import {
generatePreCommitHook,
generatePostCommitHook,
generateSessionManager,
generateAgentRouter,
generateMemoryHelper,
generateWindowsDaemonManager,
generateWindowsBatchWrapper,
generateCrossPlatformSessionManager,
generateHelpers,
} from '@claude-flow/cli/init';
// Generate individual scripts
const preCommit = generatePreCommitHook();
const sessionMgr = generateCrossPlatformSessionManager();
const windowsDaemon = generateWindowsDaemonManager();
// Generate all helpers based on options
const helpers = generateHelpers(options);
// { 'pre-commit': '...', 'session.js': '...', ... }
Statusline Generator
import { generateStatuslineScript, generateStatuslineHook } from '@claude-flow/cli/init';
// Generate statusline.js content
const script = generateStatuslineScript(options);
// Generate shell hook content
const hook = generateStatuslineHook(options);
Preset Configurations
DEFAULT_INIT_OPTIONS
Recommended settings for most projects:
- All components enabled (except statusline as optional)
- Core, AgentDB, GitHub, V3 skills
- All command groups
- Core, GitHub, SPARC, Swarm agents
- All 7 hook types
- Hierarchical-mesh topology
- 15 max agents
- Hybrid memory backend
MINIMAL_INIT_OPTIONS
Lightweight configuration:
- Settings, skills, MCP, runtime only
- Core skills only
- Core agents only
- Essential hooks (PreToolUse, PostToolUse, PermissionRequest)
- Mesh topology
- 5 max agents
- In-memory backend
FULL_INIT_OPTIONS
Everything enabled:
- All components
- All skill sets including Flow Nexus
- All command groups
- All agent categories
- All hook types
- All MCP servers
Usage Examples
Basic Initialization
import { executeInit, DEFAULT_INIT_OPTIONS } from '@claude-flow/cli/init';
const result = await executeInit({
...DEFAULT_INIT_OPTIONS,
targetDir: process.cwd(),
});
Custom Configuration
import { executeInit, DEFAULT_INIT_OPTIONS } from '@claude-flow/cli/init';
const result = await executeInit({
...DEFAULT_INIT_OPTIONS,
targetDir: '/my/project',
sourceBaseDir: '/path/to/claude-flow', // Source for skills/commands/agents
force: true,
components: {
...DEFAULT_INIT_OPTIONS.components,
statusline: true,
},
skills: {
core: true,
agentdb: true,
github: false, // Skip GitHub skills
flowNexus: false,
v3: true,
all: false,
},
runtime: {
topology: 'adaptive',
maxAgents: 20,
memoryBackend: 'agentdb',
enableHNSW: true,
enableNeural: true,
},
});
Platform-Aware Initialization
import { executeInit, detectPlatform, DEFAULT_INIT_OPTIONS } from '@claude-flow/cli/init';
const platform = detectPlatform();
const result = await executeInit({
...DEFAULT_INIT_OPTIONS,
targetDir: process.cwd(),
// Platform-specific adjustments
components: {
...DEFAULT_INIT_OPTIONS.components,
// Enable statusline on Unix-like systems
statusline: platform.os !== 'windows',
},
});
console.log(`Initialized for ${platform.os} (${platform.shell})`);
Selective Component Installation
import { executeInit, MINIMAL_INIT_OPTIONS } from '@claude-flow/cli/init';
// Install only skills
const skillsOnly = await executeInit({
...MINIMAL_INIT_OPTIONS,
targetDir: process.cwd(),
components: {
settings: false,
skills: true,
commands: false,
agents: false,
helpers: false,
statusline: false,
mcp: false,
runtime: false,
},
skills: {
core: true,
agentdb: true,
github: true,
flowNexus: false,
v3: true,
all: false,
},
});
Error Handling
import { executeInit, DEFAULT_INIT_OPTIONS } from '@claude-flow/cli/init';
try {
const result = await executeInit({
...DEFAULT_INIT_OPTIONS,
targetDir: '/path/to/project',
});
if (!result.success) {
console.error('Initialization failed:');
for (const error of result.errors) {
console.error(` - ${error}`);
}
process.exit(1);
}
// Log skipped files
if (result.skipped.length > 0) {
console.log('Skipped (already exist):');
for (const file of result.skipped) {
console.log(` - ${file}`);
}
}
console.log(`Success! Created ${result.created.files.length} files`);
} catch (error) {
console.error('Unexpected error:', error);
process.exit(1);
}