An orchestration layer for agentic coding
Project description
bouquet
Warning โ Alpha Software Bouquet is under active development. APIs, config format, and CLI behavior may change without notice. Use at your own risk.
An orchestration layer for multi-agent coding โ multiplexing coding agents across git worktrees with isolated services, all in one tmux session.
cd my-project
bouquet start # TUI launches, create worktrees, agents spin up
Key Features
| Feature | Details | |
|---|---|---|
| ๐ฟ | Git worktree isolation | Each feature branch gets its own worktree, venv, and node_modules โ no cross-contamination |
| ๐ฅ๏ธ | Multi-pane services | Run API servers, workers, frontends alongside the agent with automatic port offsetting per worktree |
| ๐ค | Agent orchestration | TUI in tmux window 0 to create, switch, and manage worktree-backed agent windows |
| ๐ | Task queue | Define a backlog of tasks that get automatically delegated to worktrees โ agents pick up work without manual intervention |
| ๐ง | Rich configuration | Expressive .bouquet.toml with template expressions ({{ 8000 + BOUQUET_WORKTREE_INDEX }}), per-worktree variables, agent profiles, and multi-service layouts |
| โก | Fast bootstrap | CoW-clones .venv/node_modules (APFS), copies .env files, runs uv sync/pnpm install |
| ๐ | Agent-agnostic | Named profiles let you mix Claude, Aider, Codex, or any CLI agent โ even different agents per worktree |
Requirements
- Python 3.14+
- tmux (
brew install tmux) - GitHub CLI (optional,
brew install ghโ enables PR links in TUI and GitHub Issues task backend) - direnv (optional,
brew install direnv) - Language package managers as needed (
uv,pnpm)
Installation
# Run without installing
uvx pybouquet --help
# Or install via pip
pip install pybouquet
# Or from source (development)
git clone https://github.com/promptromp/bouquet.git
cd bouquet
uv sync
Quick Start
1. Initialize
cd /path/to/your/repo
bouquet init
Creates a .bouquet.toml template. Edit it to configure your project.
2. Start a session
bouquet start
This will:
- Create a tmux session (
bouquet-<project-name>) - Launch the orchestrator TUI in window 0
- Adopt any existing git worktrees
- Attach you to the session
You can also pass arguments explicitly:
bouquet start my-project --repo /path/to/repo --config /path/to/.bouquet.toml
3. Use the TUI
| Key | Action |
|---|---|
N |
Create a new worktree (opens branch dialog with optional agent profile selector) |
S / Enter |
Switch to the selected worktree's window |
D |
Delete the selected worktree and its window |
a |
Toggle auto-accept for selected worktree (auto-sends "y" at permission prompts) |
A |
Toggle autopilot mode (auto-schedules tasks by dependency order) |
P |
Send a prompt to selected or all agent terminal(s) via tmux send-keys |
T |
Request a status summary from all agents (captures responses) |
C |
Create a new task in the task queue |
X |
Pick up the highlighted task (creates worktree and sends task to agent) |
M |
Complete the highlighted task (optionally remove the associated worktree) |
Backspace |
Delete the highlighted task from the queue |
R |
Refresh the worktree list and task queue |
Q |
Quit (with confirmation โ kills the session) |
When you create a worktree, bouquet will:
- Create a git worktree with a new branch
- Bootstrap the environment (copy
.envfiles, CoW-clone.venv/node_modules) - Open a new tmux window with service panes (if configured)
- Launch the agent (e.g.
claude) in pane 0
Switch back to the orchestrator: Ctrl-b 0.
4. Stop
bouquet stop
Cleans up all managed worktrees, resets any in-progress tasks back to open, kills the tmux session, and removes state.
Services
Run dev servers alongside the agent in each worktree window. Define them in .bouquet.toml:
[[services]]
name = "api"
command = "uv run uvicorn app.main:app --reload --port {{ 8000 + BOUQUET_WORKTREE_INDEX }}"
[[services]]
name = "frontend"
command = "npm run dev -- --port {{ 3000 + BOUQUET_WORKTREE_INDEX }}"
[tmux]
layout = "main-vertical"
Each worktree gets a unique index (1, 2, 3, ...) so services bind to different ports automatically.
Template Variables
| Variable | Type | Example |
|---|---|---|
BOUQUET_WORKTREE_INDEX |
int | 1, 2, 3 |
BOUQUET_WORKTREE_BRANCH |
str | feature/auth |
BOUQUET_WORKTREE_PATH |
str | /path/to/.bouquet-worktrees/feature-auth |
BOUQUET_PROJECT_NAME |
str | my-project |
Arithmetic supported: {{ 8000 + BOUQUET_WORKTREE_INDEX }} โ 8001.
The same template variables are also available in [bootstrap] setup_commands, post_deps_commands, and teardown_commands โ both as {{ โฆ }} placeholders and as plain shell env vars ($BOUQUET_WORKTREE_INDEX, etc.) โ so setup/teardown scripts can provision and reclaim per-worktree resources (databases, queues, โฆ) without external coordination.
No services defined = single pane with just the agent (backward compatible).
Bootstrap hooks: setup_commands vs post_deps_commands vs teardown_commands
The three [bootstrap] hook lists run at different points in the worktree lifecycle:
| Hook | When | On failure | Typical use |
|---|---|---|---|
setup_commands |
Before python_deps_command / node_deps_command |
Loud โ raises SetupCommandsError, worktree โ ERROR |
private-registry auth (CodeArtifact tokens), provisioning per-worktree DBs / queues |
post_deps_commands |
After deps install, before direnv_allow (so .venv / node_modules exist) |
Loud โ same as setup_commands | uv run migrate upgrade head, asset compilation, anything that needs the project's tooling |
teardown_commands |
Before tmux window kill + git worktree removal (so the on-disk checkout is still reachable) | Best-effort โ failures logged, cleanup proceeds | reclaiming external per-worktree resources at remove time (drop DBs, delete queues) |
setup_commands and post_deps_commands also have env-capture: any export FOO=bar lines in the user's commands are captured and propagated into both subsequent install commands and the tmux session, so service panes inherit them.
See docs/configuration.md for the full reference.
Agent Profiles
Named agent configurations so you can switch between Claude, Aider, Codex, etc. per worktree:
[agent]
command = "claude"
default_profile = "claude"
[[agent.profiles]]
name = "claude"
command = "claude"
[[agent.profiles]]
name = "aider"
command = "aider"
args = ["--model", "claude-sonnet-4-20250514"]
When profiles are defined, the TUI's new-worktree dialog shows a profile selector. If no profiles are defined, the top-level command/args are used (backward compatible).
Activity Detection
Bouquet polls each agent's tmux pane every 2 seconds to infer real-time status:
| Status | Meaning |
|---|---|
| โ running (green) | Agent output is actively changing |
| โ waiting (yellow) | Agent output stopped and a permission prompt was detected |
| โ idle (dim) | Agent output hasn't changed for several polls |
This replaces the static "active" status with live feedback. The TUI table updates automatically.
Task Queue
Define a backlog of tasks that agents pick up automatically. Two backends are supported:
Local (SQLite) โ default
Tasks are stored in ~/.local/state/bouquet/<project>.tasks.db. No external dependencies.
[task_queue]
backend = "local"
auto_branch_prefix = "task/"
GitHub Issues
Uses your repo's GitHub Issues as the task source. Requires gh CLI authenticated.
[task_queue]
backend = "github"
label_filter = "bouquet" # only issues with this label appear as tasks
auto_branch_prefix = "task/"
Status mapping: OPEN = open issue, IN_PROGRESS = open issue + in-progress label, DONE = closed issue.
Task workflow
- Create (
c) โ opens a dialog to create a task with an optional parent dependency - Pick up (
x) โ creates a worktree from the task, marks it in-progress, and sends the task description to the agent - Complete (
m) โ marks the task as done, optionally removes the associated worktree - Reconciliation โ on startup, tasks stuck as in-progress (from a crash or quit) are automatically reset to open if their worktree no longer exists
Task Dependencies
Tasks can declare a parent dependency, forming a DAG. A task with an unsatisfied dependency shows as blocked in the queue and cannot be picked up until its parent is done. Cycles are rejected at creation time.
Autopilot
Press A to toggle autopilot mode. When active, bouquet automatically:
- Picks up tasks whose dependencies are satisfied (or have none)
- Runs up to
max_autopilot_concurrencytasks in parallel (default 3) - Enables auto-accept on all autopilot-created worktrees
- Auto-completes tasks when their agent goes idle (~10 seconds)
- Cascades: completing a parent unblocks its children for the next scheduling cycle
[task_queue]
max_autopilot_concurrency = 3 # max parallel worktrees
autopilot_auto_complete = true # auto-complete IDLE tasks
Pane Layout
With layout = "main-vertical" and two services:
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ โ api โ
โ agent (claude) โโโโโโโโโโโโโโค
โ โ frontend โ
โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโ
Logs & Troubleshooting
Bouquet writes a rotating log file to ~/.local/state/bouquet/<project>.log (5 MB ร 3 backups). bouquet start prints the path to stderr on launch.
When a worktree creation fails โ most often because a setup_commands or post_deps_commands step exited non-zero โ the TUI shows a terse toast (Error creating worktree: โฆ) but the captured stdout and stderr from your bash commands land in the log file, along with the phase name (setup_commands vs post_deps_commands), cwd, and exit code. Tail it to debug:
tail -f ~/.local/state/bouquet/<project>.log
The SetupCommandsError raised on failure includes the log file path in its message, so the toast will point you there directly.
teardown_commands failures are best-effort and don't block worktree removal โ they log a WARNING-level line in the same log file, so check there if a worktree was removed but external resources weren't reclaimed.
Pass --log-level=DEBUG to bouquet start for more verbose output (e.g. each rendered setup_commands / post_deps_commands / teardown_commands line):
bouquet start --log-level=DEBUG
Architecture
The Conceptual Stack
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Orchestration Layer โ โ TUI, agent coordination, task queue
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Session / Mux Layer โ โ tmux sessions, windows, panes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Isolation Layer โ โ git worktrees + env isolation
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Environment Layer โ โ venv/node_modules/env vars
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Related Projects
Bouquet draws inspiration from and complements several tools in the multi-agent coding space:
-
claude-squad โ A Go-based TUI for managing multiple Claude Code instances in parallel. Claude-squad focuses on running agents side-by-side with a clean terminal UI. Bouquet goes further with declarative multi-service layouts (API servers, frontends, workers per worktree), a rich template-based configuration language with per-worktree variable expansion, and a task queue for automatic work delegation across agents.
-
ruflo โ A Rust-based agentic workflow orchestrator with a focus on DAG-based task execution and CI/CD integration. Ruflo takes a pipeline-oriented approach to agent coordination, while bouquet is designed around the developer's local workflow โ git worktrees, tmux sessions, and interactive TUI management with live activity detection.
-
Claude Code Agent Teams โ Anthropic's experimental built-in feature for coordinating multiple Claude Code agents. Agent Teams operates within the Claude Code runtime itself. Bouquet is agent-agnostic (works with Claude, Aider, Codex, or any CLI tool), provides full control over environment isolation, service orchestration, and configuration through
.bouquet.toml.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pybouquet-0.6.1.tar.gz.
File metadata
- Download URL: pybouquet-0.6.1.tar.gz
- Upload date:
- Size: 579.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f16c77fda742e1993af0732acc3f594677ab6c523847558497f3e8379d09b660
|
|
| MD5 |
1aada7bf424d41509f133f6ac010ff66
|
|
| BLAKE2b-256 |
4b5e5153538fa5c6068f19ee1b6e54b80849203dca61407604ceb2fa28b069b5
|
Provenance
The following attestation bundles were made for pybouquet-0.6.1.tar.gz:
Publisher:
publish-to-pypi.yml on promptromp/bouquet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pybouquet-0.6.1.tar.gz -
Subject digest:
f16c77fda742e1993af0732acc3f594677ab6c523847558497f3e8379d09b660 - Sigstore transparency entry: 1415867958
- Sigstore integration time:
-
Permalink:
promptromp/bouquet@c6b035e0cabf3de9e7998cc096512399cc1843cf -
Branch / Tag:
refs/tags/0.6.1 - Owner: https://github.com/promptromp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@c6b035e0cabf3de9e7998cc096512399cc1843cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file pybouquet-0.6.1-py3-none-any.whl.
File metadata
- Download URL: pybouquet-0.6.1-py3-none-any.whl
- Upload date:
- Size: 58.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be296cc701ccd7fb6f802305f4c3a7ee817e8a5668cf113a1272658005b6ea40
|
|
| MD5 |
fe5fcfef47e0f86edb77cf4fe5f2df0f
|
|
| BLAKE2b-256 |
0e97bdede4e386f33e4dd678b23c7824331b41aafdbeba73d97d69958293dc82
|
Provenance
The following attestation bundles were made for pybouquet-0.6.1-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on promptromp/bouquet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pybouquet-0.6.1-py3-none-any.whl -
Subject digest:
be296cc701ccd7fb6f802305f4c3a7ee817e8a5668cf113a1272658005b6ea40 - Sigstore transparency entry: 1415868017
- Sigstore integration time:
-
Permalink:
promptromp/bouquet@c6b035e0cabf3de9e7998cc096512399cc1843cf -
Branch / Tag:
refs/tags/0.6.1 - Owner: https://github.com/promptromp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@c6b035e0cabf3de9e7998cc096512399cc1843cf -
Trigger Event:
push
-
Statement type: