GitHubのトレンドを見ていたら、ruvnet/ruflo が6位に入っていました。確認した時点でスターは 75,420。説明には「The original agent harness」とあり、README には「Agent = Model + Harness」という一文があります。モデルが書き、ハーネスが道具・記憶・ループ・制御を与える、という整理です。
私は受託の検収を長く担当してきたので、新しいツールを見ると、機能より先に「入れたら何が書き込まれるのか」が気になります。動いた=品質OK ではありません。init が成功と表示することと、チームのリポジトリに入れてよいことは、別の話なんですよね。
そこで今回は、あえてモデルを外しました。Claude Code も API キーも使わず、ハーネス単体で何が起きるのかを測っています。
試した環境はこんな感じです。
- 使い捨ての Docker コンテナ(
node:22-bookworm、Debian 12.14、x86_64)。メモリ 4GB・CPU 2 に制限し、ホストのフォルダはマウントしない - Node v22.23.1 / npm 10.9.8
- ruflo は README の手順どおり
npx ruflo@latestで実行(表示された版はruflo v3.56.10)。リポジトリの HEAD は1882006a(2026-10-14) - 作業対象は、README を1行だけ置いた空の git リポジトリ
npx の初回は66秒、キャッシュは1.9GB
Claude Code を使わない場合、README が最初に示すのは npx ruflo@latest init wizard です。いきなり init を流す前に、まず版を表示するだけのコマンドを叩きました。これが返ってくるまでに1分以上かかりました。
# 1回目(npx のキャッシュが空の状態。所要は date +%s%N の差で計測)
$ npx -y ruflo@latest --version
npm warn deprecated koa-router@14.0.0: Please use @koa/router instead, starting from v9!
npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
npm warn deprecated prebuild-install@7.1.3: No longer maintained. ...
ruflo v3.56.10
rc=0 npx_cold_ms=65966
# npx のキャッシュの大きさと、大きい順の中身
npx_cache=1.9G
pkgs_in_npx=371 top_level # package.json は全部で 776 個
547.8 MB onnxruntime-node
273.4 MB @agntcy
221.1 MB @xenova
140.4 MB onnxruntime-web
103.7 MB @opentelemetry
72.4 MB @anthropic-ai
# 2回目(キャッシュあり)
ruflo v3.56.10
npx_warm_ms=788
npm のメタデータ上、ruflo 本体の展開サイズは約8.2MBです。依存は @claude-flow/cli の1つだけで、その先に通常の依存28個と optional 12個がぶら下がっています。結果として、手元には1.9GBが展開されました。半分近くは ONNX Runtime と Transformers.js、つまりローカルで埋め込みを計算するための部品です。
正直なところ、ここが最初のつまずきでした。初回の66秒、出力は npm の警告と版の表示だけで、進み具合は分かりません。もし対話型の init wizard から始めていたら、固まったと思って Ctrl+C を押していたと思います。
もう一つ気になったのは、後で見る .mcp.json が npx -y ruflo@latest mcp start で MCP サーバを起動する設定になっている点です。版を固定していないので、新しい版が出れば次の起動でそれを取りにいく書き方です。手元と同じ条件なら、新しいメンバーは最初の起動でこの66秒と1.9GBを払うことになります。
init が書いたものを数えた
空のリポジトリで npx -y ruflo@latest init を流しました。7,748ms で終わり、終了コードは0です。最後に要約が表示されます。
$ npx -y ruflo@latest init
Transformers.js loaded: Xenova/all-MiniLM-L6-v2
RuFlo V3 initialized successfully!
| Directories: 12 created |
| Files: 102 created |
| Skills: .claude/skills/ (21 skills) |
| Commands: .claude/commands/ (16 commands) |
| Agents: .claude/agents/ (17 agents) |
[INFO] Hooks: 7 hook types enabled in settings.json
[WARN] No runnable claude binary on PATH: the plugins are enabled in settings, not installed.
# 実際に増えたファイル(.git と元からある README.md を除く)
$ find . -path ./.git -prune -o -type f -print | wc -l
253 # README.md を除くと 252。合計 4.3M
148 .claude/commands
44 .claude/helpers
21 .claude/skills
18 .claude/agents # .md は 17
$ git status --porcelain
?? .agents/
?? .claude-flow/
?? .claude/
?? .gitignore
?? .mcp.json
?? .swarm/
?? CLAUDE.md
?? ruvector.db
数え直すと、表示と実際が合わない箇所がいくつかありました。README の Quick Start の表に書かれた数字も並べておきます。
| 項目 | init の表示 | README・—help の記載 | 実際に数えた値 |
|---|---|---|---|
| 作成ファイル | 102 | 記載なし | 252(4.3M) |
| commands | 16 | 60+(README) | 直下 16 項目(13 フォルダ+3 ファイル)、.md は 148 |
| agents | 17 | 98(README)/既定は約24(—help) | 6 フォルダに .md が 17 |
| skills | 21 | 30(README) | 21 |
| hooks | 7 hook types | 記載なし | 10 種類のイベントに 16 コマンド |
| MCP ツール | 表示なし | 314(README) | 358(tools/list) |
断っておくと、README の 98 agents は --all-agents や --full を付けた場合の数かもしれません。init --help には「既定は約24の substrate agents、--all-agents で約89に戻す」とあります。今回はどちらも試していないので、既定の init で何が起きるかだけを書いています。
それでも、要約の「102」と実際の252は、同じ既定の init での食い違いです。検収でファイル数を受け入れ基準にしていたら、ここで止めていたところです。
hooks は .claude/settings.json に入ります。PreToolUse・PostToolUse・UserPromptSubmit・SessionStart・SessionEnd・Stop・PreCompact・SubagentStart・SubagentStop・Notification の10種類です。どれも node .claude/helpers/hook-handler.cjs などを呼びます。プロジェクトにそのファイルが無いときは $HOME/.claude/helpers/ の同名ファイルに切り替える書き方でした。timeout が 30 のフックと 3000〜15000 のフックが同じファイルに並んでいたのも気になりましたが、単位の扱いまでは確かめていません。
権限の allow には Bash(node .claude/*) と mcp__claude-flow__* が入りました。.claude/ 配下のスクリプトと ruflo の MCP ツールは、確認なしで実行できる設定です。
リポジトリの外にも書いていた
init のあとでホームディレクトリを見て、少し驚きました。プロジェクトの外にも書き込みがあったからです。
# init の前後で変わった /root 配下のファイル(npm のキャッシュを除く)
/root/.config/ruflo/policy-trust/111b1182.../ledger-anchor-head.json
/root/.claude/CLAUDE.md
$ cat /root/.claude/CLAUDE.md # 322 バイト・4 行
# Ruflo Integration (auto-generated by ruflo init)
When working on multi-file tasks or complex features, use ToolSearch to find and invoke ruflo MCP tools.
Key tools: memory_store, memory_search, hooks_route, swarm_init, agent_spawn.
Check system-reminder tags for [INTELLIGENCE] pattern suggestions before starting work.
# .claude/settings.json に足された設定(init のログより)
+ enabledPlugins["ruflo-mods@ruflo"] = true
+ enabledPlugins["ruflo-swarm@ruflo"] = true
+ enabledPlugins["ruflo-console@ruflo"] = true
+ extraKnownMarketplaces.ruflo
+ env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS = "1"
Each teammate's first trusted interactive Claude Code start clones github.com/ruvnet/ruflo; ...
# このまま git add -A したら入るもの(抜粋)
$ git add -A --dry-run | wc -l
263
add '.claude/settings.json.bak-ruflo-mods-1792152483699'
add '.swarm/agentdb-memory.db'
add '.swarm/memory.db'
add 'ruvector.db'
~/.claude/CLAUDE.md は、そのユーザーが Claude Code で開くすべてのプロジェクトに効くファイルです。そこに「ruflo の MCP ツールを探して使え」という4行が足されます。init --help には、これを止める --no-global が載っていました(今回は試していません)。
settings.json の plugins の設定も、ログ自身がはっきり書いています。この settings.json をコミットすると、チームメンバーがそのプロジェクトで最初に Claude Code を信頼して起動したときに、github.com/ruvnet/ruflo が clone されます。悪いことだとは言いません。ただ、init を流した本人だけでなく、チーム全員の端末の挙動が変わる設定です。レビューで見落とさないようにしたいところです。
.gitignore も init が作ります。ただし直下の .gitignore の対象は .env 系と .claude-flow/ の data・logs・sessions だけでした。.claude-flow/ の中にも .gitignore がありますが、こちらも data・logs・sessions・neural・console とログ類だけです。リポジトリ直下の ruvector.db(1,589,248 バイト)や .swarm/ の SQLite は、どちらの対象にも入っていません。後で試すメモリの書き込みで更新されたのも、この ruvector.db と .swarm/ の DB でした。書き込みのあとに測ると、WAL を含めて約5.3MBあります。git add -A すれば、これがまとめてコミット対象になります。
モデル抜きのハーネスは何をするのか
ここからが本題です。npx -y ruflo@latest mcp start を自作の小さな stdio クライアントから起動し、JSON-RPC で直接叩きました。Claude Code は使っていません。
まず tools/list が返したツールは358個、定義の JSON は271,522バイトでした。接頭辞で数えると、hooks が44、wasm が27、agentdb が20、metaharness が16、memory が15です。MCP クライアントにつなぐと、この定義がまるごとクライアントに渡ります。ToolSearch のような仕組みが無いクライアントだと、このサイズを毎回抱えることになるはずです(これは推測で、今回は確かめていません)。
次に memory_store で4件を記憶させました。英語と日本語で、認証の話とDB移行の話を1件ずつです。API キーは何も設定していませんが、応答には "hasEmbedding": true と "embeddingDimensions": 384 が付いていました。init のときに取得したローカルの埋め込みモデル(Xenova/all-MiniLM-L6-v2、model.onnx は90,387,606バイト)で計算しているようです。1件目だけ1,702msかかり、2件目以降は17〜96msでした。
そのうえで、意味で探す memory_search を英語と日本語で投げました。
| 記憶させた文(key) | クエリ「token-based login flow」 | クエリ「トークンでログインする仕組み」 |
|---|---|---|
| Use JWT access tokens stored in httpOnly cookies for login sessions(en-auth) | 0.520(1位) | 0.025(3位) |
| ログイン状態は httpOnly Cookie に入れた JWT で保つ(ja-auth) | 0.229(2位) | 0.597(2位) |
| デプロイ前に Flyway でデータベースのマイグレーションを流す(ja-db) | 0.002(3位) | 0.676(1位) |
| Run database migrations with Flyway before deploying(en-db) | 0(4位) | 0.004(4位) |
英語のクエリは、ちゃんと認証の文を1位に返しました。日本語のクエリでは、DB移行の文が0.676で1位になり、認証の文(0.597)を上回っています。日本語どうしは、中身に関係なく近いと判定されているように見えます。ただし4件・2クエリだけの観測なので、断定はしません。init --help には --embedding-model で別の ONNX モデルを指定する口があります。日本語で記憶を貯めるなら、まず自分のデータでこの表を作るのが先だと思います。
最後に、ハーネスらしい操作を順に呼びました。
# swarm を作り、エージェントを1体「spawn」し、タスクを作る(モデルも API キーも無し)
>>> swarm_init {"topology":"hierarchical","maxAgents":4} | 8ms | isError=false
{ "success": true, "swarmId": "swarm-1792152616943-xuwike", ..., "persisted": true }
>>> agent_spawn {"agentType":"coder","model":"haiku","task":"write a hello world in Python"} | 191ms
{ "success": true, "agentId": "agent-1792152616949-lmryi0", "status": "registered",
"note": "Agent registered for coordination. Three execution paths: (1) call agent_execute(agentId, prompt)
— direct LLM call via Anthropic Messages API (requires ANTHROPIC_API_KEY); (2) Claude Code Task tool
— spawns a real subagent; (3) claude -p — headless background instance." }
>>> task_create {"type":"feature","description":"add a /health endpoint"} | 10ms
{ "taskId": "task-1792152617152-a149wg", ..., "status": "pending", ..., "assignedTo": [], ... }
>>> swarm_status {} | 2ms
{ "status": "running", "agentCount": 1, "taskCount": 0, ... }
# 呼び出しのあとに更新されたファイル(抜粋)
./.swarm/agentdb-memory.db
./ruvector.db
./.claude-flow/tasks/store.json
./.claude-flow/swarm/swarm-state.json
./.claude-flow/agents/store.json
全部 success です。ただ、agent_spawn が返した状態は registered で、note に書かれているとおり、実際に動かすには API キーか Claude Code が要ります。swarm_status は running と答えますが、ps で見ても、エージェントらしきプロセスは何も起動していませんでした。作ったタスクも assignedTo は空のままです。
ここは使い方次第の部分です。私は「running」を、プロセスが走っている意味ではなく、台帳上の状態として読みました。ハーネスはモデルが来るまで、状態を JSON と SQLite に書き留めて待っている。README の「Agent = Model + Harness」は、文字どおりの意味だったわけです。
試してみての所感
今思えば、モデル抜きで触ったのは正解でした。モデルが動いていると、成果物の出来に目が行きます。その裏でハーネスがリポジトリやホームに何を書いたかは、意外と見落とします。
テストは目的ではなく手段です。今回の目的は、チームに入れてよいかを判断する材料をそろえることでした。私がこのツールを現場に入れるなら、受け入れ基準はこう置きます。
initは使い捨てのディレクトリかコンテナで一度流し、増えたファイルをgit statusで全部見てからにする- ホームの
~/.claude/CLAUDE.mdを書き換えさせない(--no-globalを付けて確かめる) .mcp.jsonのruflo@latestは版を固定し、初回の66秒・1.9GBを CI やオンボーディングの手順に書いておくruvector.dbと.swarm/を.gitignoreに足し、記憶の中身をコミットしない- 日本語でメモリを使うなら、自分の文章で検索の順位表を作ってから使う
358個のツールと10種類のフックを一度に入れるより、--minimal(今回は試していません)から始めて必要な分だけ足すほうが、私の性分には合っています。一方で、モデル無しでも TUI(ruflo tui --plain、2,381ms)や状態の台帳はきちんと動きました。土台としての作りは丁寧だと感じます。モデルをつないだ本番の動きは、この受け入れ基準を満たしてから、改めて試すつもりです。