claude-pool
Slot pool orchestration library for Claude CLI
Overview
claude-pool manages N Claude CLI slots behind a unified interface. A coordinator (typically an interactive Claude session) submits work, and the pool routes tasks by availability, tracks budgets, and handles slot lifecycle and session management.
Perfect for:
- Scaling Claude work across multiple slots
- Budget-aware task distribution
- Parallel and sequential task orchestration
- Slot isolation with optional Git worktrees
Architecture
Coordinator (your app or interactive session)
│
├─ pool.run("task") → synchronous
├─ pool.submit("task") → async, returns task ID
├─ pool.fan_out([tasks]) → parallel execution
└─ execute_chain(steps) → sequential pipeline
│
├── Pool (task queue, context, budget)
│
├── Slot-0 (Claude instance)
├── Slot-1 (Claude instance)
└── Slot-N (Claude instance)
Installation
Requires: claude-wrapper (included as dependency)
Quick Start
use Pool;
use Claude;
async
Core Concepts
Synchronous vs Asynchronous Tasks
Synchronous (blocking):
let result = pool.run.await?;
println!;
Asynchronous (non-blocking):
let task_id = pool.submit.await?;
// Do other work...
let result = pool.result.await??;
Budget Control
Track and limit spending:
let pool = builder
.slots
.config
.build
.await?;
Budget is tracked atomically per task. When the pool reaches its cap, subsequent tasks are rejected.
Slot Identity
Each slot has metadata for coordination:
pool.configure_slot
.await?;
pool.configure_slot
.await?;
Access slot info:
let status = pool.status.await?;
for slot in status.slots
Shared Context
Inject key-value pairs into all slot system prompts:
pool.context_set.await?;
pool.context_set.await?;
pool.context_set.await?;
// All slots now see these in their system prompts
Access context:
let value = pool.context_get.await??;
pool.context_delete.await?;
let all = pool.context_list.await?;
Pool Builder Configuration
use ;
let pool = builder
.slots
.config
.build
.await?;
Available config options:
with_model(name)- Default model for all slotswith_effort(level)- Effort: Min, Low, Medium, High, Maxwith_budget_usd(amount)- Total pool budgetwith_permission_mode(mode)- Permission defaultswith_system_prompt(text)- Base system promptwith_worktree(true)- Enable Git worktree per slot
Execution Patterns
Single Task (Synchronous)
let result = pool.run.await?;
println!;
println!;
Result includes:
output- Claude's responsespend_usd- Cost of this tasktokens_used- Input and output tokens
Async Task Submission
// Submit and get task ID immediately
let task_id = pool.submit.await?;
// Do other work...
// Poll for result later
let result = pool.result.await??;
Parallel Fan-Out
Execute multiple prompts in parallel, all at once:
let prompts = vec!;
let results = pool.fan_out.await?;
for in results.iter.enumerate
All tasks run concurrently. Returns when all complete (or timeout).
Sequential Chains with Failure Policies
Execute steps in order, with control over failures:
use ;
let steps = vec!;
let task_id = pool.submit_chain.await?;
let result = pool.result.await?;
Failure policies:
- retries - Number of retries before failing (default: 0)
- recovery_prompt - Optional prompt to run on failure instead of aborting
Access chain progress:
let progress = pool.chain_result.await?;
for step in progress.steps
Skills System
Skills are reusable prompt templates that define how to approach a task. The pool discovers and references them by name in chains or direct calls.
SKILL.md Format
Skills follow the Agent Skills standard. Each skill lives in its own directory with a SKILL.md file:
.claude-pool/skills/
code_review/
SKILL.md # Required: frontmatter + prompt
scripts/ # Optional: bundled scripts
lint.sh
templates/ # Optional: templates
report.md
examples/ # Optional: examples
input.py
The SKILL.md file contains YAML frontmatter followed by the prompt body:
---
name: code_review
description: Review code for bugs and style issues
argument-hint: "<path> [criteria]"
allowed-tools: Read, Grep, Glob, Bash
metadata:
arguments:
- name: path
description: File path to review
required: true
- name: criteria
description: What to focus on (bugs, style, performance)
required: false
---
Review the code at {path} for the following criteria:
Report issues found with severity and suggestions.
Standard fields (argument-hint, allowed-tools) live at the top level.
Pool-specific extensions (scope, arguments, config) live under metadata.
Arguments are available as {arg_name} or $ARGUMENTS / $0 / $1 in the prompt.
Skill Resolution
Skills are discovered in priority order (first match wins):
- Runtime skills - Added via code (ephemeral, lost on restart)
- Project skills - Loaded from
.claude-pool/skills/(checked into repo) - Global skills - Loaded from
~/.claude-pool/skills/(user-wide) - Builtin skills - Shipped with the pool binary
CLAUDE_SKILL_DIR Substitution
Skills can reference supporting files using the ${CLAUDE_SKILL_DIR} variable:
Run linting:
bash ${CLAUDE_SKILL_DIR}/scripts/lint.sh .
Generate report from template:
python -c "..." < ${CLAUDE_SKILL_DIR}/templates/report.md
The variable resolves to the skill's directory path at render time. Available for project and global skills only (not builtins or runtime skills).
Using Skills in Chains
Reference skills in chain steps:
use ;
let steps = vec!;
Programmatic Registration
Register skills at runtime:
use ;
let mut registry = new;
registry.register;
Worktree Isolation
Enable optional Git worktree per slot for safe, isolated execution:
let pool = builder
.slots
.config
.build
.await?;
Each slot gets an isolated worktree:
- Independent filesystem
- Safe for parallel edits
- Cleanup on drain
Benefits:
- Parallel file edits without conflicts
- Isolated git state
- Safe cleanup
Quality Gates
The pool supports a human-in-the-loop review workflow. Tasks submitted with
submit_with_review transition to PendingReview on completion instead of
Completed, allowing a coordinator to inspect results before accepting them.
// Submit a task that requires approval before it's considered done.
let task_id = pool.submit_with_review.await?;
// ... task runs, completes, enters PendingReview ...
// Inspect the result.
let result = pool.result.await?;
// Approve: transitions PendingReview -> Completed.
pool.approve_result.await?;
// Or reject with feedback: re-queues the task with feedback appended
// to the original prompt. Fails after max_rejections.
pool.reject_result.await?;
Via MCP tools
The same workflow is available through the MCP server:
pool_submit_with_review-- submit a task requiring approvalpool_approve_result-- accept the resultpool_reject_result-- reject with feedback, task re-runs
Task states
Pending -> Running -> PendingReview -> Completed (approved)
|
+-> Running (rejected, re-queued with feedback)
|
+-> Failed (max rejections reached)
Rejection appends feedback to the original prompt so the slot sees what went wrong and can address it on the next attempt.
Slot Lifecycle
Spawning
Slots are created during build() and remain alive until drain().
Session Resumption
Slots automatically resume sessions if available, reducing startup cost.
Graceful Shutdown
let summary = pool.drain.await?;
println!;
println!;
println!;
All pending tasks are cancelled. Active tasks complete gracefully.
Status & Monitoring
Get current pool state:
let status = pool.status.await?;
println!;
println!;
println!;
println!;
Status includes:
- Slot list with ID, status, and active task count
- Active and pending task counts
- Total spend and budget
- Budget remaining
Error Handling
All operations return Result<T>:
use Error;
match pool.run.await
Common errors:
TaskFailed- Task execution failedBudgetExceeded- Pool exceeded spending capNoSlotsAvailable- All slots busy/offlineTaskNotFound- Invalid task ID
Feature Flags
Currently no optional features. The crate includes full functionality by default.
Future features may include:
redis-store- Redis backend for distributed pool stateprometheus- Metrics export for monitoring
API Documentation
For detailed API documentation, see docs.rs/claude-pool.
Testing
Requires the claude CLI binary:
License
MIT OR Apache-2.0