TeamCreate, TeamDelete, and TeamList were removed, a team forms automatically when the first teammate is spawned (gated by CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS), and team_name on the Agent tool is accepted but ignored. See the 2026-06-22 update below for the current model, and treat the spawn recipes that follow as historical.
Update (2026-05-09, verified on Claude Code 2.1.138). Since CC 2.1.133 the binary ships a native
worktreeBaseRefsetting ('fresh'or'head') in/configandsettings.json. If all you need is for worktrees to be based on the currentHEAD, setworktreeBaseRef: "head"and you can skip theWorktreeCreatehook described in Decision 2 below. The hook is still useful for custom worktree paths under.claude/worktrees/, theworktree/<slug>branch name with--no-track, andgit worktree lockkeyed onagent_idandpid. When the hook is present, the binary delegates entirely to it and ignores the native setting, so the warning in the pre-flight check (“WorktreeCreate hook missing”) is now strictly accurate only when both the hook andworktreeBaseRefare absent.
Update (2026-05-17, verified on Claude Code 2.1.143). Two further changes affect this post.
First,
worktree.bgIsolation: "none"(new in 2.1.143) lets a background session edit the working copy directly withoutEnterWorktree. This is for the lead session itself (for example when you runclaude --bg), not for subagents spawned withisolation: "worktree", so the skill’s default of per-teammate worktree isolation is unchanged. To opt in for a specific repo, add"worktree": { "bgIsolation": "none" }to that repo’s.claude/settings.json. The related bug from 2.1.142 (background sessions not recognizing pre-existing git worktrees, which blockedEditwhileEnterWorktreerefused to create a duplicate) is also fixed.Second, the
claude agentsdashboard (added in 2.1.139, polished through 2.1.143) lists every session (running, blocked on you, done) in one view, including team members spawned bynf-agents. From 2.1.142, the view accepts--add-dir,--settings,--mcp-config,--permission-mode,--model, and--effortflags to configure dispatched sessions. The tmux or iTerm2 pane backend is unchanged: each member is still a real shell session, the dashboard just centralizes status, model selection, and permission mode in one place. From 2.1.143, dispatched sessions also honorpermissions.defaultModefromsettings.jsoninstead of being overridden to auto mode, andclaude --bg --dangerously-skip-permissionspersists across retire and wake.Two smaller fixes worth knowing:
worktree cleanupno longer falls back torm -rfwhengit worktree removefails (2.1.143), so gitignored or in-progress files inside a worktree are preserved, and empty idle background sessions auto-retire after five minutes (2.1.141) so the dashboard does not accumulate ghost rows.
Update (2026-05-19, verified on Claude Code 2.1.144). Three changes touch this skill’s surface area.
First,
/resumenow lists background sessions alongside foreground ones with abgtag, and session notifications include runtime in the form3h 2m 5s. Fornf-agentsteams where teammates often run in background, this means you can locate a specific teammate by tag rather than scanningroster.jsonorstate.jsonby hand. The Stop hook and Telegram bridge notifications inherit the new runtime field automatically.Second,
/modelno longer changes the default model for future sessions by default. It now affects only the current session; pressdafter picking a model to persist it as the default. This matters fornf-agentsworkflows that spawn many short-lived teammates: setting/modelin a parent session no longer accidentally rewrites the default that subsequent teammates inherit.Third, two reliability fixes worth knowing: dispatched background sessions now keep the model they were spawned with (previously a teammate could pick up a different session’s model due to a race), and a macOS-specific crash in background sessions running inside Full Disk Access-protected folders (Documents, Desktop, iCloud Drive) is fixed. The API startup hang (75s when
api.anthropic.comis unreachable) is also cut to 15s, which makes spawning a team of five teammates noticeably less painful on flaky networks.
Update (2026-06-22, verified on Claude Code 2.1.185). This is the big one: the team primitives this skill was built on changed shape in v2.1.178, so parts of the recipe below are now historical. The official write-up is at
code.claude.com/docs/en/agent-teams(“as of v2.1.178”), and the facts here are cross-checked against the 2.1.185 binary.
TeamCreateandTeamDeleteno longer exist. Grepping the 2.1.185 binary returns zero hits for either tool name. WithCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1set, a team now forms automatically when the first teammate is spawned, and the team’s shared directories are cleaned up automatically when the session ends. There is no setup call and no teardown call. Theteam_nameinput on theAgenttool is still accepted but ignored; the team name is derived from the session (session-plus the first eight characters of the session id). Theteam_namefield in theTaskCreated,TaskCompleted, andTeammateIdlehook payloads is deprecated and carries that session-derived name. Net effect on this skill: thestandbyandtasksspawn recipe below (callTeamCreate, thenAgent({team_name, ...})) is the pre-2.1.178 shape. The modern equivalent is to spawn teammates directly once the env var is on, and to let session exit do the cleanup thatteardown’sTeamDeleteused to do.The feature is gated by
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. Without it, no team is set up at session start, no team directories are written, and the lead will not spawn or propose teammates. Set it insettings.jsonunderenv, or in your shell.In-process is now the default display mode (v2.1.179). Before v2.1.179 the default was
auto, so an upgraded session that used to open split panes now stays in one terminal untilteammateModeis set explicitly. The binary accepts three values:in-process,auto,tmux.autoopens split panes only when you are already inside tmux or iTerm2 and falls back to in-process otherwise;tmuxforces split-pane mode and auto-detects tmux versus iTerm2. Per session:claude --teammate-mode auto. This softens Decision 1 below: the mode setting now governs whether split panes are attempted at all, while the backend (tmux versus iTerm2) is still auto-detected from the launch environment. In in-process mode the teammates live in an agent panel under the lead’s prompt (up and down to select, Enter to view and message, Escape to interrupt,xto stop, Ctrl+T to toggle the task list); an idle row hides after 30 seconds and returns on the teammate’s next turn (v2.1.181). Split panes are unsupported in VS Code’s integrated terminal, Windows Terminal, and Ghostty, so the pre-flight “no split-pane backend” line now means “falls back to in-process,” not “spawn fails.”Team and task state live on disk.
~/.claude/teams/{team-name}/config.jsonholds runtime state (session ids, tmux pane ids) and is removed when the session ends.~/.claude/tasks/{team-name}/persists locally under yourcleanupPeriodDays, so a resumed session keeps its tasks. The team config’smembersarray is how a teammate discovers the roster. Do not hand-edit or pre-author either; they are regenerated and overwritten on every state update.Task claiming is file-locked and dependency-aware. Tasks have three states (pending, in progress, completed). A pending task with unresolved dependencies cannot be claimed until they complete, and dependents unblock on their own. Concurrent self-claims are serialized with file locking. The documented caveat matches what the skill warns about: task status can lag when a teammate forgets to mark a task complete, which blocks its dependents until the lead nudges.
Plan approval for teammates is new. You can require a teammate to work in read-only plan mode until the lead approves its plan; the lead approves or rejects with feedback autonomously, and a rejected teammate revises and resubmits. The 2.1.185 binary confirms the path (“plan approval request(s), auto-approving”). This is the clean way to gate a risky refactor that a
tasks-mode teammate would otherwise start immediately.Teammates no longer inherit the lead’s
/modelby default. Set Default teammate model in/config, or pick “Default (leader’s model)” to follow the lead. A model outside the allowlist falls back to the default teammate model.Subagent definitions double as teammate roles. Spawn a teammate using a subagent type (project, user, plugin, or CLI). Its
toolsallowlist andmodelare honored and its body is appended to the teammate’s system prompt;SendMessageand the task tools stay available even under atoolsrestriction. TheskillsandmcpServersfrontmatter fields are not applied to a teammate, which loads those from project and user settings like any session.Worktree base ref is native now.
worktree.baseRef: "head"(the binary acceptsfreshorhead) bases worktrees on your currentHEAD, which is the built-in form of theWorktreeCreatehook in Decision 2; subagent and teammate worktrees follow the same base. This was first noted in the 2026-05-09 update and is restated here because it, not the hook, is the supported path.Cooperative shutdown survives; cleanup is automatic.
SendMessageand the shutdown handshake (approve, or reject with an explanation) are still in the binary. Shutdown is still slow because a teammate finishes its current tool call first, and the zombie risk is smaller now that team directories are removed on session exit. An orphaned tmux session can still linger:tmux lsthentmux kill-session -t <name>. The/exitescape hatch in Decision 4 still works.Sizing guidance is now documented. The docs codify the range this skill assumes: 3 to 5 teammates, 5 to 6 tasks per teammate; below that, plain subagents are cheaper and simpler.
Claude Code can spawn agents, and those agents can be grouped into a team where each member has its own pane and its own worktree. Once you know it works, the question becomes: how do you avoid forgetting one of the half-dozen flags every time you spawn a team at 2am?
nf-agents is the skill I wrote to answer that. It does not invent any new capability. It collapses the recipe into five named modes, runs a pre-flight check before any spawn, and hard-codes the patterns I kept reaching for.
The problem the skill solves
Without a skill, “spawn a team” is roughly seven decisions you make every time:
- Use a single
Agentcall, orTeamCreatevà severalAgentcalls? - Pass
team_nameto each agent (or accidentally forget and end up with isolated agents that cannot message each other)? - Pass
isolation: "worktree"(or accidentally let an agent edit your live working copy)? - Are you inside
tmux? Are you in iTerm2 withit2installed? Neither? Spawn fails differently in each case. - Is
teammateModeset to a split-pane backend, or toin-process(which silently changes everything)? - Is the
WorktreeCreatehook present so worktrees are based on your currentHEADand notorigin/main? - What are you actually asking each teammate to do? Standby and wait, or execute a brief autonomously?
Get any one of these wrong and you discover it five minutes later: panes that look right but cannot send messages to each other, an agent that mutated your live branch, a worktree that was based on stale main and is now ten commits behind your work.
The skill turns those seven decisions into one: pick a mode, answer the questions the skill prompts you with, done.
The five modes
| Mode | Use when |
|---|---|
standby | You have several parallel domains in flight but you do not yet know each task. Spawn a team, give each member a role, drive them interactively. |
tasks | You know exactly what each member should do. Spawn pre-briefed members that execute autonomously and report back when done. |
solo | One task, no parallelism, but you still want a worktree so the agent can commit and push without touching your working copy. |
status | Inspect the current team. Member names, busy/idle, recent updates. |
teardown | Shut the team down. Sends a cooperative shutdown signal, calls TeamDelete, surfaces any zombies for manual cleanup. |
Standby is the default. The model that fits my brain is “spawn the team, then think about what each member should work on first.”
Pre-flight checks (run on every spawn)
Each of these maps to a specific past failure:
# 1. teammateMode must enable split-pane mode (NOT "in-process")
MODE=$(jq -r '.teammateMode // "auto"' ~/.claude/settings.json)
[ "$MODE" = "in-process" ] && echo "FAIL: set teammateMode to 'tmux' or 'auto' first"
# 2. Split-pane backend available - ONE of these must hold:
# a. inside tmux session -> tmux split backend
# b. iTerm2 + it2 CLI >= 3.6.6 -> iTerm2 native split backend
if [ -n "${TMUX:-}" ]; then
echo "OK: tmux backend"
elif [ "${TERM_PROGRAM:-}" = "iTerm.app" ] && command -v it2 >/dev/null; then
echo "OK: iTerm2 backend"
else
echo "FAIL: no split-pane backend"
fi
# 3. WorktreeCreate hook present (so worktrees base on current HEAD)
jq -e '.hooks.WorktreeCreate' ~/.claude/settings.json >/dev/null \
|| echo "WARN: WorktreeCreate hook missing"
# 4. Current cwd is a git repo (required for isolation: "worktree")
git rev-parse --git-dir >/dev/null 2>&1 || echo "FAIL: not in a git repo"
Any failure is surfaced before the spawn is attempted. The skill does not auto-fix the environment, terminal launch context is the user’s responsibility.
A first run, end to end
You type:
/nf-agents standby
The skill asks (one batch, four questions):
- Team name. Suggested kebab-case (
worker-team). - Members. Names và a one-line role each (
api-worker: handles incoming webhooks,feed-worker: handles RSS ingestion). - Target branch. Defaults to your current branch.
- Backend. iTerm2 split (default, lighter) or tmux session (persists if you close the terminal).
It then runs the pre-flight, calls TeamCreate, and for each member spawns:
Agent({
name: "<member-name>",
team_name: "<team-name>",
subagent_type: "fullstack-engineer",
isolation: "worktree",
prompt: "<standby brief>"
})
Note (2026-06-22): on Claude Code 2.1.178 and later this exact recipe is historical.
TeamCreateis gone and theteam_nameinput is accepted but ignored. WithCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1set, you spawn the teammate directly and the team forms on its own. See the 2026-06-22 update at the top of this post.
The standby brief is templated. It tells the member: do not start any code work, wait for the user to type into your pane or for SendMessage from the team lead, when you finish a task call TaskUpdate(status: "completed") and SendMessage the lead a one-paragraph summary, open PRs against the target branch, never auto-merge, never push directly to the target branch.
Acknowledge in two lines, then idle. The lead returns to idle. You start driving members.
The rest of this post walks through the four design decisions inside the skill that took the longest to get right and explains the incidents that drove each one.
Decision 1: Backend choice (tmux vs iTerm2 split)
When Claude Code spawns a team, every member needs a pane. The pane has to live somewhere, and where that “somewhere” is depends on the environment when you launched claude, not on a setting you can flip mid-session.
The decision tree is small but easy to misread:
Environment when claude was launched | Backend chosen |
|---|---|
TMUX env var is set (claude was launched inside tmux) | tmux split-window in the same tmux session |
TERM_PROGRAM=iTerm.app, it2 CLI present, TMUX unset | iTerm2 native split (separate panes in iTerm2) |
| Neither | Spawn fails, or falls through to in-process if teammateMode: auto |
The setting teammateMode: tmux is misleading: it does not force tmux. It means “any split-pane backend, do not fall back to in-process.” The binary auto-detects which split backend to use.
The persistence question is the real one
The reason backend choice matters is not aesthetics, it is whether the team survives a closed terminal:
| Backend | Close terminal, what survives |
|---|---|
| iTerm2 split | All claude PTYs are killed. Team teardown cascades. Individual transcripts persist on disk under ~/.claude/projects/.../<session-id>.jsonl and you can claude --resume <id> to pick up a single session, but the team registry and the SendMessage graph between members are gone. |
| tmux session | tmux server keeps running detached. All claude processes alive. tmux attach -t <name> reconnects exactly where you left off. Survives terminal close, even SSH disconnect. |
The skill asks about this before spawning:
Backend: do you want iTerm2 split (default, lighter, no persistence) or tmux session (persist, attach from another machine or mobile)?
If you pick tmux but claude was not launched inside tmux, the skill aborts and tells you to tmux new -s <name> first, then re-run claude inside it. It does not auto-fix the environment because the launch context is the user’s responsibility, and silently re-launching claude would orphan the current conversation.
When I pick which
- Daily work, single machine, a few hours: iTerm2 split. Lighter, and I do not need to attach from elsewhere.
- I might close the lid and come back tomorrow, or I want to peek at the team from my phone over SSH: tmux.
- Long-running team (more than a day): tmux, no question.
- One parallel task, do not need to watch live output: standalone
Agent, no team, the backend question does not apply.
Decision 2: Worktree base branch (and why a hook had to override the default)
isolation: "worktree" tells Claude Code to create a temporary git worktree for each agent. The agent commits, pushes, and PRs from inside the worktree, leaving your live working copy untouched.
The thing that took an incident to learn: by default, Claude Code bases the agent’s worktree on origin/<default-branch> (typically origin/main), not on your current HEAD.
That sounds reasonable until you actually use it. Imagine the workflow:
- You are on a feature branch with eight commits of in-progress work.
- You want an agent to add a small refactor to that feature.
- You spawn the agent with
isolation: "worktree". - The worktree is created from
origin/main, not from your current branch. - The agent does the refactor, commits, pushes, opens a PR.
- You merge the PR back into your feature branch.
- The merge brings in the refactor, và a complete reversion of your eight in-progress commits, because the worktree never knew they existed.
The fix is a WorktreeCreate hook in ~/.claude/settings.json that intercepts the worktree creation command and rewrites it to base on the parent session’s current HEAD instead. Roughly:
git worktree add --no-track -b worktree/<slug> <path> HEAD
The --no-track is deliberate: the worktree branch should not silently track any upstream. The first push from the agent must use git push -u origin worktree/<slug> to set its upstream explicitly, after which subsequent pushes are plain git push.
The skill checks for the hook on every spawn:
jq -e '.hooks.WorktreeCreate' ~/.claude/settings.json >/dev/null \
|| echo "WARN: WorktreeCreate hook missing - worktrees will base on origin/<default-branch>"
If the hook is missing, the skill warns but does not block. (Some users genuinely want the default behavior, particularly if they rebase their work onto main constantly.) For my workflow the hook is required, so the warning is also a reminder to re-verify after every Claude Code version upgrade, since the binary occasionally changes its hook resolution.
The agent’s commit and PR contract
Once the worktree is on the right branch, the agent’s contract is small:
- Auto-commit and auto-push are allowed, follow the team’s commit format.
- First push uses
-u origin worktree/<slug>. Subsequent pushes are plain. - PRs go back to the parent branch the worktree was based on, not to
main. - Never auto-merge. Never push directly to the parent branch.
- After PR is open, the agent’s job is done. The user reviews and merges.
The skill bakes all of this into the standby and tasks prompt templates, so every spawned member starts with this contract loaded.
Decision 3: Standby vs tasks (when to use which)
Both modes spawn a team. The difference is who is driving:
Standby is for “I have several domains and I will figure out each task as I go.” Members get a role, an idle protocol, and wait. You then either:
- Type directly into a member’s pane (works for tmux and iTerm2 splits, the model behind that pane is a real Claude session you can talk to).
- Send a
SendMessagefrom the lead pane:SendMessage({to: "api-worker", message: "Write a unit test for the webhook signature validator..."}).
Tasks is for “I know exactly what each member should do.” The brief is in the spawn prompt. The agent reads it, executes it, calls TaskUpdate(status: "completed") và SendMessage(to: "team-lead", message: "<summary + PR url>") when done.
The split matters because the brief is shaped differently. Standby briefs are about identity and protocol (“you are api-worker, your role is incoming webhooks, wait for direction”). Task briefs are about scope and acceptance (“read these files, change this behavior, here are the acceptance criteria, here is the reporting protocol”).
Vague task briefs are the most common failure I see. The agent ends up asking the user clarifying questions mid-execution, which defeats the point of autonomy. A good task brief includes:
- Scope: one paragraph, “what to change.”
- Acceptance criteria: bullet list, “the change is done when X, Y, Z.”
- Pointers: file paths, doc links, memory file names the agent should read first.
- Reporting protocol:
TaskUpdatevàSendMessageon done, PR target branch.
If you would not give the brief to a junior engineer expecting them to deliver without asking, do not give it to an agent.
Decision 4: The cooperative shutdown protocol
When you are done with a team you call teardown. The skill’s job is to:
- Send
SendMessage({type: "shutdown_request", reason: "..."})to each member, once. - Wait roughly ten seconds.
- Call
TeamDelete({team_name})to free the registry name. - Surface any zombies and tell the user how to clean them up by hand.
The key word is cooperative. A teammate has to actively call SendMessage({type: "shutdown_response", approve: true}) to terminate. If it only writes “Acknowledged, shutting down” as plain text without calling the tool, its process keeps running. The pane is a zombie: the worktree the team registry pointed it at has been deleted by TeamDelete, so the zombie cannot do useful work, but the OS process is still resident.
This is the part of the skill that has the most stern wording, because it is the part most likely to leave you with leaked processes:
- Do not retry the same
shutdown_request. A misunderstanding ten seconds ago will be the same misunderstanding ten seconds from now. - Tell the user explicitly which member did not approve. Naming the zombie makes it actionable.
- Suggest manual cleanup in this order: click the iTerm2 pane and
Cmd+W, focus the tmux pane andCtrl-B x, orps aux | grep claudeand kill the PID withkill(thenkill -9only if SIGTERM is ignored). - Do not try to auto-kill. The lead does not have the teammate’s PID and does not manage iTerm2 or tmux directly. Pretending it can leads to “I killed something but I am not sure what.”
The escape hatch
If multiple zombies refuse to approve and Cmd+W-ing each pane is tedious, there is a one-action nuclear option: type /exit in the lead pane.
Claude Code shows a dialog, “Background work is running, Exit anyway / Stay.” Pick Exit anyway. The lead terminates, a cascade signal kills every member, every pane closes, and in iTerm2 the entire window or tab closes too.
The cost is that the lead session is gone. The transcript persists at ~/.claude/projects/.../<session-id>.jsonl and you can claude --continue later, but the live conversation context is lost.
I recommend the escape hatch only when there is no further valuable context in the lead session anyway. If the lead has been planning something complex, do not nuke it for the sake of zombie cleanup, do the manual Cmd+W walk.
Mistakes the skill exists to prevent
These are the patterns that cost me time before the skill existed. The skill encodes a check or a default for each:
- Spawning
Agentwithoutteam_namewhen you wanted a team. The result looks like an isolated background agent: it cannotSendMessageto anyone. The skill always asks “team or solo” up front so this is impossible. - Manual
tmux split-windowvàclaude --continuein another pane. The panes look like a team, but they have no shared messaging registry, they are just independent sessions. The skill always usesAgent + team_nameso the registry exists. - Forgetting
isolation: "worktree"for code-editing teammates. Their work mutates your live working copy directly. The skill defaultsisolation: "worktree"for any agent that will write files. - Vague task briefs. Covered above. The skill’s templates include the structure I have learned to write.
- Auto-merging the agent’s branch. Never do this. The skill’s prompt template is explicit: PR only, never auto-merge.
What you should keep even if you never use my skill
Four patterns generalize cleanly:
- Pre-flight checks before any irreversible action. Spawning a team that lacks split-pane backend ends with you killing eight processes by hand. Four
bashlines at the top of the skill removes the entire class of failure. - Named modes instead of free-form arguments. “Spawn a team for me” is ambiguous.
standby,tasks,soloare not. The skill’s user (you, six hours later) does not have to remember which arguments do what. - A
WorktreeCreatehook that bases agent worktrees on your currentHEAD. Re-verify after every Claude Code upgrade. - A reporting protocol baked into every spawn prompt.
TaskUpdatevàSendMessageon completion, PR not auto-merge, never push to parent branch directly.
The skill itself is just one way to encode those, and the encoding is the cheap part. The patterns are the value.
Get the skill
White-labeled, ready to drop into ~/.claude/skills/. Published in the claude-skills-toolkit repo.
# Option 1: download the latest release zip
curl -L https://github.com/llawliet11/claude-skills-toolkit/releases/latest/download/nf-agents.zip -o nf-agents.zip
unzip nf-agents.zip -d ~/.claude/skills/
# Option 2: clone the repo
git clone https://github.com/llawliet11/claude-skills-toolkit.git
cp -r claude-skills-toolkit/nf-agents ~/.claude/skills/
Verify in a new Claude Code session: type /nf-agents and the skill should appear in the slash-command list. The skill’s own README.md covers required environment, the WorktreeCreate hook, and customization.
If you adopt it and find a failure mode I have not yet hit, open an issue on the repo or let me know.