Files
oh-my-opencode/src/features/builtin-skills/skills/team-mode.ts
T
YeonGyu-Kim c90be7f9a6 feat(team-mode): align member and lead prompts with Claude Code free-code reference
Bring the team member addendum and the team-mode skill in line with the
Claude Code experimental Agent Teams reference (../free-code) on the
behavioral framing the LLM needs, while keeping every omo-specific
constraint (TeamRunId param, lead-only tool blocklist, eligibility
registry, sisyphus-junior routing) intact.

Member addendum (src/features/team-mode/member-guidance.ts) — adopt
free-code teammatePromptAddendum.ts wisdom that was missing:

- "User interacts primarily with the team lead" — members no longer try
  to address the user directly; they coordinate via task system + DMs.
- Stronger emphasis that plain assistant text is invisible to the team;
  team_send_message is mandatory, not optional.
- Broadcast (`to: "*"`) called out explicitly with "sparingly" guidance
  and the lead-only constraint.
- "Idle is normal" section: idle ≠ done, idle teammates can still
  receive messages, the lead must not treat idle as an error.
- "Communication rules" section forbidding structured JSON status
  messages and forbidding terminal tools as a peer-inspection backdoor;
  always reference teammates by NAME, not session ID.
- Auto-delivery framing: messages arrive as new turns, no manual inbox
  polling, no need to quote a teammate's message back to the lead.

Team-mode skill (src/features/builtin-skills/skills/team-mode.ts) —
adopt free-code TeamCreateTool/prompt.ts sections that were missing:

- Task ownership: any agent can set/change `owner` via team_task_update.
- Automatic message delivery: <peer_message ...> envelope flow.
- Teammate idle state: idle is the expected post-turn state; peer DM
  summaries surface in the lead's idle notification.
- Discovering team members: team_status + ~/.omo/teams/ config; refer by
  name, never raw session IDs.
- Task list coordination: claim by ID order (lowest first), check after
  every completion, escalate blocking deadlocks to the lead.
- Communication rules: same anti-patterns as the member addendum, scoped
  for the lead's awareness (no JSON status, no terminal peer-inspection,
  no delegate-task from inside members).

Test (create.test.ts) updated to track the renamed section header
("Lead-only tools you must NOT call") and to lock the new free-code
parity assertions ("user interacts primarily with the team lead",
"Idle is normal", "structured JSON status messages").

All other contract tests (team-mode skill keyword/section tests,
member parser, runtime tests) untouched and still passing.

Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode).
2026-05-06 14:20:50 +09:00

182 lines
7.9 KiB
TypeScript

import type { BuiltinSkill } from "../types"
export const teamModeSkill: BuiltinSkill = {
name: "team-mode",
description:
"Team orchestration — create and manage parallel agent teams (OFF by default; enable via team_mode.enabled in config). Loading this skill provides usage documentation; the team_* tools are registered globally when team_mode.enabled=true and access-gated by team role.",
template: `# Team Mode
Team mode gives Claude Code Agent Teams parity. It is off by default. Enable it only when you want parallel multi-agent coordination, where each team member is an opencode child session.
## When to use
- Split a large job across several agents.
- Keep a lead agent focused while member agents work in parallel.
- Use worktree mode for isolated code changes, or tmux visualization when you want live session layout.
## Declare a team
Create a team at \`~/.omo/teams/{name}/config.json\`.
You can also pass the same object directly to \`team_create({ inline_spec: ... })\`.
This TeamSpec uses a lead plus members list. Every canonical member has a \`kind\` discriminator.
Example:
\`\`\`json
{
"name": "release-squad",
"lead": {
"kind": "subagent_type",
"subagent_type": "sisyphus"
},
"members": [
{
"kind": "category",
"category": "quick",
"prompt": "review small changes and report risks"
},
{
"kind": "subagent_type",
"subagent_type": "atlas"
}
]
}
\`\`\`
Inline shorthand is accepted for category members. If \`kind\` is omitted, \`category\` implies \`kind: "category"\`. If a member uses natural planning fields like \`role\`, \`description\`, \`capabilities\`, or an unknown \`kind\`, it becomes a category worker using the current config's first enabled category. If \`kind\` is an unknown string such as a category name, that string is used as the category. \`systemPrompt\` is accepted as a \`prompt\` alias, and \`loadSkills\` is ignored because team members receive their behavior through \`prompt\`.
Example:
\`\`\`json
{
"name": "project-analysis-team",
"members": [
{
"name": "structure-analyst",
"category": "quick",
"systemPrompt": "Analyze directory layouts, module boundaries, and architectural organization."
},
{
"name": "quality-analyst",
"category": "quick",
"systemPrompt": "Analyze tests, CI/CD, build scripts, conventions, and anti-patterns."
},
{
"name": "Agent 3: Quality/Process Analyst",
"role": "Quality/Process Analyst",
"capabilities": ["tests", "builds", "CI/CD"]
}
]
}
\`\`\`
## Member schema
Use \`kind: "category"\` when you want a category-backed worker. It must include both \`category\` and \`prompt\`. D-40: category members always route through \`sisyphus-junior\`.
Use \`kind: "subagent_type"\` only for eligible agents.
### Eligible subagent types
- \`sisyphus\`
- \`atlas\`
- \`sisyphus-junior\`
- \`hephaestus\`
### Hard rejects
Do not use \`oracle\`, \`prometheus\`, or other non-eligible agents here. For those, use \`delegate-task\` instead.
## Lifecycle
1. Lead creates the team with \`team_create({ teamName: "existing-team" })\` or \`team_create({ inline_spec: { name: "team-name", members: [...] } })\`. Never call \`team_create\` with empty arguments.
2. Lead assigns work with \`team_send_message\` or \`team_task_create\`.
3. Members report progress with \`team_send_message\` plus \`team_task_update\`.
4. Lead and members track progress with \`team_task_list\`, \`team_task_get\`, and \`team_status\`.
5. Lead requests shutdown with \`team_shutdown_request\` when the team is ready to wind down.
6. The targeted member or the lead handles \`team_approve_shutdown\` or \`team_reject_shutdown\`.
7. Lead removes the team with \`team_delete\`.
## Task ownership
Any agent can set or change task ownership via \`team_task_update\` with the \`owner\` field. Members typically claim work by setting \`owner: "<their-name>"\` and \`status: "claimed"\` (or directly \`"in_progress"\`). The lead can also pre-assign work by creating tasks with \`owner\` set.
## Automatic message delivery
Messages sent via \`team_send_message\` are automatically delivered to the recipient as new conversation turns — no manual inbox polling. If a recipient is mid-turn, the message is queued and injected when its turn ends, wrapped in a \`<peer_message ...>\` envelope. The UI surfaces a brief notification with the sender's name. When reporting on teammate messages, do NOT quote the original — it has already been rendered.
## Teammate idle state
Teammates go idle after every turn — this is normal and expected. A teammate going idle immediately after sending a message does NOT mean they are done or unavailable. Idle simply means they are waiting for input.
- Idle teammates can still receive messages; sending one wakes them up.
- The system emits idle notifications automatically. The lead does not need to react to every idle event — only when assigning new work or following up.
- Do not treat idle as an error. A teammate that sent a message and went idle has done its job and is awaiting reply.
- Peer DMs include a brief summary in the lead's idle notification, giving the lead visibility into peer collaboration without the full message text.
## Discovering team members
Members and the lead use \`team_status({ teamRunId })\` to see who is active, their session IDs, message backlog, and tmux pane assignments. The team config also lives at \`~/.omo/teams/{name}/config.json\` for declared teams. Always refer to teammates by their NAME (e.g., \`"lead"\`, \`"researcher"\`) — never by raw session IDs.
## Task list coordination
Members should:
1. Check \`team_task_list\` periodically, **especially after completing each task**, to find newly unblocked work.
2. Claim unassigned, unblocked tasks via \`team_task_update\` (set \`owner\` and \`status: "claimed"\` or \`"in_progress"\`). Prefer tasks in ID order (lowest first) — earlier tasks usually establish context for later ones.
3. Create new tasks via \`team_task_create\` when they identify additional work.
4. Mark tasks completed via \`team_task_update\` with \`status: "completed"\`, then re-check the task list.
5. If all available tasks are blocked, send a \`team_send_message\` to the lead to either resolve blockers or assign different work.
## Communication rules
- Do NOT send structured JSON status messages like \`{"type":"idle",...}\` or \`{"type":"task_completed",...}\`. Communicate in plain natural language.
- Do NOT use terminal tools (Bash, file readers) to inspect another teammate's session, inbox, or pane — always go through \`team_send_message\` and \`team_status\`.
- Members must NOT call \`delegate-task\` — its budget is zero inside team members. Use \`team_send_message\` to coordinate with peers instead.
## Lead-only tools
- \`team_create\` - create a team from a declaration.
- \`team_delete\` - remove a team.
- \`team_shutdown_request\` - start the shutdown flow.
## Lead or target-member shutdown tools
- \`team_approve_shutdown\` - approve shutdown for the targeted member.
- \`team_reject_shutdown\` - reject shutdown for the targeted member.
## Universal team-run tools
- \`team_send_message\` - send a direct message; broadcast is still lead-only.
- \`team_task_create\` - create a task for a member.
- \`team_task_list\` - list team tasks.
- \`team_task_update\` - update task state.
- \`team_task_get\` - inspect one task.
- \`team_status\` - show live team status.
## Global query tool
- \`team_list\` - list known teams.
## Bounds
- Max 8 members.
- Max 4 parallel workers.
- Max 32KB per message.
- Max 256KB unread inbox.
## Failure modes
- Broadcast is lead-only.
- No nested teams.
- No peer sync wait; work moves asynchronously.
## Notes
Team mode is a docs-only skill. The team_* tools are registered globally when \`team_mode.enabled=true\`.
Use \`~/.omo/teams/{name}/config.json\` plus worktree or tmux visibility to understand how the team is laid out.
`,
}