Run N AI agents that collaborate with you on a single text file, safely. Powered by cotype.
Project description
chorale
Run N AI agents that brainstorm with you on a single text file, safely.
You write under ## user. Each AI agent owns its own ## agent:<role> section and edits it in place to reply. The file is the conversation — no chat windows, no scrolling transcripts, no lost edits. Concurrent saves are reconciled by cotype's 3-way merge; the harness splices each agent's reply into ONLY its own section's bytes, so two agents editing two different sections cannot conflict by construction.
pip install chorale
chorale brainstorm.md cook logistics ux-designer note-taker
…and you have four Claude personas waiting on your ## user prompt. Edit brainstorm.md in any editor (with cotype-mode for live updates in Emacs); agents see your saves on their next poll and respond.
Why this exists
Long sessions with AI agents drift into chat transcripts that scroll away from the work you actually want at the end. chorale flips it: the document accumulates in place, every actor has a labelled section, and disagreements between actors surface as inline diff3 markers rather than lost work.
The tool was extracted from cotype's examples/headless-agents.sh — that bash script is still the readable "what's the idea, on one screen" demo; this Python rewrite is the production-friendly version: tested, configurable, extensible.
Install
pip install chorale
Requires Python ≥ 3.11, cotype (auto-installed), and the claude CLI on PATH.
Usage
chorale FILE ROLE [ROLE ...] [OPTIONS]
# four agents on a fresh brainstorm
chorale brainstorm.md cook logistics ux-designer note-taker
# tighter polling, faster turns
chorale notes.md reviewer linter --interval 0.5 --stagger 2 --model claude-haiku-4-5-20251001
# custom prompt template
chorale notes.md author editor --prompt-file my-prompt.txt
chorale --help prints the full surface, the protocol, and a copy-paste example.
Custom prompts
Pass --prompt-file PATH to override the built-in brainstorm prompt. The file is treated as a str.format template with two placeholders the harness fills in per turn:
{role}— the agent's role name (e.g.cook).{file_content}— the current state of the shared file.
Anything an agent emits outside its own ## agent:{role} section is discarded by the splicer, so prompts only need to nudge the agent toward filling its own section sensibly.
Stopping
Ctrl-C on the running process stops all agents cleanly. While running, you can edit the shared file in any editor; agents will see your edits on their next poll. If a conflict happens (you and an agent both edit the same section), chorale idles all agents and waits for you to resolve it (cotype resolve FILE after editing the markers).
How it works
┌─────────────────────────────────────────────────────────────┐
│ user (any editor) ─┐ │
│ │ writes under ## user │
│ ▼ │
│ ┌──── shared.md (cotype-managed) ────┐ │
│ agent_A ───┤ ├─── disk │
│ agent_B ───┤ one section per actor │ │
│ agent_C ───┘ diff3 reconciles concurrent saves│ │
│ │ │ │
│ └────── chorale runtime ─────────┘ │
└─────────────────────────────────────────────────────────────┘
Each agent thread runs an independent loop:
cotype status— idle if a conflict is pending (only the user can resolve).cotype open— capture a fresh base; skip if it hasn't changed since our last save.claude --print -p PROMPT— generate a candidate reply.- Splice: parse the agent's output as Markdown sections, take only the body of
## agent:<role>, splice it into the bytes frombase_path. By construction, no other section's bytes can change. cotype save— submit the spliced bytes; cotype decides direct / merged / noop / conflict.
The structural splice is the key idea: the agent can produce arbitrary content, but only its own section's bytes ever reach the file. Two agents editing two different sections produce edits in disjoint byte ranges, no matter how adjacent the section headers are.
Tests
pip install pytest
pytest -q
Tests cover the splicer's contract (round-trip, role isolation, codefence stripping, no-change short-circuit) and the template generator. The runtime (subprocess wrappers, threading) is intentionally untested — it's almost entirely IO and best validated by running the demo.
Compared to
- cotype — the byte-level safe-save CLI underneath.
choraleis the agent harness;cotypeis the merge engine. headless-agents.sh— the original bash version, still in cotype's repo as a one-screen reference.choraleis the same idea with structure (config, tests, prompt extension point).
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
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 chorale-0.1.0.tar.gz.
File metadata
- Download URL: chorale-0.1.0.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca2965e98e2bfeac8f60eb4cbf95fc2729a52e148f61f8b9dcec2e2e48460478
|
|
| MD5 |
cc4c9e4dcb2e9bad75c7cee2bacca48e
|
|
| BLAKE2b-256 |
bccebbfbbe2376dbdee1ad7fcf8f3cad86234f9a72832584a6f22f70ea6d6da2
|
Provenance
The following attestation bundles were made for chorale-0.1.0.tar.gz:
Publisher:
publish.yml on yurug/chorale
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chorale-0.1.0.tar.gz -
Subject digest:
ca2965e98e2bfeac8f60eb4cbf95fc2729a52e148f61f8b9dcec2e2e48460478 - Sigstore transparency entry: 1440004815
- Sigstore integration time:
-
Permalink:
yurug/chorale@5bbf58461ea0fcfba371c3c60a30eea93f9a2aac -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/yurug
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5bbf58461ea0fcfba371c3c60a30eea93f9a2aac -
Trigger Event:
push
-
Statement type:
File details
Details for the file chorale-0.1.0-py3-none-any.whl.
File metadata
- Download URL: chorale-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.4 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 |
7b7a462b6a717a6fee562051aaa38abb90a2356a53575aced063fa38a3ea0de1
|
|
| MD5 |
22c9cd580395bb521234e3e1c6d942c1
|
|
| BLAKE2b-256 |
4617418bb6c9a8a14d6cb8fcdcdb10bc6a74a50f54a3fec37ee4795b5aaeb665
|
Provenance
The following attestation bundles were made for chorale-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on yurug/chorale
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chorale-0.1.0-py3-none-any.whl -
Subject digest:
7b7a462b6a717a6fee562051aaa38abb90a2356a53575aced063fa38a3ea0de1 - Sigstore transparency entry: 1440004816
- Sigstore integration time:
-
Permalink:
yurug/chorale@5bbf58461ea0fcfba371c3c60a30eea93f9a2aac -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/yurug
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5bbf58461ea0fcfba371c3c60a30eea93f9a2aac -
Trigger Event:
push
-
Statement type: