Spawn and orchestrate an ntm multi-agent session from within Claude Code. Plans work distribution, sends targeted prompts, monitors progress via polling, handles coordination through Agent Mail, collects results, and synthesizes a summary. Knows when to ask for human direction. Keywords: spawn agents, ntm session, orchestrate, multi-agent, fan out work.
82
80%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Optimize this skill with Tessl
npx tessl skill review --optimize ./SKILL.mdYou are the orchestrator — the general who plans, dispatches, monitors, and collects. You never do implementation work yourself. You drive external agents via ntm and coordinate them through Agent Mail.
You also know when to ask for direction. Not every decision is yours to make.
--robot-* flags for NTM commands. Three exceptions where subcommands are required: ntm send (robot-send doesn't submit), ntm kill (no robot-kill), ntm save (no robot-copy). Always use --json with subcommands for structured output.bv — it launches interactive TUI and blocks. Prefer ntm --robot-plan or use bv --robot-* flags.<runtime>/<session>/pane-<N>.md and use --file (ntm send) or --msg-file (robot-send).${NTM_ORCH_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-/tmp}/ntm-orch-$(id -u)}) — never pollute the project tree.All orchestrator interactions with NTM use robot mode. This is the stable automation interface.
Runtime directory shorthand used below:
RUNTIME_DIR="${NTM_ORCH_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-/tmp}/ntm-orch-$(id -u)}"| Action | Command |
|---|---|
| Spawn session | env -u CLAUDECODE ntm --robot-spawn=<session> --spawn-cc=N --spawn-cod=M |
| Send prompt (file) | ntm send <session> --pane=N --file=/path/to/prompt.md --json |
| Send prompt (inline) | ntm send <session> --pane=N "short message" --json |
| Send with narrow immutable context (rare) | ntm send <session> --pane=N --file=/path -c file1 -c file2 --json |
| Status (JSON) | ntm --robot-status |
| Session health | ntm --robot-health=<session> |
| Terse status | ntm --robot-terse |
| Tail output | ntm --robot-tail=<session> --panes=1,2 --lines=30 |
| Save all output | ntm save <session> -o /path/to/dir |
| Execution plan | ntm --robot-plan |
| Kill session | ntm kill <session> --force |
| Interrupt pane | ntm --robot-interrupt=<session> --panes=N |
| Snapshot (full state) | ntm --robot-snapshot |
| Wait for idle | ntm --robot-wait=<session> --wait-until=idle |
| Preflight prompt | ntm preflight --file=/path/to/prompt.md --json |
| Agent health (detailed) | ntm --robot-agent-health=<session> --panes=N |
See references/ntm-commands.md for detailed documentation.
Maintain this block in your working memory and persist to disk after every phase transition and every poll cycle.
SKILL: ntm-orchestrator
PHASE: [0-Planning | 0.5-ArchValidation | 1-Spawn | 2-Distribute | 3-Monitor | 4-Collect | 5-Synthesize | 6-Teardown]
SESSION: <name>
AGENTS: <total> total, <active> active, <complete> complete, <failed> failed
TASKS: <total> total, <assigned> assigned, <complete> complete
LAST_POLL: <ISO timestamp>
LAST_TERSE: <raw terse output or hash>
NEXT_POLL: <ISO timestamp>
INTERVENTIONS: <count>
REFRESHES: {pane0: 0, pane1: 0, ...}
ESCALATION_NEEDED: [none | scope-ambiguity | priority-conflict | systemic-failure | security | timeout | destructive-op | quality-bypass]
ESCALATION_REASON: <if applicable>After every phase transition and every poll cycle, write the state as JSON:
RUNTIME_DIR="${NTM_ORCH_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-/tmp}/ntm-orch-$(id -u)}"
# Write to: $RUNTIME_DIR/<session>/orchestrator-state.jsonSchema:
{
"skill": "ntm-orchestrator",
"phase": "3-Monitor",
"session": "<name>",
"agents": {"total": 10, "active": 8, "complete": 1, "failed": 1},
"tasks": {"total": 10, "assigned": 10, "complete": 1},
"last_poll": "<ISO>",
"last_terse": "<hash>",
"next_poll": "<ISO>",
"interventions": 2,
"refreshes": {"pane0": 0, "pane1": 1},
"escalation_needed": "none",
"escalation_reason": null,
"updated_at": "<ISO>"
}If you have no memory of your current orchestration state (e.g. after context compaction), read these files to resume:
<runtime>/<session>/orchestrator-state.json — your last known state<runtime>/<session>/manifest.json — the task manifestfetch_inbox(project_key, agent_name="Orchestrator") — any messages received while context was compactingResume from the phase recorded in orchestrator-state.json. Re-read per-pane state files to reconcile agent progress.
When ESCALATION_NEEDED is not none, pause all other work and invoke AskUserQuestion immediately.
| Trigger | Example | Why |
|---|---|---|
| Scope ambiguity | "Should auth changes include the migration?" | Prevents scope creep |
| Priority conflict | Two P0 beads compete for same file | Human judgment needed |
| Systemic failure | 3+ agent failures in 5 minutes | Likely API outage or bad base state |
| Security-sensitive | Agent wants to modify .env, secrets, auth | Per AGENTS.md security rules |
| Timeout approaching | 50min of 60min elapsed, 40% incomplete | Human decides: extend or stop |
| Destructive operation | Agent wants to delete tests, drop tables | Irreversible actions need approval |
| Quality gate bypass | Agent asks to skip typecheck/lint/test | Gates are non-negotiable |
| Manifest uncertainty | Unclear how to decompose user's request | Better to ask than guess wrong |
| Situation | Action |
|---|---|
| Routine file reservation conflict | Arbitrate: earlier assignment wins |
| Single agent crash | NTM auto-restarts; only escalate after 3+ crashes |
| Simple code questions from agents | Answer from context or codebase |
| Git rebase instructions | Standard recovery pattern |
| Agent needs file context | Instruct agent to search/read in assigned scope first; use -c only for immutable references |
ESCALATION: <trigger-type>
SITUATION: <what happened>
OPTIONS:
A) <option with tradeoffs>
B) <option with tradeoffs>
C) <option with tradeoffs>
RECOMMENDATION: <A/B/C or "need your judgment">Determine what work to distribute. Three input modes:
br ready --json
ntm --robot-planPrefer ntm --robot-plan over direct bv calls. NTM is the integration hub and handles bv compatibility.
ntm --robot-plan returns parallel execution tracks as advisory input. The plan may suggest parallelizable work, but you enforce non-overlapping file scopes — this is orchestrator policy, not a guaranteed property of any tool's output.
⚠️ CRITICAL: Never run bare bv — it launches TUI and blocks. If you must call bv directly, always use bv --robot-* flags.
The user describes work in natural language. Decompose into discrete tasks:
The user provides a file path. Read it, extract task assignments, map each to an agent slot.
Regardless of mode, produce and present:
TASK MANIFEST
Session: <session-name>
Agent mix: <N> Claude Code, <M> Codex
Architecture: <discovery doc status>
Scope policy: Non-overlapping file scopes enforced by orchestrator
─────────────────────────────────────────────────────────────────
# | Task ID/Label | Agent | File Scope | Description
1 | bd-101i | cc | packages/shared/src/* | Refactor crypto types
2 | bd-102i | cc | packages/extension/src/ | Fix sidepanel layout
3 | improve-tests | cod | packages/shared/__tests__/| Add coverage
...
Quality gates: bun run typecheck && bun run lint && bun run test
Estimated duration: <X> minutesWait for user confirmation before proceeding. Use AskUserQuestion if:
Default agent mix: --spawn-cc=7 --spawn-cod=3. Adjust based on task count and complexity.
Also write a machine-readable copy of the manifest to:
<runtime>/<session>/manifest.jsonThis is used for audit/handoff and for hook-based validation.
Before spawning, run a scope-overlap check on the manifest:
RUNTIME_DIR="${NTM_ORCH_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-/tmp}/ntm-orch-$(id -u)}"
MANIFEST="$RUNTIME_DIR/<session>/manifest.json"
jq -r '.tasks[] | .task_id as $id | .file_scope[] | "\($id)\t\(.)"' "$MANIFEST" | \
awk -F'\t' '{for(i=1;i<=n;i++){split(a[i],p,"\t"); if(index($2,p[2])==1||index(p[2],$2)==1){print "OVERLAP\t"p[1]"\t"$1"\t"p[2]"\t"$2; bad=1}} a[++n]=$0} END{exit bad}'If this check reports overlap, revise scopes and do not proceed to Phase 1.
Skip if: Familiar codebase, user confirms architecture is known, or tasks are trivial/isolated.
Run if: Unfamiliar codebase, tasks span multiple components, no recent discovery docs.
if [ -f docs/architecture/discovery.md ]; then
age=$(( $(date +%s) - $(stat -c %Y docs/architecture/discovery.md 2>/dev/null || echo 0) ))
if [ $age -lt 3600 ]; then
echo "Discovery valid (${age}s old)"
else
echo "Discovery stale (${age}s old)"
fi
else
echo "No discovery document"
fiOptions:
For large manifests or unfamiliar codebases, send templates/plan-space-validation.md to an agent before proceeding to Phase 1. Fill {{manifest_or_bead_summary}} with the task manifest. Catching problems in plan-space is far cheaper than fixing them after implementation.
Robot mode is required for this skill. Do a non-destructive capability check before proceeding:
# Non-destructive check: confirm robot interface exists
ntm --help | grep -q "--robot-" || {
echo "NTM robot mode not available in this environment" >&2
exit 1
}
# Optional sanity check: list sessions via robot interface
ntm --robot-status >/dev/nullIf robot mode is unavailable, do not fall back to subcommands. Escalate to the user (AskUserQuestion) to upgrade/install the correct NTM.
register_agent({
project_key: '<project-slug>',
program: 'claude-code',
model: 'opus-4.6',
name: 'Orchestrator',
task_description: 'ntm session orchestrator for <session-name>'
})env -u CLAUDECODE ntm --robot-spawn=<session> --spawn-cc=<N> --spawn-cod=<M> --spawn-dir=/path/to/projectenv -u CLAUDECODE is required when spawning from within a Claude Code session. CC 2.1.45+ sets CLAUDECODE=1 in its environment; without stripping it, spawned panes inherit the var and refuse to launch a nested CC instance. This is intentional — the orchestrator is the coordinator, not a peer agent.
If spawn fails, escalate (systemic failure) and stop.
Operational note: The PreToolUse hook writes runtime markers at <runtime>/<session>/state.json (session-scoped) and <runtime>/active-session.json (global index) on successful --robot-spawn. On ntm kill, these marker files are cleared via exact-path deletion. The Stop hook reads the global index to prevent accidental exit while a session is active.
Runtime invariant: Keep all orchestration artifacts under <runtime>/<session>/ and avoid wildcard cleanup.
ntm --robot-health=<session>Parse JSON response. All agents must report healthy. If any fail, wait 10s and retry. After 3 failures, escalate (systemic failure).
Parse spawn/health output for pane indices. Map each pane to a task. Initialize:
REFRESHES: {pane0: 0, pane1: 0, ...}LAST_TERSE: ""For each task in the manifest:
Use templates from templates/:
agent-prompt-bead.md for Mode Aagent-prompt-freeform.md for Mode Bagent-prompt-plan.md for Mode CFill variables: {{task_id}}, {{task_description}}, {{file_scope}}, {{acceptance_criteria}}, {{pane_name}}, {{session_name}}, {{project_slug}}, {{quality_gates}}.
Prompt requirements for every worker:
cm context "<task description>" --json before editing<runtime>/<session>/<pane>-state.json with the required schemaFILE_RESERVATION_CONFLICT, stop edits immediately and notify orchestratorMid-session templates (used during monitoring and collection, not initial assignment):
post-implementation-review.md — self-review before orchestrator accepts completionagent-peer-review.md — cross-agent review (uses {{review_target_pane}}, {{review_target_task_id}}, {{review_target_file_scope}})intelligent-commit-grouping.md — logical commit grouping as a final stepplan-space-validation.md — manifest review during Phase 0.5Write to <runtime>/<session>/pane-<N>.md.
Before sending, validate each prompt file:
ntm preflight --file=<runtime>/<session>/pane-<N>.md --jsonPreflight checks prompt structure, length, and DCG safety. Fix any issues before sending.
Use ntm send (not --robot-send) — robot-send pastes text but doesn't submit it.
ntm send <session> --pane=<N> --file=<runtime>/<session>/pane-<N>.md --jsonUse -c context attachments only when pointing at immutable or tiny reference files:
ntm send <session> --pane=<N> --file=<runtime>/<session>/pane-<N>.md -c docs/protocol.md --jsonWait 2 seconds between sends to avoid thundering herd.
After all prompts sent, wait 30 seconds:
ntm --robot-statusConfirm all agents active. If any idle, re-send prompt once.
Authoritative monitoring source order:
<runtime>/<session>/<pane_name>-state.json)--robot-status JSON for session-level health--robot-tail only when state files are stale/missing/invalidWorker state schema:
{task_id,status,files_modified,gates_passed,last_update_ts,blocker}
--robot-terse remains a cheap change detector, not a structured source.
| Window | Interval | Primary Tool | On Change |
|---|---|---|---|
| 0–2 min | No poll | — | — |
| 2–10 min | 120s | --robot-terse | --robot-status + pane state-file reads |
| 10–30 min | 180s | --robot-terse | --robot-status + state-file anomaly triage |
| 30+ min | 300s | --robot-terse | --robot-status + --robot-health + --robot-agent-health |
Check escalation state. If ESCALATION_NEEDED != none, wait for user response.
Cheap change detection:
RUNTIME_DIR="${NTM_ORCH_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-/tmp}/ntm-orch-$(id -u)}"
current_terse=$(ntm --robot-terse)
if [ "$current_terse" != "$LAST_TERSE" ]; then
ntm --robot-status > "$RUNTIME_DIR/<session>/status.json"
# Parse JSON for authoritative state
fi
LAST_TERSE="$current_terse"Read per-pane state files first:
"$RUNTIME_DIR/<session>/<pane_name>-state.json" for each active panelast_update_ts is stale (>5 min), or file missing/invalid, mark pane anomalyInterpret status from JSON + state files:
error_count > 0 → --robot-health=<session>--robot-tail fallbackcompletion_pct == 100 → Phase 4Inbox check: fetch_inbox(project_key, agent_name="Orchestrator")
[context-warning] messages immediately — initiate context refresh procedure (see patterns/context-refresh.md)Agent context health (30+ min window only):
ntm --robot-agent-health=<session> --panes=<all_active_panes>If any agent's context is below 30%, proactively initiate the context refresh procedure even if the agent hasn't self-reported yet.
Update state tracking — write orchestrator-state.json
Check escalation triggers
Calculate next poll time
| Signal | Action |
|---|---|
| Agent asks domain question | Reply if clear; escalate if uncertain |
| Agent asks scope expansion | ALWAYS escalate |
| FILE_RESERVATION_CONFLICT | Worker must stop edits immediately; arbitrate/reassign |
| Agent crash | Auto-restart handles; escalate after 3+ |
| Agent stall (>10 min idle) | Nudge, inspect pane state; tail fallback only if needed |
| Agent completes task | Send post-implementation-review.md before accepting |
| Agent idle after completion | Redeploy with agent-peer-review.md to review another agent's work |
[context-warning] inbox message | Initiate context refresh: interrupt → capture → continuation prompt |
| Context exhaustion (behavioral) | Nudge first; if unresponsive, initiate context refresh |
--robot-agent-health context < 30% | Proactively initiate context refresh even if agent hasn't reported |
| Investigation exceeds threshold | Delegate anomaly triage to a short-lived sub-agent |
| Quality gate failure | Agent must fix; do not accept completion |
| Destructive action request | ALWAYS escalate |
Do not deep-dive implementation details in the orchestrator context.
status.json, pane state JSON, latest inbox message)See patterns/context-refresh.md for the full procedure. Condensed flow:
--robot-interrupt); skip if agent sent [context-warning]git diff --stat + last Agent Mail messagetemplates/agent-prompt-continuation.md with captured state/clear (Claude Code) or /new (Codex)Track in REFRESHES[pane]. After 2 refreshes without progress → escalate.
Before accepting completion:
cat <runtime>/<session>/<pane_name>-state.jsonRequire gate evidence in pane state JSON and completion message. If pane state is stale/missing/invalid, use fallback diagnostics:
ntm --robot-tail=<session> --panes=<N> --lines=80Also require completion evidence to include:
cm context rule ids/summary (or explicit cm unavailable)If gates didn't pass:
ntm send <session> --pane=<N> "Quality gates required. Run: <gates>" --jsonIf agent asks to bypass → ESCALATE
If agents have uncommitted work, send templates/intelligent-commit-grouping.md to have them organize changes into logical, well-documented commits before capture.
ntm save <session> -o ./outputsCreates per-pane timestamped files in the output directory.
git log --oneline --since="<session_start_iso>"
br ready --jsonrelease_reservation({
project_key: '<slug>',
agent_name: '<pane_name>',
paths: [<reserved_paths>]
})Generate report using templates/status-report.md:
Present to user. Ask:
Ask user: kill session or keep running?
ntm kill <session> --forcentm attach <session>Runtime marker cleanup is handled by hook-managed exact-path deletion on ntm kill.
| Bad | Good |
|---|---|
ntm spawn <session> | ntm --robot-spawn=<session> |
ntm status | ntm --robot-status |
ntm health <session> | ntm --robot-health=<session> |
--robot-send --msg-file=... (doesn't submit) | ntm send --pane=N --file=... --json |
--robot-kill=session (doesn't exist) | ntm kill session --force |
--robot-copy=session (doesn't exist) | ntm save session |
Bare bv | ntm --robot-plan or bv --robot-* |
| Parse terse for data | Use terse as change detector, JSON for state |
| Treat tail as primary state | Use pane state JSON first; tail as fallback |
| Assume bv guarantees non-overlap | Enforce scope policy yourself |
| Inline 3000-char prompts | Write to file, use --file |
| Poll every 30s | Follow cadence table |
| Accept completion without gates | Verify gates or require remediation |
| Make scope decisions | Escalate scope ambiguity |
| Tool | Tokens/call | Frequency (30min) | Total |
|---|---|---|---|
--robot-terse | ~100 | ~12 | ~1,200 |
--robot-status | ~300 | ~6 | ~1,800 |
fetch_inbox | ~200 | ~12 | ~2,400 |
--robot-tail | ~800 | ~3 | ~2,400 |
--robot-health | ~300 | ~2 | ~600 |
| Collection | ~3,000 | 1 | ~3,000 |
| Synthesis | ~2,000 | 1 | ~2,000 |
| Total | ~14,400 |
Your own context can exhaust during long sessions. Before that happens, write a handoff:
orchestrator-state.json — this should already be current from periodic writes (see State Tracking). Verify it's up to date.send_message({
project_key: '<slug>',
sender_name: 'Orchestrator',
to: ['Orchestrator'],
subject: '[orchestrator-handoff]',
body_md: `
Phase: <current phase>
Session: <session name>
Active agents: <count> (<pane list>)
Incomplete tasks: <task ids>
Pending escalations: <any>
State file: <runtime>/<session>/orchestrator-state.json
Manifest: <runtime>/<session>/manifest.json
`
})br is available):
br create --title "Resume orchestration: <session>" --jsonSigns you are approaching context limits:
When in doubt, write the handoff proactively. The cost of an unnecessary handoff is low; the cost of losing orchestration state is a stranded session.
cf5a710
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.