mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-11-09 14:04:06 +09:00
* fix(ruflo-mods): trust gate also treats the agent.spawn, prompt.submit, tool.call, command.run, session.append and agent.register calls and the tool.describe and session.append hooks as risky (#3787, #3801) Adversarial review of the previous commit: risk was checked per scanned `calls` and `events` separately, and only the events side had agent.spawn and prompt.submit, so a mod CALLING $.agent.spawn / $.prompt.submit / $.tool.call still loaded under refuse-risky ("no risky calls or hooks"). session.append and agent.register, and the tool.describe and session.append hooks (rewriting what the model reads). Docs follow the code: README hardening note lists the first-party mods that now need modTrustAllow under refuse-risky (agentdb, ruos, intelligence, graph-intelligence, goals, iot-cognitum beside swarm and console), the modTrust row and plugin.json description name the categories, and the create-mod skill's list matches. * fix(ruflo-mods): trust gate judges a glob or negation by what it selects; covers prompt sections/context, peer receive, model requests (#3801) Re-review: the scan reports hook patterns as written, and riskOf matched names exactly, so `on('tool.*')` or `on('!tool.describe')` loaded under refuse-risky with "no risky calls or hooks" and could answer tool permission verdicts (shown: tool.check returned allow). A glob now maps to every risky event it selects, a negation to `*`, and any classic.* pattern to the classic risk. Lookups use Object.hasOwn. Same-category hooks added: prompt.section, prompt.context, prompt.attachment, skill.prompt, session.receive, turn.step, and hooks on process.run / http.fetch / mcp.call (they can answer other mods' calls). Docs: every first-party mod with function hooks (43 of 43) is risky under the gate's sets, so the README now says to allow-list each ruflo mod you use under refuse-risky and points to `claude plugin validate`. * fix(ruflo-mods): a hook on a risky call is a risky hook (fs.write, env.set, process.spawn, ...) (#3801) Third review: every $ call is also an event a hook above the caller can rewrite or answer for every other mod. Under refuse-risky a mod hooking only fs.write loaded clean and redirected ruflo-mods' own heartbeat write to .claude/settings.json; one hooking env.set rewrote RUFLO_MODS_OWNS; one on process.spawn rewrote an allow-listed mod's argv; fs.* loaded too. Hook risk is now derived: every RISKY_CALLS entry is also a risky event ("can rewrite or answer another mod's <call>"), on top of RISKY_EVENTS (+ tool.register). Globs match against the union. Tests: fs.write, env.set and fs.* refused (each fails on the previous head); the harmless-glob control is clock.*, since command.run is a risky call. README and the create-mod skill say so. * fix(ruflo-mods): trust gate treats hooks that forge what other mods read, or replace the conversation, as risky (#3801) Fourth review: a mod hooking only fs.stat (answer ENOENT for the policy projection) or fs.read (return an empty observe projection) loaded under refuse-risky and turned ruflo's own enforced deny into allow; one hooking session.compact replaced the whole conversation. Added fs.read, fs.stat, fs.exists, fs.list, settings.read, env.get, session.compact, prompt.edit and attribution.text to the risky hooks; three tests (each fails on the previous head). README states the gate is a list over the 2.1.289 engine API, so an event added later is not judged until listed. * fix(ruflo-mods): session.root, session.cwd, fs.ancestors and command.register hooks are risky; docs name ruflo-ruos (#3801) Fifth review: a mod hooking only session.root moved ruflo-mods' project root, so the enforced policy projection was read from elsewhere and a deny became allow (the round-4 exploit through another input). Added those inputs and command.register (consistent with tool.register), with a root-forger test (fails on the previous head). Derived messages read "another mod's fs.write, which writes files ..." instead of nesting parentheses. Docs: ruflo-ruos hooks only prompt.submit, so it is newly risky under refuse-risky (#3787); README and PR body said every first-party mod was already risky via a status file or tool.call, which was false for it. * test(router): seed Thompson sampler convergence trajectory * ci: retain one plugin smoke execution and its report * Register smoke workflow test with its dedicated Node runner * Prevent same-millisecond hierarchy embedding index collisions
ruflo-plugin-creator
Scaffold, validate, and publish new Claude Code plugins with proper structure, MCP tool wiring, AND the canonical plugin contract (ADR + smoke + Compatibility + namespace coordination).
Install
/plugin marketplace add ruvnet/ruflo
/plugin install ruflo-plugin-creator@ruflo
Features
- Scaffold plugins: Generate complete plugin directory structure in seconds, including the canonical contract (ADR-0001, scripts/smoke.sh, README contract sections)
- Validate format: Check plugin.json, SKILL.md frontmatter, and file references
- MCP tool wiring: Auto-discover and wire ruflo MCP tools into skills, with drift warnings for known-broken tool references (
embeddings_embed, namespace passed toagentdb_hierarchical-*/agentdb_pattern-*) - Marketplace integration: Update marketplace.json for distribution
Commands
/create-plugin-- Interactively scaffold a new Claude Code plugin
Skills
create-plugin-- Generate plugin structure with skills, commands, agents, ADR-0001, smoke test, and contract sectionsvalidate-plugin-- Validate plugin format and catch issues before publishingcreate-mod-- Scaffold a Claude Code mod (function hooks) fromtemplates/mod/(scripts/scaffold-mod.mjs <name> <dir>; the manifest isplugin.json.tmplso validators do not read it as a nested plugin): hybridhooks.jsonwith a classic fallback that stands down while the module runs, a host adapter over literal$calls,userConfig, engine-kit tests, tsconfig. Validated withclaude plugin validateandclaude plugin test(ruflo ADR-404)
Compatibility
- CLI: pinned to
@claude-flow/cliv3.6 major+minor. - Verification:
bash plugins/ruflo-plugin-creator/scripts/smoke.shis the contract.
Canonical plugin contract (what gets scaffolded)
Every plugin scaffolded by this plugin inherits the same shape every other plugin in the ruflo family adopted via its own ADR-0001:
plugins/<name>/
├── .claude-plugin/plugin.json # version, keywords, mcp keyword
├── skills/<skill>/SKILL.md # name + description + allowed-tools (no wildcards)
├── commands/<command>.md # name + description + dispatch logic
├── agents/<agent>.md # name + description + model
├── docs/adrs/0001-<name>-contract.md # ADR (Proposed) — pinning, namespace, smoke scope
├── scripts/smoke.sh # Structural contract, ≥8 checks
└── README.md # Compatibility + Namespace coordination + Verification + ADR
MCP-tool drift to avoid
Lessons learned from sibling-ADR fixes — the scaffolder warns about these:
| Bug | Real tool / pattern |
|---|---|
embeddings_embed referenced as a tool |
Use embeddings_generate (the _embed name does not exist) |
namespace arg passed to agentdb_hierarchical-* |
Use tier (working/episodic/semantic), or use memory_* for namespaced reads/writes |
namespace arg passed to agentdb_pattern-* |
Don't pass it — ReasoningBank routes; fallback writes to pattern reserved |
pattern and patterns confused |
They are different reserved namespaces |
| Hard-coded "19 AgentDB controllers" | Defer to agentdb_controllers runtime; real count varies (20 MCP tools, 29 controller names) |
Verification
bash plugins/ruflo-plugin-creator/scripts/smoke.sh
# Expected: "11 passed, 0 failed"
Architecture Decisions
Related Plugins
ruflo-agentdb— namespace convention owner;agentdb_controllersruntime is the canonical controller listruflo-cost-tracker,ruflo-market-data,ruflo-migrations,ruflo-observability— each fixed namespace-routing bugs the scaffolder now warns aboutruflo-knowledge-graph,ruflo-market-data— each fixedembeddings_embedreferences the scaffolder now warns about
As a mod
Function-hook mod (ADR-445, pattern of ruflo-agentdb), loaded from hooks/hooks.json → hooks/register.ts. This plugin's own tools only search the plugin store, so there is no guard and no userConfig: the mod is a local command and a status file.
- Command:
/creator-modanswers locally with no model call, read-only. Verbs:status;reserved <plugin-dir>lists the command and skill names a plugin already uses (a mod command must not reuse one);check <plugin-dir>summarises its manifest,hooks.jsonmodules anduserConfigoptions. Paths with..are refused. - Status file:
.claude-flow/creator-mod/status.json({version, updatedMs, checked, reserved, lastTarget}). - Safety: no network, no process spawning.
- Scaffold status contract (0.4.1):
/create-modscaffoldshooks/status.ts, which writes.claude-flow/<name>-mod/status.jsonwith the fields the console's Mods section reads:version: 1,summary,modVersion,guard,calls(a number),blocked,lastDenied(only once the mod has refused something), plusupdatedMs/startedMs. The scaffold'stests/status.test.tsasserts that shape; the steps are inskills/create-mod/SKILL.md. - Test:
claude plugin validate plugins/ruflo-plugin-creator,claude plugin test plugins/ruflo-plugin-creator,bash plugins/ruflo-plugin-creator/scripts/smoke.sh. The runner sweeps intemplates/mod/tests(the scaffold's own test, which only passes against a scaffolded mod), so expect that one file to fail from the plugin root.node plugins/ruflo-plugin-creator/scripts/test-scaffold-mod.mjsscaffolds a mod from the template and holds it toclaude plugin validateandclaude plugin test.