rufler
Health Uyari
- No license รขโฌโ Repository has no license file
- Description รขโฌโ Repository has a description
- Active repo รขโฌโ Last push 0 days ago
- Low visibility รขโฌโ Only 5 GitHub stars
Code Gecti
- Code scan รขโฌโ Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions รขโฌโ No dangerous permissions requested
This tool is a Python-based MCP server and wrapper designed to simplify the deployment of autonomous, multi-agent AI swarms. It acts as an automation layer that turns a single YAML configuration file into a fully operational AI workflow using Claude.
Security Assessment
Overall Risk: Medium. The code scan did not find dangerous patterns, hardcoded secrets, or requests for excessive permissions. However, the tool inherently functions by executing shell commands and managing background processes (such as daemon lifecycles) on your machine. It also interacts with AI models and explicitly includes an "AI decomposition" feature that runs external processes (e.g., `claude -p`). While the underlying code appears safe, its core functionality involves orchestrating autonomous agents that can execute tasks, which requires a high level of trust from the user.
Quality Assessment
The project is actively maintained, with its last code push occurring today. On the downside, it currently has no license file, meaning there are no formal legal terms granting usage rights or clarifying liability. Additionally, it has extremely low community visibility (only 5 GitHub stars), indicating that very few people have reviewed or tested the code. Because it acts as an abstraction layer wrapping another CLI tool (`ruflo`), it relies heavily on the stability of that external dependency.
Verdict
Use with caution. The code is actively maintained and clean of obvious threats, but the lack of a license, low community adoption, and highly autonomous execution capabilities mean it should be thoroughly tested in a sandboxed environment before integrating into trusted systems.
๐ The simple wrapper of leading agent orchestration platform for Claude. Deploy intelligent multi-agent swarms, coordinate autonomous workflows, and build conversational AI systems. Features enterprise-grade architecture, distributed swarm intelligence, RAG integration, and native Claude Code / Codex Integration
One command. One YAML file. A whole autonomous AI swarm.
rufler is a Python wrapper around ruflo (the TypeScript AI agent orchestration CLI from the claude-flow family) that turns a dozen bootstrap commands into a single, declarative rufler_flow.yml file.
Instead of memorizing ruflo init โ daemon start โ memory init โ swarm init โ hive-mind init โ hive-mind spawn --claude --objective=..., you write a YAML file that describes your project, your agent team and your task, and run:
rufler run
rufler handles preflight checks, daemon lifecycle, swarm/memory/hive-mind init, objective composition, logging, multi-task sequencing, AI-driven task decomposition, background supervision and live log following.
Why rufler and not ruflo directly?
ruflo is powerful but low-level. It exposes ~26 commands and 140+ subcommands that all have to be called in the right order with the right flags. For day-to-day driving of an autonomous Claude-Code swarm, that's a lot of ceremony.
Problem with raw ruflo |
How rufler fixes it |
|---|---|
6โ10 shell commands to boot a project (init โ daemon start โ memory init โ swarm init โ hive-mind init โ hive-mind spawn --claude --objective=...) |
One command: rufler run reads your YAML and runs the whole pipeline. |
| Objective prompt must be assembled by hand โ project name, task body, every agent's role and prompt, autonomy flags | Auto-composed objective from rufler_flow.yml โ agents are sorted lead โ senior โ junior and injected into a single coherent prompt. |
| No native multi-task support โ you spawn one hive per task manually | Multi-task mode (task.multi: true) โ run a list of subtasks sequentially or in parallel from one YAML. |
| No task decomposition | AI decomposition (task.decompose: true) โ rufler calls claude -p to split a big main task into N subtasks before spawning the swarm. |
| Backgrounding is fragile: detaching from terminal, redirecting stdout, closing fds | Built-in supervisor (rufler.logwriter) โ detached runs write a clean NDJSON log and never leak fds or tie up your terminal. |
No uniform log โ ruflo/Claude emit mixed ANSI text + stream-JSON + plain logs |
Normalized NDJSON: every line is a JSON object with ts, src (claude / ruflo / rufler), detected level and cleaned text. Trivial to parse, tail, grep. |
| No way to see "what's the swarm doing right now?" without 3 different commands | rufler follow โ tail -f with makeup: live dashboard of session, tasks, tokens, last tool activity, recent events. |
| Preflight failures are silent until something crashes mid-run | rufler check and rufler doctor-style start gate โ validates node, claude, ruflo and config before touching anything. |
| No graceful teardown | rufler stop โ shuts down autopilot, hive-mind, daemon and records post-task metrics in one call. |
Flags like --dangerously-skip-permissions get silently dropped when --objective is huge |
Belt-and-suspenders: rufler also writes .claude/settings.local.json with permissions.defaultMode=bypassPermissions so headless runs never stall on a prompt. |
| YAML config? There isn't one. | rufler_flow.yml โ checked into your repo, reviewable in PRs, diffable over time. |
| Third-party skills have to be cloned / vendored / copied by hand | skills.sh integration โ list GitHub repos, owner/repo shorthands, or paste full skills add โฆ commands under skills.custom; rufler runs npx skills add for you and verifies every SKILL.md before the swarm starts. |
In short: ruflo is the engine, rufler is the ignition + dashboard + cruise control.
Features
- Single-file project config โ
rufler_flow.ymldescribes project, memory, swarm, task and the agent team. - One-command boot โ
rufler runruns checks โ init โ daemon โ memory โ swarm โ hive-mind โ spawn. - Docker-like run lifecycle โ
rufler run(foreground, Ctrl+C kills) /rufler run -d(detached background), just likedocker run. Every invocation gets an 8-char hex id. - Cross-project registry โ
~/.rufler/registry.jsontracks every run from every project.rufler ps/rufler ps -a/rufler logs <id>/rufler follow <id>/rufler stop <id>all work from any directory. - Project rollup โ
rufler projectsshows the last-run timestamp and total runs per project name, survivingrufler rmand pruning. - Rich status model โ
running/exited/failed(rc)/stopped/dead, computed from pid liveness + log tail + finish marker. - Token usage tracking โ
rufler tokensreports input/output/cache tokens per run, per project, and grand total across all projects. Per-project totals are cumulative and surviverufler rm. - Agent inspection โ
rufler agentslists every agent in the flow with type, role, seniority, depends_on and a 150-char prompt preview (--fullfor the full body). - Soft agent DAG โ declare
depends_on: [architect]on an agent and rufler injects GATE/HANDOFF blocks into the objective so downstream agents poll shared memory for an upstream brief and approval before starting work. Cycles, self-deps and unknown names are rejected at load time. - Task tracking โ every task gets a sub-id (
a1b2c3d4.01), persistent status (queued / running / exited / failed / stopped / skipped), per-task token accounting, and timing.rufler tasksshows it all;rufler tasks <sub-id> -vprints a detailed card. - Resume on restart โ
rufler runautomatically detects the previous run for the same project, skips already-completed tasks, and reuses decomposed task files. No wasted claude calls.--newforces a clean start,--from Nresumes from a specific task slot. - Soft resume via memory โ the composed objective tells agents to probe the shared memory namespace for prior progress before restarting work, so an interrupted run can pick up where it left off.
- Mono, multi and decomposed tasks โ run one big task, an explicit list of subtasks, or let Claude decompose
maininto N subtasks. - Sequential and parallel run modes for multi-task groups.
- Task chaining (
task.chain: true) โ in sequential multi-task mode, each newclaude -psession receives a compressed retrospective of all previous tasks (body + report), so it has full context without sharing a session. Per-task override withchain: falseon individual group items. - Deep Think (
task.deep_think: true) โ before decomposing or executing, rufler spawns a read-onlyclaude -psession (configurable model, default opus) that scans the project structure, reads key files, and writes a structured analysis to.rufler/analysis.md. The analysis is injected into the decomposer and/or the agent objective so every downstream step has project-aware context. Cached on re-run;--newforces rescan. - Detached background mode with a proper supervisor process and NDJSON logs.
- Live dashboard via
rufler followโ 4-panel TUI with task list, session stats, AI conversation stream (thinking, text, tool calls), and system events. Tails all per-task logs in multi-task mode. - Custom decomposer prompt โ override the task-splitter prompt inline or from an
.mdfile. - Typed dataclass config โ schema validation, role/seniority checks, unknown-key tolerance.
- External prompts โ agents and tasks can be inline OR loaded from
.mdfiles, so prompts stay out of YAML. - skills.sh integration โ pull third-party Claude Code skills straight from the yml. Drop a repo URL, an
owner/reposhorthand, or paste a fullskills add โฆcommand intoskills.custom; rufler shells out tonpx skills addpre-run, verifies eachSKILL.mdlanded, and reports results in the plan banner. Mix-and-match with local paths and ruflo's built-in packs in the same list. - MCP server management โ declare MCP servers in
mcp.serversand rufler registers them with Claude Code viaclaude mcp addduring init. Supports stdio, http, and sse transports with env vars and headers.rufler mcpinspects what's declared vs registered. - Automatic reports โ after each task (
on_task_complete) and after all tasks (on_complete), rufler spawns a short claude session to write a markdown report. Enabled by default; custom prompts supported inline or from.mdfiles. - Graceful shutdown โ
rufler stopends the session cleanly and writes post-task hooks.
Installation
Prerequisites
- Python 3.9+
- Node.js 20+ and npm 9+
- Claude Code CLI (
claudeon$PATH) โ see claude.ai/code - ruflo โ rufler will resolve it via
$RUFLER_RUFLO_BIN, localnode_modules,$PATH, npm global bin, ornpx -y ruflo@latestas a last resort. You can also runnpm i -g ruflo.
Install rufler
From source (until it's on PyPI):
git clone https://github.com/lib4u/rufler.git
cd rufler
pip install -e .
Verify:
rufler --help
rufler check
rufler check will tell you exactly which dependency (node / claude / ruflo) is missing and how to install it.
Quickstart
# 1. Create a sample flow file in the current directory
rufler init
# 2. Edit rufler_flow.yml โ change project name, task, agents
$EDITOR rufler_flow.yml
# 3. Dry run โ prints the composed objective and the plan, runs nothing
rufler run --dry-run
# 4. Launch the swarm (foreground, streams to terminal AND to NDJSON log)
rufler run
# 5. Or launch detached in the background
rufler run -d
# 6. Watch it work
rufler follow # live dashboard
rufler status # one-shot status
rufler progress # autopilot task progress
rufler logs # recent autopilot events
# 7. Clean shutdown
rufler stop
Commands
| Command | What it does |
|---|---|
rufler check |
Verify that node, claude and ruflo are available and report how each was resolved. |
rufler init |
Create a sample rufler_flow.yml in the current directory. |
rufler agents [ID] |
List agents declared in the flow (name, type, role, seniority, depends_on, 150-char prompt preview). With an id โ reads the flow file from that run's registry entry. --full prints full prompt bodies. |
rufler run [FLOW_FILE] |
Validate config, run ruflo init/daemon/memory/swarm/hive-mind, spawn the Claude swarm. Foreground by default; Ctrl+C kills. Add -d to detach. Resumes from last completed task by default; --new to start fresh, --from N to resume from slot N. |
rufler build [FLOW_FILE] |
Same preparation pipeline as rufler run (checks โ ruflo init โ daemon โ memory โ swarm โ hive-mind โ skills install) without launching Claude. Useful to apply yml changes (skills, memory, swarm) to an existing project. --skip-init skips the ruflo init + daemon step for quick re-builds. |
rufler skills |
Inspect installed skills in <project>/.claude/skills/ and show the yml snapshot. --available lists packs/skills in ruflo's bundled source tree. --delete wipes non-symlinked skill dirs from the project (with confirmation; -y to skip). |
rufler mcp |
List MCP servers declared in rufler_flow.yml. --active shows what's actually registered in ~/.claude.json for this project. Servers are added via claude mcp add during rufler run/build. |
rufler ps [ID] |
Docker-style list of runs. No args โ running only. -a โ all. --prune / --prune-older-than-days N โ clean stale entries. With an ID โ detailed view of one run (status, tasks, claude procs, log tail). |
rufler tasks [ID] |
List tasks for a run with sub-ids, status, tokens, timing. Without id โ latest run in cwd. -a โ all runs. --status running โ filter. Pass a task sub-id (e.g. a1b2c3d4.01 -v) for a detailed card with token breakdown and recent log events. --rm removes tasks from the registry; --rm-all removes all tasks in cwd; --rm-files also deletes task files and logs from disk. |
rufler projects |
Per-project rollup: last run id, when it last ran, total runs. Survives rufler rm and pruning. |
rufler logs [ID] |
Tail recent autopilot events. --raw prints the NDJSON log directly. -f / --follow streams new lines live. Pass a task sub-id (e.g. a1b2c3d4.01) to see only that task's log slice. |
rufler follow [ID] |
Live TUI dashboard: task progress (with status icons), AI conversation stream (thinking, text, tool calls), session stats, and system events. Tails all per-task logs automatically. |
rufler status [ID] |
System + swarm + hive-mind + autopilot status in one call. |
rufler watch [ID] |
Poll rufler status on a loop until Ctrl-C. |
rufler progress [ID] |
Autopilot task progress + recent iteration log. |
rufler stop [ID] |
Shutdown autopilot, hive-mind and daemon; record post-task hook; end session. --kill sends SIGTERM to the supervisor pids. |
rufler rm [ID]... |
Remove registry entries. --all-finished / --older-than-days N for bulk cleanup. Refuses running entries. |
rufler tokens [ID] |
Token usage report โ without id: per-project table + grand total. With an id: detailed breakdown. --by-task shows per-task token breakdown. --rescan re-parses run logs from disk. |
Every run-scoped command (logs, follow, status, watch, progress, stop) accepts an optional run id prefix as a positional argument. Without an id it resolves to the run started from the current working directory.
rufler run flags
rufler run [FLOW_FILE] [OPTIONS]
Arguments:
FLOW_FILE Positional path to flow yml. Overrides -c.
Options:
-c, --config PATH Path to rufler_flow.yml [default: rufler_flow.yml]
--dry-run Print the plan and composed objective, don't execute.
--skip-checks Skip node/claude/ruflo preflight.
--skip-init Skip ruflo init + daemon + memory init (useful for re-runs).
--non-interactive/--interactive
Run Claude Code headless (-p stream).
--yolo/--no-yolo Pass --dangerously-skip-permissions.
-d, --background/--foreground
Detach from terminal (implies --non-interactive --yolo).
--log-file PATH Override execution.log_file from yml.
--new Start all tasks from scratch: re-decompose and ignore
previous progress.
--from N Resume from task slot N (1-based), skipping tasks before it.
CLI flags always override the execution: section of rufler_flow.yml.
By default rufler run resumes from where a previous run stopped. It reuses existing decomposed task files (skipping the claude call) and skips tasks that already completed successfully (rc=0). Use --new to force a clean start.
rufler_flow.yml reference
Minimal example:
project:
name: my-service
description: Small Go HTTP service.
task:
main: |
Build a Go HTTP server on :8080 with a /health endpoint and unit tests.
autonomous: true
agents:
- name: coder
type: coder
role: worker
seniority: senior
prompt: Implement the server and tests.
Full schema:
project:
name: my-project # required-ish; drives objective header
description: Free-form description.
memory:
backend: hybrid # hybrid | sqlite | ...
namespace: my-project
init: true
checkpoint_interval_minutes: 5 # how often agents flush state to memory (0 = disable timer, still on events)
swarm:
topology: hierarchical-mesh # hierarchical | mesh | hierarchical-mesh | adaptive
max_agents: 8
strategy: specialized
consensus: raft # raft | byzantine | gossip | crdt | quorum
task:
# --- mono mode ---
main: | # inline main task
Build X, test Y, verify Z.
main_path: ./tasks/main.md # OR load from file
autonomous: true
max_iterations: 100
timeout_minutes: 180
# --- multi mode ---
multi: false # set true to enable group / decompose
run_mode: sequential # sequential | parallel
group: # explicit list of subtasks
- name: backend
file_path: tasks/backend.md
- name: frontend
content: | # inline alternative to file_path
Build the React frontend...
# --- decompose mode (AI decomposition) ---
decompose: false # true โ call claude -p to split `main`
decompose_count: 4 # how many subtasks to generate
decompose_dir: .rufler/tasks # where to write generated .md files
decompose_file: .rufler/tasks/decomposed_tasks.yml
decompose_prompt: | # optional inline decomposer prompt override
You split tasks. Placeholders: {n} and {main}.
Output YAML: tasks: [ {name, title, content} ]
decompose_prompt_path: ./prompts/decompose.md # OR load decomposer prompt from file
decompose_model: sonnet # model for the decompose call (default sonnet)
decompose_effort: high # thinking effort for decomposer (low/medium/high/max)
# --- deep think (project analysis before decompose/execute) ---
deep_think: false # true โ read-only claude session scans the project first
deep_think_model: opus # model for analysis (opus recommended for deep reasoning)
deep_think_effort: max # thinking effort for deep think (low/medium/high/max; default max)
deep_think_output: .rufler/analysis.md # cached; reused on re-run, --new to regenerate
deep_think_timeout: 600 # seconds
deep_think_budget: 1.50 # optional max spend in USD for the deep think session
deep_think_allowed_tools: "Read,Glob,Grep,Bash" # restrict --allowedTools (default: not set โ ALL tools including MCP)
deep_think_prompt: | # optional inline override
Analyze this project for: {main}
deep_think_prompt_path: ./prompts/analyze.md # OR load from file
# --- task chaining (sequential multi-task only) ---
chain: false # true โ inject compressed retrospective of previous tasks into the next task's prompt
chain_max_tokens: 2000 # word budget for the compressed retrospective (body + report combined)
chain_include_report: true # include the per-task report in the retrospective (requires on_task_complete.report)
# --- reports (two levels) ---
on_task_complete: # after EACH task completes
report: true # default: enabled
report_path: .rufler/reports/{task}.md # {task} replaced with task name
# report_prompt: | # optional custom prompt
# Summarize what this task accomplished.
# report_prompt_path: ./prompts/task-report.md
on_complete: # after ALL tasks complete
report: true # default: enabled
report_path: .rufler/report.md
# report_prompt: | # optional custom prompt
# Write a final project completion report.
# report_prompt_path: ./prompts/final-report.md
execution:
non_interactive: false
yolo: false # pass --dangerously-skip-permissions
background: false # detach from terminal
log_file: .rufler/run.log
# MCP servers โ registered with Claude Code via `claude mcp add -s project`
mcp:
servers:
- name: my-db # server name (unique, required)
command: npx # executable (required for stdio)
args: ["-y", "@anthropic/mcp-postgres"]
env:
DATABASE_URL: "postgresql://localhost/mydb"
- name: sentry # HTTP transport example
transport: http # stdio (default) | http | sse
url: "https://mcp.sentry.dev/mcp"
- name: corridor # HTTP with auth headers
transport: http
url: "https://app.corridor.dev/api/mcp"
headers:
Authorization: "Bearer ${CORRIDOR_TOKEN}"
agents:
- name: architect
type: system-architect # any ruflo agent type, or custom string
role: specialist # queen | specialist | worker | scout
seniority: lead # lead | senior | junior
prompt: | # inline prompt
Define package layout and interfaces.
# OR
prompt_path: ./prompts/architect.md
- name: coder
type: coder
role: worker
seniority: senior
prompt: Implement the design.
depends_on: [architect] # soft DAG โ see "Agent dependencies" below
# Per-task chain override (inside group items):
# - name: review
# file_path: tasks/review.md
# chain: false # this task does NOT receive the retrospective (overrides task.chain)
Validation rules
agent.roleโ{queen, specialist, worker, scout}agent.seniorityโ{lead, senior, junior}- Each agent needs
promptORprompt_path. agent.depends_onmust be a list of known agent names. Self-deps and cycles are rejected at load time.nulland[]are equivalent (no deps). Duplicates are deduped.task.run_modeโ{sequential, parallel}.mcp.servers[*].transportโ{stdio, http, sse}. stdio requirescommand, http/sse requireurl. Duplicate names are rejected.task.groupmay be a list or a dict โ both are accepted.- Unknown keys inside
groupitems are ignored (forward-compatible).
Multi-task mode
Explicit group
task:
multi: true
run_mode: sequential
group:
- name: backend
file_path: tasks/backend.md
- name: frontend
file_path: tasks/frontend.md
- name: e2e
file_path: tasks/e2e.md
Run:
rufler run
rufler spawns a hive for backend, waits for its NDJSON log to emit log ended, then spawns frontend, then e2e.
Parallel group
task:
multi: true
run_mode: parallel
group:
- name: service_a
file_path: tasks/a.md
- name: service_b
file_path: tasks/b.md
rufler run -d # parallel mode only makes sense detached
Each subtask gets its own background supervisor and its own NDJSON log under .rufler/.
AI-decomposed subtasks
task:
multi: true
decompose: true
decompose_count: 4
main: |
Build a URL shortener: REST API, SQLite storage, CLI client, tests.
On rufler run, rufler calls claude -p with the decomposer prompt, parses the YAML response, writes .tasks/task_{1..4}.md and a companion .tasks/decomposed_tasks.yml, then runs the group sequentially.
You can override the decomposer prompt inline (decompose_prompt) or from a file (decompose_prompt_path). Templates may reference {n} (count) and {main} (main task) as placeholders โ literal { / } in the template are safe.
When to use which mode
| Situation | Recommended mode | Why |
|---|---|---|
You already wrote the N subtasks as .md files and the order / scope matters for review |
mono โ single task OR explicit group |
Reproducible, diffable in PRs, no LLM randomness. You control everything. |
| One focused goal, no real decomposition needed | mono (multi: false, main or main_path) |
Lowest overhead โ one hive, one log, one objective. |
| Hand-written skeleton with clear hand-off between stages (schema โ api โ tests โ ci) | group + run_mode: sequential |
Subsequent subtasks read prior agents' output from shared memory; order is load-bearing. |
Independent subtasks that can race (service_a, service_b, docs) |
group + run_mode: parallel + rufler run -d |
Each subtask gets its own supervisor and NDJSON log; you only need -d because parallel needs detached supervisors. |
| Big fuzzy goal, you want Claude to propose a split before executing | decompose: true |
rufler calls claude -p with the decomposer prompt, writes .tasks/task_{1..N}.md + a companion yml, then runs them as a group. |
decompose: true but you want to pin the split style (always "schema โ api โ tests โ ci") |
decompose_prompt (inline) or decompose_prompt_path (md file) with {n} / {main} placeholders |
The default prompt is generic; a custom template makes decomposition deterministic across runs of the same project. |
| You want to review the generated split before running the swarm | decompose: true + rufler run --dry-run |
Decomposition runs, files are written, plan banner is printed, then rufler stops before spawning. Inspect .tasks/*.md, then re-run without --dry-run. |
| Subtasks are independent enough for parallel but you don't want to hand-write them | decompose: true + run_mode: parallel + rufler run -d |
Works fine โ decomposer writes the group, supervisor spawns them in parallel. |
| A previous run crashed halfway through a group | group (or already-decomposed .tasks/decomposed_tasks.yml) + re-run |
Rufler's composed objective tells agents to probe shared memory for prior progress, so the swarm picks up where it left off instead of restarting from zero. |
| You need a soft DAG between agents inside each subtask (architect โ coder โ tester) | Either mode + depends_on: on the agents |
Orthogonal to group vs decompose โ depends_on injects GATE/HANDOFF clauses into the objective regardless of how the subtask came to be. |
| CI or one-shot experiment where reproducibility matters more than convenience | group with committed .md files |
Avoid decompose โ every run would re-ask the LLM and risk a different split. |
Rule of thumb: write group when the split is part of your spec; use decompose when the split itself is the question you're asking Claude.
Deep Think (project analysis)
By default, decompose: true splits a task "blindly" โ the decomposer LLM doesn't see the project's actual files, structure, or existing code. This leads to subtasks that may duplicate existing work, target wrong files, or miss dependencies.
task.deep_think: true fixes this by adding a read-only analysis phase before everything else:
0a. deep think โ claude -p --model opus (Read, Glob, Grep, Bash only) โ .rufler/analysis.md
0b. decompose โ claude -p --model sonnet (analysis injected as context) โ subtasks
1-N. execute โ hive spawn (analysis injected into every objective)
task:
main: "Add /users CRUD endpoint to the REST API"
deep_think: true
deep_think_model: opus # deep reasoning model (default opus)
deep_think_effort: max # thinking effort (low/medium/high/max)
deep_think_budget: 1.50 # optional max spend in USD
decompose: true
decompose_count: 4
decompose_effort: high # thinking effort for the decomposer
The analysis agent scans the project and writes a structured report:
- Project Overview โ language, framework, dependencies
- Directory Structure โ layout with descriptions
- Existing Implementation โ what's already built (with file paths)
- Gaps & Missing Pieces โ what's NOT implemented relative to the task
- Dependencies & Impact โ which files will be affected, risk areas
- Recommended Approach โ step-by-step plan informed by the analysis
This report is then:
- Injected into the decomposer prompt (when
decompose: true) โ so subtasks are project-aware - Injected into every agent objective โ so agents don't waste time re-discovering what exists
Caching. The analysis is saved to deep_think_output (default .rufler/analysis.md). On re-run, the cached file is reused. Pass --new to force a fresh scan.
Model selection. Use deep_think_model to pick the model for analysis โ both aliases (opus, sonnet) and full model IDs (claude-opus-4-6) are accepted. Opus is recommended for thorough reasoning; Sonnet is faster and cheaper for simpler projects.
Effort & budget. deep_think_effort controls reasoning depth (low/medium/high/max; default max). deep_think_budget sets an optional USD spending cap for the analysis session. Similarly, decompose_effort controls the decomposer's reasoning depth (default high).
MCP access. By default deep think has access to all tools โ file tools, Bash, and every registered MCP server. To restrict the session to specific tools only, set deep_think_allowed_tools:
task:
deep_think: true
deep_think_allowed_tools: "Read,Glob,Grep,Bash" # lock down to read-only file tools
When set, this passes --allowedTools to claude -p, limiting the session to the listed tools. When omitted (default), no restriction is applied โ deep think can use everything available, including MCP.
Without decompose. Deep Think is also useful in mono mode โ the analysis is injected directly into the single objective:
task:
main_path: tasks/feature.md
deep_think: true
deep_think_model: sonnet # cheaper for simple analysis
# decompose: false (default)
Task chaining
In sequential multi-task mode each subtask runs in its own claude -p session โ a fresh process with no memory of the previous session's conversation. By default the only link between tasks is the shared memory namespace (agents are asked to read/write checkpoints, but it's a soft contract).
task.chain: true makes the link explicit and hard: after each task completes, rufler compresses its body and report into a compact text block and injects it into the next task's prompt as a PREVIOUS TASK RETROSPECTIVE section. The new Claude session sees exactly what was asked, what was done, and what the report said โ without relying on memory polling.
task:
multi: true
run_mode: sequential
chain: true
chain_max_tokens: 2000 # word budget for the retrospective (default 2000)
chain_include_report: true # include per-task report in the retrospective (default true)
group:
- name: design
file_path: tasks/design.md
- name: implement
file_path: tasks/implement.md
- name: review
file_path: tasks/review.md
chain: false # this task does NOT receive the retrospective
How compression works. The compressor is deterministic (no AI call) and fast:
- Strips HTML tags, horizontal rules, bold/italic markers
- Flattens fenced code blocks into one-line summaries (
[code:python: def hello()โฆ]) - Downgrades markdown headers to
[Header]form - Collapses blank lines and whitespace
- Truncates to
chain_max_tokenswords
The token budget is split between the task body (~2/3) and the report (~1/3). If chain_include_report is false or no report exists, the full budget goes to the body.
Per-task override. Set chain: false on any group item to skip retrospective injection for that specific task, or chain: true on a single item when the global task.chain is false.
When to use chaining:
| Scenario | Use chain? |
|---|---|
| Tasks build on each other (design โ implement โ review) | Yes |
| Tasks are independent (service_a, service_b) | No โ use parallel mode instead |
| You already rely on shared memory and it works well | Optional โ chain adds redundancy |
| Prompt budget is tight (very long task bodies + many tasks) | Tune chain_max_tokens down |
Runs, projects and statuses
rufler keeps a central docker-like registry at ~/.rufler/registry.json so you can manage every run from any directory.
The run lifecycle
rufler run # foreground: Ctrl+C kills the swarm, exits 130
rufler run -d # detached: supervisor survives terminal close
# => rufler id: a1b2c3d4
Every rufler run gets an 8-char hex id (like a1b2c3d4). The id is printed at startup and is how every other command addresses the run.
rufler ps โ list runs
rufler ps # currently running (like `docker ps`)
rufler ps -a # everything ever run (like `docker ps -a`)
rufler ps a1b2 # detail view of one run by id prefix
rufler ps --prune # drop entries for projects whose base dir is gone
rufler ps --prune-older-than-days 7
Columns: ID | PROJECT | MODE | STATUS | TASKS | CREATED | LAST RUN | BASE DIR.
STATUS is docker-style โ Up 2m, Exited (0) 1h ago, Failed (2) 23h ago, Stopped 5m ago, Dead. CREATED is relative time since the run started; LAST RUN is relative time since it last changed state (finished_at for completed runs, started_at for live ones).
Run statuses
| Status | Meaning |
|---|---|
running |
At least one registered pid is alive |
exited |
Process ended cleanly with rc=0 |
failed (N) |
Process ended with non-zero exit code |
stopped |
User ended the run cleanly (rufler stop or Ctrl+C on foreground) |
dead |
Supervisor vanished without leaving a finish marker (e.g. killed with -9, crashed, power loss) |
Status is recomputed on every read from three signals: pid liveness, the log ended rc=N marker in the NDJSON log tail, and the finished_at timestamp written by rufler itself. Nothing is cached โ what you see is always the current truth.
rufler projects โ project rollup
rufler projects
Columns: PROJECT | LAST RUN | AGE | RUNS | BASE DIR. One row per unique project.name, sorted by last launch time. This rollup is stored separately from individual runs and survives rufler rm, rufler ps --prune and age-based pruning โ so you can always see when a project was last touched, even if all its individual run entries have been cleaned up.
rufler rm โ cleanup
rufler rm a1b2 e5f6 # remove specific runs
rufler rm --all-finished # remove everything that's not running
rufler rm --older-than-days 30 # bulk prune
rm refuses to delete runs that are currently running โ use rufler stop <id> first.
rufler tasks โ task tracking
rufler tasks # tasks of the latest run in cwd
rufler tasks a1b2 # tasks of a specific run
rufler tasks -a # all tasks across all runs
rufler tasks --status running # filter by status
rufler tasks a1b2c3d4.01 -v # detailed card for one task
Delete tasks:
rufler tasks a1b2c3d4.05 --rm # remove one task from the registry
rufler tasks a1b2c3d4 --rm # remove ALL tasks in that run
rufler tasks --rm-all # remove all tasks across all runs in cwd
rufler tasks --rm-all --rm-files # also delete .rufler/tasks/*.md and log files
rufler tasks a1b2c3d4 --rm --rm-files # delete one run's tasks + files
--rm / --rm-all only touch the registry bookkeeping by default. Add --rm-files to also delete on-disk task files (.rufler/tasks/*.md) and their logs.
Table columns: TASK ID | SLOT | NAME | STATUS | SOURCE | STARTED | DURATION | TOKENS | LOG.
Every task gets a sub-id in the format <run_id>.<slot> (e.g. a1b2c3d4.01). Status is derived lazily from log markers + pid liveness + registry data โ never cached.
| Task status | Meaning |
|---|---|
queued |
Run is still alive, task hasn't started yet |
running |
task_start marker found, no task_end, pid alive |
exited |
task_end with rc=0 |
failed |
task_end with rc != 0 |
stopped |
Run stopped before task finished |
skipped |
Run finished but task never started |
The detail view (-v) shows full metadata, per-task token breakdown (input / output / cache_read / cache_creation), and the last 10 log events for that task.
rufler tokens --by-task โ per-task token breakdown
rufler tokens --by-task # per-task tokens for the latest run
rufler tokens --by-task a1b2c3d4 # per-task tokens for a specific run
Task resume on restart
When you stop a run mid-way and restart:
rufler run -d # starts 4 tasks, task_1 and task_2 complete
# Ctrl+C / rufler stop
rufler run -d # auto-resumes from task_3
# => resuming: skipping 2 completed tasks, starting from task_3
Resume works at two levels:
- Decomposed task files โ if
.rufler/tasks/decomposed_tasks.ymlalready exists, rufler loads tasks from it instead of calling claude again. No wasted tokens on re-decomposition. - Completed tasks โ rufler finds the previous run for this project/directory in the registry, checks which tasks finished with
rc=0, and skips them. Comparison is by task name (not slot), so adding/removing tasks in the yml correctly triggers re-execution.
Flags:
rufler runโ resume by defaultrufler run --newโ ignore previous progress, re-decompose, start all tasks from scratchrufler run --from 3โ skip tasks 1-2 explicitly, start from slot 3
rufler follow โ live TUI dashboard
rufler follow # auto-picks the running (or latest) run in cwd
rufler follow a1b2 # follow a specific run by id
Four-panel dashboard:
โญโ rufler follow โโโโโโโโโโโโโโโโโโโโโโโ [running] โโ 00:04:23 โโฎ
โ model: claude-opus-4-6 swarm: hive-17762684 workers: 4 โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โญโ Tasks 2/4 โโโโโโโโโโโโโฎโญโ Session โโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ โ task_1 done 1m12sโโ model claude-opus-4-6 โ
โ โถ task_2 running 52sโโ tokens in=98 out=829 โ
โ task_3 queued โโ turns 14 โ
โ task_4 queued โโ last Write โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโฏโฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โญโ Conversation (task_2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ 14:23 think Planning the API routes โ need UserList... โ
โ 14:23 text I'll create the handler files with types... โ
โ 14:24 tool Write(src/api/routes.go) โ
โ 14:24 result File created successfully โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โญโ Log โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ 14:22 OK task_end task_1 done โ
โ 14:22 INFO task_start task_2 โ
โ 14:23 INFO session init (claude-opus-4-6) โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
- Tasks โ task list with status icons (โ โถ โ โ), duration, per-task tokens
- Session โ model, token totals, turns, last tool
- Conversation โ AI stream for the active task: thinking (3-5 lines), text (full), tool calls with params, tool results
- Log โ system events: task markers, hooks, errors, rate limits
In multi-task mode, follow tails all per-task log files simultaneously (not just the primary run.log).
Soft resume via shared memory (periodic checkpointing)
In addition to the task-level resume above, every composed objective ends with two auto-injected sections that force agents to continuously persist state to AgentDB:
# RESUME AWARENESS โ tells agents, before they start, to search the shared memory namespace (from memory.namespace) for prior progress using standard keys (checkpoint:latest, progress, decisions, blockers, completed, last_step). If they find state from a prior interrupted run, they continue from there instead of redoing work.
# CHECKPOINT DISCIPLINE โ tells agents to write a checkpoint to shared memory:
- Every
memory.checkpoint_interval_minutesminutes of wall-clock work (default: 5,0disables the timer). - Immediately after every file write, test run, build, sub-task, or design decision.
The objective prescribes exact key names so recovery is deterministic:
| Key | Contents |
|---|---|
checkpoint:latest |
Rolling pointer โ compact JSON of {current_step, done_steps[], next_step, open_questions[], last_ts} |
checkpoint:<unix_ts> |
Timestamped snapshot for history |
progress |
One-line human summary: what is done, what is next |
decisions |
Append-only list of design decisions |
blockers |
Anything stuck on; cleared when unstuck |
So an interrupted run leaves behind: its NDJSON log, any files it wrote to disk, and a constantly-refreshed checkpoint in AgentDB memory. The next rufler run picks up that memory state and reconstructs context from it. Rule of thumb baked into the prompt: if you just spent >2 minutes on something you could not reconstruct from the repo alone, it MUST go into memory before your next tool call.
Inspecting agents
rufler agents reads the flow file and prints one row per declared agent โ name, type, role, seniority, and a single-line 150-character preview of the prompt (whether it was inline or loaded from prompt_path).
# Agents from ./rufler_flow.yml in the current directory
rufler agents
# Agents from a specific past run (looks up its flow_file via the registry)
rufler agents a1b2c3d4
# Print full prompt bodies instead of the 150-char preview
rufler agents --full
Use this to sanity-check that every agent's prompt_path actually resolves and to eyeball the prompts you wired up before launching rufler run.
Agent dependencies (soft DAG)
You can declare that an agent must wait for other agents to finish before it starts:
agents:
- name: architect
type: system-architect
role: specialist
seniority: lead
prompt: Design the system.
- name: coder
type: coder
role: worker
seniority: senior
prompt: Implement the design.
depends_on: [architect]
- name: qa
type: tester
role: worker
seniority: junior
prompt: Write tests.
depends_on: [coder]
This builds a soft DAG: architect โ coder โ qa.
How it's enforced
rufler does not spawn agents in separate processes โ the whole team lives inside one hive-mind. Instead, build_objective injects two prompt sections per agent:
### GATEโ appended to every agent that hasdepends_on. Tells the agent: before doing any work, read these keys from the shared memory namespace; if any are missing, pollmemory_searchevery ~30s; never bypass a gate even if you "know what to do":instructions:<task>:<upstream>-><self>โ the work brief from upstreamapproval:<task>:<upstream>-><self>โ must equalapproved
### HANDOFFโ appended to every agent that has downstream agents waiting on it. Tells the agent it must publish a brief and anapprovedflag for each downstream agent, in that order, before its own work is considered done. If it must reject a downstream, it writesvalue='rejected: <reason>'.
Memory keys are scoped per task name (<task> segment), so multi-task runs sharing the same memory namespace can't pick up each other's briefs or approvals.
What's validated at load time
depends_onreferencing an unknown agent name โValueError.- An agent depending on itself โ
ValueError. - Cycles (e.g.
a โ b โ a) โValueErrorprinting the cycle path. depends_on: nullis normalised to[]. Duplicates are deduped while preserving order.
Caveats
This is soft enforcement โ a contract written in the prompt, not a process scheduler. Claude Code with autonomous: true and the checkpoint discipline section above respects it well in practice, but there is no OS-level guarantee that a downstream agent won't peek ahead. If you need hard ordering, split the work into multi-task sequential mode โ each task runs in its own claude -p process, and rufler only spawns the next task after the previous task's NDJSON log emits log ended.
You can verify the dependency graph at any time:
rufler agents # DEPENDS ON column shows each agent's upstreams
rufler run --dry-run # prints the full composed objective with GATE/HANDOFF blocks
Global skills (.claude/skills/)
Claude Code reads skill definitions from .claude/skills/<name>/SKILL.md at session start. rufler knows about three sources of skills and lets you mix them in one yml block:
| Source | yml field | Where it comes from |
|---|---|---|
| ruflo bundled packs | packs: / all: |
core, agentdb, github, flowNexus, browser, v3, dualMode packs shipped inside the ruflo npm package |
| ruflo standalone skills | extra: |
Individual skill dir names under ruflo's bundled .claude/skills/ source tree |
custom: (unified) |
custom: |
Local paths and skills.sh installs โ one list, path-first resolution |
skills:
enabled: true
clean: false # false (default) = keep ruflo init's ~30 defaults
# true = wipe non-symlinked skill dirs after
# `ruflo init` so yml is the single
# source of truth
all: false # true = every pack (overrides `packs`)
packs: [] # subset of {core, agentdb, github, flowNexus, browser, v3, dualMode}
extra: [] # individual skill dir names from ruflo's bundled source
# tree (e.g. ["my-bundled-skill"])
custom: # Unified list โ local paths + skills.sh installs.
# rufler tries the filesystem first; unknown strings
# fall back to `npx skills add <source>`. See below.
- ./skills/my-skill # local dir
- ~/shared/claude-skills/reviewer # local dir
- npx skills add https://github.com/samber/cc-skills-golang --skill golang-error-handling
- source: vercel-labs/skills # dict form (explicit skills.sh)
skill: azure-ai
The default behavior (empty yml skills: section)
rufler init creates a flow file where skills: is enabled but empty: packs: [], extra: [], custom: [], clean: false. On rufler run / rufler build, this means:
ruflo init --forceplants its standard bundle of ~30 default skills (core Claude Code + ruflo skills).- rufler adds nothing on top (no packs declared, no extras, no custom).
clean: falsetells rufler not to touch what ruflo just planted.
Net result: a fresh project gets ruflo's full default skill set out of the box, and you only need to edit skills: if you want to restrict the set (clean: true) or add your own (custom:) or pull in extra packs (packs:).
How each field is resolved
packsโcore,agentdb,github,v3are proxied toruflo init skills --<pack> --force(reuses ruflo's own installer).flowNexus,browser,dualModeare copied directly from ruflo's bundled.claude/skills/source tree.extraโ individual skill directory names under ruflo's bundled source tree (copied, not symlinked).customโ unified list mixing local paths and skills.sh installs. Each string entry is resolved path-first: absolute โ used as-is,~โ expanded, relative โ resolved against the directory containing the yml file. If the resolved path is an existing directory it's copied into<project>/.claude/skills/<basename>. If the string isn't a directory, rufler falls back to a skills.sh install vianpx skills add <source>. You can also embed a fullnpx skills add โฆcommand line or a dict ({source, skill?, agent?, copy?}) โ see the next section.- Where ruflo is found (for
extraand manual packs) โ$RUFLER_RUFLO_BINโ localnode_modules/.bin/rufloโ$PATHโ npm global bin. If ruflo is only reachable vianpx(no stable on-disk path),extraand manual packs print a warning and skip โ install ruflo locally or globally for those to work.customis unaffected โ it doesn't need ruflo's source tree.
clean: true โ yml as the single source of truth
By default, ruflo init always plants ~30 default skills regardless of yml. If you want the installed skill set to exactly match what your yml declares, set clean: true. rufler will then, after ruflo init runs and before installing from yml, wipe every non-symlinked directory under <project>/.claude/skills/. Symlinks (e.g. ones you created manually for dev work) are preserved.
skills:
enabled: true
clean: true # wipe ruflo's defaults
packs: [core] # ...and install only what's listed here
custom:
- ./skills/my-skill
Native skills.sh integration (inside custom:)
skills.sh is an open ecosystem of reusable agent skills hosted on GitHub and installed via the skills CLI (npx skills add <source>). rufler has native support โ drop the repo reference straight into custom: and rufler runs the CLI for you before the swarm launches. No second section to maintain.
rufler resolves each custom: string path-first: if the string points at a real directory on disk, it's copied locally; otherwise it's treated as a skills.sh install. Three forms are accepted per entry โ pick whichever is most convenient:
skills:
enabled: true
custom:
# Local paths (absolute / ~ / relative to this yml)
- ./skills/my-skill
- ~/shared/claude-skills/reviewer
# 1. Pasted command โ copy the exact line from https://skills.sh and
# drop it in. rufler parses `npx skills add โฆ` / `skills add โฆ`
# with the real CLI flags.
- npx skills add https://github.com/samber/cc-skills-golang --skill golang-error-handling
- skills add owner/repo -s azure-ai -a claude-code --no-copy
# 2. Dict form โ most explicit, good for code-reviewed configs.
- source: vercel-labs/skills
skill: azure-ai # passed as `-s azure-ai` to the skills CLI
agent: claude-code # target agent (default: claude-code)
copy: true # pass --copy instead of symlink (default: true)
# 3. Bare string with no filesystem match โ falls back to skills.sh
# as if you'd written `npx skills add owner/repo`. Use the dict
# or command form for explicit skills.sh installs to avoid
# accidental ambiguity with local paths.
Legacy: earlier versions of rufler had a separate
skills_sh:section. It's still accepted on load (entries are transparently merged intocustom:), but new configs should usecustom:exclusively.
Recognised flags in the pasted-command form: -s / --skill / --skill=โฆ, -a / --agent / --agent=โฆ, --copy, --no-copy, -y / --yes (tolerated โ rufler always passes it anyway). A leading npx is stripped. -g / --global is rejected at load time because rufler installs skills per-project only.
Under the hood, each entry becomes:
cd <project> && npx -y skills add <source> -a <agent> -y [--copy] [-s <skill>]
Installs land in <project>/.claude/skills/<skill-name>/ โ the same directory ruflo/rufler use for everything else, so Claude Code picks them up on session start alongside packs/extras/custom.
Why --copy is the default
skills.sh supports both symlink and copy installs. rufler defaults to --copy because:
- Symlinks point at a shared cache outside the project and break when the project is cloned, zipped, or committed.
- Copies are self-contained, diffable, and review-friendly.
- You can opt into symlinks per entry with
copy: false.
Preflight check โ fail fast
When any entry in custom: resolves to a skills.sh install, rufler check adds a skills.sh row that probes the CLI:
rufler check
rufler dependency check
โโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Tool โ OK โ Source โ Version / Hint โ
โโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ node โ OK โ - โ v20.x โ
โ claude โ OK โ path โ 1.0.x โ
โ ruflo โ OK โ local โ 3.5.x โ
โ skills.sh โ OK โ npx โ skills CLI reachable โ
โโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
If npx is missing or the skills package won't resolve, this check fails before anything touches your project โ no more finding out mid-run that a skill didn't install.
Post-install verification โ SKILL.md required
Every directory that appears under .claude/skills/ as a result of a skills.sh install is inspected for a SKILL.md file. If one is missing, rufler warns:
skills.sh: installed dirs with NO SKILL.md โ Claude Code may not discover them: some-dir
This catches broken or partial packages in the upstream repo before the swarm launches with a half-installed skill.
Validation
sourceis required and must be a non-empty string.skill,agent,copyare optional. Unknown fields in a dict entry โValueErrorat load time.- Entries are deduped by
(source, skill)pair, preserving order. npx skills addfailures (non-zero exit, timeout 180s,npxmissing) are surfaced as warnings and the run continues โ userufler checkto catch them upfront.
rufler skills โ inspect and reset
# Show what's currently installed in <project>/.claude/skills/
# plus a snapshot of the yml config (enabled / clean / packs / extra / custom)
rufler skills
# List every skill pack / standalone skill available in ruflo's bundled source
rufler skills --available
# Wipe every non-symlinked dir under <project>/.claude/skills/
# (prompts for confirmation; -y / --yes to skip the prompt)
rufler skills --delete
rufler skills --delete -y
Use rufler skills --delete when you've been iterating on your yml and want a clean slate before rufler build / rufler run.
Validation
- Unknown pack name โ
ValueErrorwith the list of known packs. packs,extramust be lists of strings; blank entries are dropped; duplicates are deduped while preserving order.customis a mixed list: strings (local path / skills.sh shorthand / pastednpx skills add โฆcommand) OR dicts ({source, skill?, agent?, copy?}). Unknown dict fields โValueError. Local-path strings are deduped by resolved path; skills.sh entries are deduped by(source, skill). Order is preserved.skills.enabled: falseskips the install step entirely (includingclean).- Omitting the section defaults to
enabled=true,clean=false, empty lists โ i.e. whateverruflo initinstalls, nothing added, nothing removed.
Caveat: skills are session-global
Claude Code discovers skills at session start โ all agents in the hive-mind see the same .claude/skills/ directory. rufler cannot bind a specific skill to a specific agent at the process level; per-agent scoping would have to be enforced in each agent's prompt.
Token usage tracking
rufler parses every NDJSON run log it writes, sums Anthropic API token usage across all claude assistant turns, and persists the totals in two places:
- Per run โ
RunEntrycarriesinput_tokens / output_tokens / cache_read / cache_creation. Refreshed automatically when a foreground run finishes and onrufler stop. - Per project โ
ProjectEntrycarries cumulativetotal_input_tokens / total_output_tokens / total_cache_read / total_cache_creation. These surviverufler rmand--prune, so you keep your project's lifetime token spend even after individual run entries are gone.
Updates are idempotent: re-running token accounting on the same log never double-counts the rollup (only the delta vs the previously-recorded entry total is added).
rufler tokens
# Per-project rollup + grand total across all projects
rufler tokens
# Detailed breakdown of one run
rufler tokens a1b2c3d4
# Force re-parse of run logs from disk (slower, accurate)
rufler tokens --rescan
rufler tokens a1b2c3d4 --rescan
Without an id you get a per-project table and a grand total:
token usage by project
โโโโโโโโโโโโโโณโโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโโณโโโโโโโโโโโโโโโโโณโโโโโโโโโโ
โ PROJECT โ INPUT โ OUTPUT โ CACHE READ โ CACHE CREATION โ TOTAL โ
โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
โ my-app โ 3,210 โ 1,845 โ 142,330 โ 1,200 โ 148,585 โ
โ scraper โ 980 โ 412 โ 18,422 โ 210 โ 20,024 โ
โโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโ
grand total: 168,609 (168.6K) โ in=4.2K out=2.3K cache_read=160.8K cache_creation=1.4K
With an id you get the breakdown for that single run, with raw counts. rufler ps now also shows a TOKENS column for every run, and rufler projects shows project-cumulative totals so you can spot which projects are the most expensive at a glance.
How counts are derived
The parser walks NDJSON log files, looking for src=claude, type=assistant records. Claude stream-json emits multiple assistant events per turn (one per content block), all sharing the same message.id. The parser deduplicates by message.id, keeping only the last event per turn.
Token semantics:
input_tokens/output_tokensโ per-turn deltas โ summed across turnscache_read_input_tokens/cache_creation_input_tokensโ session-cumulative โ max taken
Multi-task runs scan per-task log files automatically (with deduplication if a task points at the same log). rufler tasks uses byte-range slicing (task_start.offset โ task_end.offset) to attribute tokens to individual tasks within a shared sequential log. Missing logs contribute 0.
Background runs and the NDJSON log
rufler always writes an NDJSON run log. In foreground mode it uses logwriter --tee (streams to your terminal AND the log). In -d mode it spawns a detached supervisor:
rufler run -d
# => PID 12345, log: .rufler/run.log
Every line in the log is a JSON object:
{"ts": 1776190000.12, "src": "rufler", "level": "info", "text": "log started: ruflo hive-mind spawn --count=5 ..."}
{"ts": 1776190001.44, "src": "claude", "type": "assistant", "message": {...}}
{"ts": 1776190002.10, "src": "ruflo", "level": "info", "text": "hive-mind: session initialized"}
{"ts": 1776190500.77, "src": "rufler", "level": "ok", "text": "log ended rc=0 elapsed=500.6s"}
src=claudeโ raw Claude Code stream-json (preserved as-is, envelope added)src=rufloโ normalized ruflo/stderr output, ANSI + box-drawing stripped,levelauto-detectedsrc=ruflerโ rufler's own supervisor markers (start, end, elapsed, exit code)
Follow live:
rufler follow # pretty dashboard
tail -f .rufler/run.log # raw NDJSON
jq 'select(.level=="error")' .rufler/run.log # grep errors
Examples
The examples/ directory ships several ready-to-run flows:
| Example | What it demonstrates |
|---|---|
examples/rufler_flow.yml |
Basic Go WebSocket server with a 5-agent team |
examples/rust-cli |
Rust CLI project flow |
examples/python-fastapi |
FastAPI backend flow |
examples/nextjs-saas |
Next.js SaaS project flow |
examples/md-prompts |
Prompts loaded from .md files instead of inline |
examples/multi-task-group |
Explicit multi-task group (backend / frontend / e2e) |
examples/multi-task-decompose |
AI-decomposed multi-task flow |
examples/autonomous-background |
Detached background run with NDJSON log |
Run any of them:
cd examples/multi-task-decompose
rufler run --dry-run # inspect plan first
rufler run -d # launch detached
rufler follow # watch it work
Or point at a flow from anywhere:
rufler run ./examples/rust-cli/rufler_flow.yml
Environment variables
| Variable | Purpose |
|---|---|
RUFLER_RUFLO_BIN |
Absolute path to a ruflo binary. Highest priority in resolution. |
RUFLER_RUFLO_SPEC |
npm spec for the npx -y fallback. Default: ruflo@latest. |
How it works (under the hood)
rufler checkโ resolvesnode,claude,ruflo(local โ PATH โ npm global โnpx) and reports what it found.rufler runโ- Loads and validates
rufler_flow.ymlinto typed dataclasses. - If
task.decompose, callsclaude -pto decomposemaininto N subtasks and writes.tasks/*.md+ companion yml. - Writes
.claude/settings.local.jsonwithpermissions.defaultMode=bypassPermissionsas a safety net. - Runs
ruflo init --force(unless--skip-init),daemon start,memory init,swarm init,hive-mind init. - For each task in the group (or the single mono task), composes an objective prompt โ project header, task body, all agents sorted leadโseniorโjunior, autonomy footer.
- Spawns
ruflo hive-mind spawn --count=N --role=... --claude --dangerously-skip-permissions=true --objective=<composed>. - In foreground: streams to terminal via
python -m rufler.logwriter --tee. In-d: detaches viaPopen(start_new_session=True)with stdio โ/dev/nulland stdout/stderr piped into the supervisor. - Sequential multi-task mode polls the log tail for the
log endedmarker (scanning only bytes added after spawn, to avoid stale markers).
- Loads and validates
rufler stopโ post-task hook + autopilot disable + hive-mind shutdown + daemon stop + session-end.
Development
git clone https://github.com/lib4u/rufler.git
cd rufler
pip install -e .
# Run from source
python -m rufler.cli --help
python -m rufler.cli run --dry-run --skip-checks
Project layout:
rufler/
โโโ rufler/
โ โโโ cli.py # Typer app: run / ps / tasks / logs / follow / stop / rm / tokens
โ โโโ config.py # dataclass schema + YAML loader + objective composer
โ โโโ registry.py # ~/.rufler/registry.json โ RunEntry + TaskEntry with fcntl lock
โ โโโ runner.py # thin wrapper around ruflo subcommands
โ โโโ checks.py # node/claude/ruflo resolution
โ โโโ decomposer.py # AI task decomposition via claude -p
โ โโโ logwriter.py # NDJSON supervisor (foreground tee + detached)
โ โโโ follow.py # live 4-panel TUI dashboard (multi-log tailing)
โ โโโ task_markers.py # task_start/task_end NDJSON markers + boundary scanner
โ โโโ tokens.py # per-turn token parser with message.id dedup
โ โโโ run_steps.py # decompose / plan / finalize helpers
โ โโโ templates.py # `rufler init` sample
โ โโโ tasks/ # task tracking subpackage
โ โ โโโ resolve.py # status derivation, resume logic, per-task tokens
โ โ โโโ display.py # Rich tables, detail cards, log tail rendering
โ โโโ skills/ # skill install/display subpackage
โ โโโ process/ # daemonization, pid management, log paths
โโโ tests/
โ โโโ test_basics.py # 71 tests (registry, tokens, tasks, resume, CLI)
โโโ examples/ # ready-to-run rufler_flow.yml files
License
MIT.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi