The Chorus Plugin packages the Chorus Skill with Hooks for automatic session lifecycle management. Hooks guarantee execution at specific Claude Code lifecycle events, removing the dependency on Claude "remembering" to manage sessions.
Hooks (automatic, no intelligence needed):
SessionStart → chorus checkin + session discovery (inject context)
SubagentStart → create Chorus session + write session file (SYNC)
TeammateIdle → session heartbeat
SubagentStop → auto-checkout all tasks + close Chorus session
TaskCompleted → checkout via metadata bridge
Skill + MCP (requires LLM judgment):
Task claiming, checkin_task, status updates, report_work, proposals
Sub-agents discover their Chorus session UUID without Team Lead intervention:
Team Lead spawns sub-agent "frontend-worker"
│
├─ [Hook: SubagentStart] fires SYNCHRONOUSLY before sub-agent runs
│ ├─ Creates Chorus session via MCP
│ ├─ Writes .chorus/sessions/frontend-worker.json
│ └─ Output to Team Lead: "Session UUID: xxx"
│
└─ Sub-agent starts
├─ [Hook: SessionStart] fires (if applicable)
│ └─ Scans .chorus/sessions/ → outputs "Your session: xxx"
│
└─ OR: Sub-agent reads .chorus/sessions/frontend-worker.json
(instructed by skill docs or Team Lead prompt)
The sub-agent gets its session UUID via two redundant paths:
- SessionStart hook scans session files and outputs them as context
- File read — the sub-agent reads
.chorus/sessions/<my-name>.jsondirectly
When a sub-agent exits, the SubagentStop hook automatically:
- Queries the Chorus session for active task checkins
- Checks out from every checked-in task
- Closes the Chorus session
- Removes the session file and state entries
This means sub-agents never leave behind dangling checkins or open sessions.
Set the following environment variables (e.g., in .env or your shell profile):
export CHORUS_URL="https://chorus.example.com" # or http://localhost:8637
export CHORUS_API_KEY="cho_your_api_key_here"See the install instructions in public/chorus-plugin/skills/chorus/SKILL.md for skill details. The skill is bundled with the plugin and delivered automatically with plugin updates.
For local development within this repo, the skill is already symlinked:
.claude/skills/chorus → ../../public/chorus-plugin/skills/chorus
For external users who downloaded via the install script:
CHORUS_URL=<url> CHORUS_API_KEY=cho_xxx claude --plugin-dir .chorus-pluginFor local development within this repo:
CHORUS_URL=http://localhost:8637 CHORUS_API_KEY=cho_xxx claude --plugin-dir public/chorus-pluginThe plugin includes a .mcp.json template that configures the Chorus MCP server. It uses $CHORUS_URL and $CHORUS_API_KEY from the environment.
public/chorus-plugin/ # Plugin root
├── .claude-plugin/plugin.json
├── hooks/hooks.json
├── bin/ # Hook scripts
│ ├── chorus-api.sh # Shared API + state + session file helpers
│ ├── on-session-start.sh # SessionStart: checkin + session discovery
│ ├── on-subagent-start.sh # SubagentStart: create session + write file (SYNC)
│ ├── on-subagent-stop.sh # SubagentStop: checkout tasks + close + cleanup
│ ├── on-teammate-idle.sh # TeammateIdle: heartbeat
│ └── on-task-completed.sh # TaskCompleted: checkout via metadata bridge
├── skills/chorus/ # Skill files
│ ├── SKILL.md
│ ├── package.json
│ └── references/ # Role-specific workflow docs
└── .mcp.json
.claude/skills/
└── chorus → ../../public/chorus-plugin/skills/chorus (symlink, local dev)
Runtime state (gitignored):
.chorus/
├── state.json # Hook state: agent→session mappings
└── sessions/ # Per-agent session files (Plan A)
├── frontend-worker.json
└── backend-worker.json
Trigger: Claude Code session starts or resumes.
Behavior:
- Checks if
CHORUS_URLandCHORUS_API_KEYare set - Calls
chorus_checkinvia MCP to verify connectivity and inject agent context - Outputs hook status and usage hints
- If resuming with existing state, sends a heartbeat
- Session discovery (Plan A): Scans
.chorus/sessions/for pre-created session files and outputs them. If the current agent is a sub-agent, it can identify its own session by name.
Trigger: A sub-agent (teammate) is spawned via the Task tool.
SYNCHRONOUS — completes before the sub-agent starts executing.
Behavior:
- Reads
agent_id,agent_name, andagent_typefrom the event - Skips non-worker types (Explore, Plan, haiku, etc.)
- Creates a Chorus session via MCP (
chorus_create_session) - Stores mappings in
state.json(by agent_id and agent_name) - Writes session file to
.chorus/sessions/<agent_name>.jsonwith full session info - Outputs session UUID and file path to Team Lead's context
Trigger: A sub-agent exits.
Behavior:
- Reads
agent_idandagent_namefrom the event - Looks up the session UUID from state
- Auto-checkout (Plan D): Queries
chorus_get_sessionfor active task checkins, then callschorus_session_checkout_taskfor each - Closes the Chorus session via MCP
- Cleans up state entries and session file
Trigger: A teammate goes idle (between turns).
Behavior:
- Reads
agent_idorteammate_namefrom the event - Looks up the session UUID from state (by agent_id or name)
- Sends a heartbeat via
chorus_session_heartbeat
This prevents Chorus sessions from being marked inactive during long-running agent team operations.
Trigger: A Claude Code task is marked completed.
Behavior:
- Reads task info from the event
- Searches for
chorus:task:<uuid>pattern in the task description/subject - If found, checks out the corresponding session from that Chorus task
The plugin stores session mapping state in $CLAUDE_PROJECT_DIR/.chorus/state.json. This file is automatically created and should be gitignored.
Format:
{
"main_session_uuid": "abc-123-...",
"session_<agent_id>": "<chorus-session-uuid>",
"session_<agent_name>": "<chorus-session-uuid>",
"agent_for_session_<session-uuid>": "<agent-id>"
}Per-agent session files live in $CLAUDE_PROJECT_DIR/.chorus/sessions/<agent_name>.json:
{
"sessionUuid": "abc-123-def-456",
"agentId": "agent_abc123",
"agentName": "frontend-worker",
"agentType": "general-purpose",
"chorusUrl": "http://localhost:8637",
"createdAt": "2026-02-16T01:00:00Z"
}These files are:
- Created by SubagentStart hook (synchronously, before sub-agent runs)
- Read by SessionStart hook (output as context) and by sub-agents directly
- Deleted by SubagentStop hook (cleanup on exit)
To link a Claude Code task with a Chorus task, include the pattern chorus:task:<uuid> in the Claude Code task description. For example:
TaskCreate:
subject: "Implement login API"
description: "Build the /api/login endpoint. chorus:task:a1b2c3d4-e5f6-7890-abcd-ef1234567890"
When the Claude Code task is completed, the TaskCompleted hook will automatically check out from the linked Chorus task.
| Command | Description |
|---|---|
checkin |
Check connectivity with Chorus backend |
mcp-tool <name> [args_json] |
Call any MCP tool via JSON-RPC |
state-get <key> |
Read a value from state.json |
state-set <key> <value> |
Write a value to state.json |
state-delete <key> |
Delete a key from state.json |
session-read <name> |
Read a session file for a named agent |
session-list |
List all pre-created session files as JSON |
| Operation | Who | How |
|---|---|---|
| Create Chorus session | Hook (SubagentStart) | Automatic, sync |
| Session heartbeat | Hook (TeammateIdle) | Automatic, async |
| Close Chorus session | Hook (SubagentStop) | Automatic, async |
| Checkout all tasks on exit | Hook (SubagentStop) | Automatic, async |
| Checkout on CC task done | Hook (TaskCompleted) | Automatic, needs chorus:task:<uuid> |
| Discover session UUID | Hook (SessionStart) + file | Automatic via session files |
| Checkin to a task | LLM | chorus_session_checkin_task |
| Move task status | LLM | chorus_update_task |
| Report work | LLM | chorus_report_work (needs LLM to summarize) |
| Submit for verify | LLM | chorus_submit_for_verify (needs judgment) |
| Claim/release tasks | LLM | chorus_claim_task / chorus_release_task |
curl— for REST API callsjq— for JSON parsing (recommended; basic fallback available without it)CHORUS_URLandCHORUS_API_KEYenvironment variables
- Verify the plugin is loaded:
claude --plugin-dir public/chorus-plugin(or.chorus-pluginfor external installs) - Check that
hooks.jsonis valid JSON - Ensure hook scripts are executable (
chmod +x bin/*.sh)
Set the environment variables before starting Claude Code:
export CHORUS_URL="http://localhost:8637"
export CHORUS_API_KEY="cho_your_key"The hook skips read-only agent types (Explore, Plan, haiku). Only worker agents (general-purpose, Bash) get Chorus sessions.
- Check that
.chorus/sessions/<agent-name>.jsonexists - Verify SubagentStart hook ran (check Team Lead's context for "Chorus session auto-created" message)
- Ensure the sub-agent name matches the filename
Check that $CLAUDE_PROJECT_DIR is set and writable. The state file is at $CLAUDE_PROJECT_DIR/.chorus/state.json.
If heartbeats fail silently, check:
- Network connectivity to Chorus
- API key validity (keys expire if rotated)
- Session may have been manually closed