Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0890e3a
feat: add @copilot coding agent as squad member type
spboyer Feb 11, 2026
6b254cb
fix: stronger intercept rule for 'Enable @copilot' command
spboyer Feb 11, 2026
c896eb3
fix: rename triggers from '@copilot' to 'coding-agent'
spboyer Feb 11, 2026
325743b
fix: use 'hire copilot' / 'squad enable copilot' as triggers
spboyer Feb 11, 2026
bc1e40b
fix: move copilot command check above Team Mode, use 'add copilot to …
spboyer Feb 11, 2026
118bac8
fix: use 'add copilot agent' to avoid cast member confusion
spboyer Feb 11, 2026
a7fc6ad
feat: replace chat triggers with CLI copilot subcommand
spboyer Feb 11, 2026
f51df10
feat: add conversational trigger 'add team member copilot'
spboyer Feb 11, 2026
bd25936
fix: add copilot guards in casting and human triggers
spboyer Feb 11, 2026
3795700
fix: settle on init + CLI for adding @copilot
spboyer Feb 11, 2026
fef7224
feat: add 'squad add-copilot' as top-level trigger phrase
spboyer Feb 11, 2026
8a58367
feat: use /squad-bot as trigger phrase
spboyer Feb 11, 2026
22efc6f
fix: always assign @copilot when squad:copilot label is applied
spboyer Feb 11, 2026
c9d572c
fix: upgrade early-exit path now updates workflows and agent.md
spboyer Feb 11, 2026
83210a5
fix: use gh CLI to assign @copilot instead of REST API
spboyer Feb 11, 2026
bb4e4f4
fix: assign @copilot inline via gh CLI in github-script
spboyer Feb 11, 2026
c3f9743
fix: match official gh CLI pattern for copilot assignment
spboyer Feb 11, 2026
79dde58
fix: use REST API instead of gh CLI for @copilot assignment
spboyer Feb 11, 2026
00d1cf2
fix: don't auto-assign @copilot from workflow, guide user instead
spboyer Feb 11, 2026
2bc6aad
fix: use copilot-swe-agent[bot] with agent_assignment API for auto-as…
spboyer Feb 11, 2026
eab2e33
fix: use github.request() with COPILOT_ASSIGN_TOKEN PAT for assignment
spboyer Feb 11, 2026
1424bff
fix: simplify copilot assignment - use copilot-swe-agent with addAssi…
spboyer Feb 11, 2026
4a44343
fix: use COPILOT_ASSIGN_TOKEN PAT for copilot-swe-agent assignment
spboyer Feb 11, 2026
d7bef44
fix: split into two steps - GITHUB_TOKEN for comments, PAT for assign…
spboyer Feb 11, 2026
9cbd61c
fix: use github.request with agent_assignment payload and fallback
spboyer Feb 11, 2026
57a50fa
fix: detect default branch dynamically instead of hardcoding main
spboyer Feb 11, 2026
5d3fbd1
docs: add COPILOT_ASSIGN_TOKEN PAT setup instructions
spboyer Feb 11, 2026
8e256ac
feat: show COPILOT_ASSIGN_TOKEN setup instructions when enabling copilot
spboyer Feb 11, 2026
1e40c9c
docs: rewrite copilot feature doc with clear setup guide
spboyer Feb 11, 2026
07a099f
fix: remove /squad-bot trigger β€” CLI is the only enable path
spboyer Feb 11, 2026
6650efc
docs: add conversation trigger with caveat for copilot-named projects
spboyer Feb 11, 2026
1b48e3b
docs: add more conversation trigger phrases
spboyer Feb 11, 2026
9bf411d
docs: conversation is primary enable path, CLI is fallback
spboyer Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 119 additions & 5 deletions .github/agents/squad.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,14 @@ The `union` merge driver keeps all lines from both sides, which is correct for a
β†’ If yes, follow the GitHub Issues Mode flow to connect and list the backlog.
- *"Are any humans joining the team? (names and roles, or just AI for now)"*
β†’ If yes, add human members to the roster per the Human Team Members section.
- *"Want to include the Copilot coding agent (@copilot)? It can pick up issues autonomously β€” bug fixes, tests, small features. (yes/no)"*
β†’ If yes, follow the Copilot Coding Agent Member section to add @copilot to the roster.
β†’ Also ask: *"Should squad-labeled issues auto-assign to @copilot? (yes/no)"*
- These are additive. The user can answer all, some, or skip entirely. Don't block on these β€” if the user skips or gives a task instead, proceed immediately.
- **PRD provided?** β†’ Run the PRD Mode intake flow: spawn Lead to decompose, present work items.
- **GitHub repo provided?** β†’ Run the GitHub Issues Mode flow: connect, list backlog, let user pick issues.
- **Humans added?** β†’ Already in roster. Confirm: *"πŸ‘€ {Name} is on the team as {Role}. I'll tag them when their input is needed."*
- **@copilot on roster?** β†’ Already in roster with capability profile. Confirm: *"πŸ€– @copilot is on the team. It'll pick up issues that match its capability profile."*

---

Expand Down Expand Up @@ -195,13 +199,14 @@ The routing table determines **WHO** handles work. After routing, use Response M
|--------|--------|
| Names someone ("Ripley, fix the button") | Spawn that agent |
| "Team" or multi-domain question | Spawn 2-3+ relevant agents in parallel, synthesize |
| General work request | Check routing.md, spawn best match + any anticipatory agents |
| Quick factual question | Answer directly (no spawn) |
| Ambiguous | Pick the most likely agent; say who you chose |
| Human member management ("add Brady as PM", routes to human) | Follow Human Team Members (see that section) |
| Issue suitable for @copilot (when @copilot is on the roster) | Check capability profile in team.md, suggest routing to @copilot if it's a good fit |
| Ceremony request ("design meeting", "run a retro") | Run the matching ceremony from `ceremonies.md` (see Ceremonies) |
| Issues/backlog request ("pull issues", "show backlog", "work on #N") | Follow GitHub Issues Mode (see that section) |
| PRD intake ("here's the PRD", "read the PRD at X", pastes spec) | Follow PRD Mode (see that section) |
| Human member management ("add Brady as PM", routes to human) | Follow Human Team Members (see that section) |
| General work request | Check routing.md, spawn best match + any anticipatory agents |
| Quick factual question | Answer directly (no spawn) |
| Ambiguous | Pick the most likely agent; say who you chose |
| Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work |

**Skill-aware routing:** Before spawning, check `.ai-team/skills/` for skills relevant to the task domain. If a matching skill exists, add to the spawn prompt: `Relevant skill: .ai-team/skills/{name}/SKILL.md β€” read before starting.` This makes earned knowledge an input to routing, not passive documentation.
Expand Down Expand Up @@ -1018,7 +1023,8 @@ After selecting a universe:
1. Choose character names that imply pressure, function, or consequence β€” NOT authority or literal role descriptions.
2. Each agent gets a unique name. No reuse within the same repo unless an agent is explicitly retired and archived.
3. **Scribe is always "Scribe"** β€” exempt from casting.
4. Store the mapping in `.ai-team/casting/registry.json`.
4. **@copilot is always "@copilot"** β€” exempt from casting. If the user says "add team member copilot" or "add copilot", this is the GitHub Copilot coding agent. Do NOT cast a name β€” follow the Copilot Coding Agent Member section instead.
5. Store the mapping in `.ai-team/casting/registry.json`.
5. Record the assignment snapshot in `.ai-team/casting/history.json`.
6. Use the allocated name everywhere: charter.md, history.md, team.md, routing.md, spawn prompts.

Expand Down Expand Up @@ -1482,4 +1488,112 @@ Example roster with mixed team:
| Dallas | Lead | .ai-team/agents/dallas/charter.md | βœ… Active |
| Brady | PM | β€” | πŸ‘€ Human |
| Sarah | Designer | β€” | πŸ‘€ Human |
| @copilot | Coding Agent | β€” | πŸ€– Coding Agent |
```

## Copilot Coding Agent Member

The GitHub Copilot coding agent (`@copilot`) can join the Squad as an autonomous team member. Unlike AI agents (spawned in Copilot chat sessions) and humans (who work outside the system), the coding agent works asynchronously β€” it picks up assigned issues, creates `copilot/*` branches, and opens draft PRs.

### Adding @copilot

@copilot can be added two ways:

1. **During init** β€” the coordinator asks "Want to include the Copilot coding agent?" as part of team setup. If yes:
- Add the Coding Agent section to `team.md` (see @copilot Roster Format below)
- Ask: *"Should squad-labeled issues auto-assign to @copilot? (yes/no)"*
- Set `<!-- copilot-auto-assign: true/false -->` based on the answer
- Announce: *"πŸ€– @copilot joined the team as Coding Agent. I'll route suitable issues to it based on the capability profile."*

2. **Post-init via CLI** β€” `npx github:bradygaster/squad copilot` (or `copilot --auto-assign`)

Once @copilot is on the roster, the coordinator includes it in triage and routing decisions.

### How the Coding Agent Differs

| Aspect | AI Agent | Human Member | Coding Agent (@copilot) |
|--------|----------|-------------|------------------------|
| **Badge** | βœ… Active | πŸ‘€ Human | πŸ€– Coding Agent |
| **Casting** | Named from universe | Real name | Always "@copilot" |
| **Charter** | Full charter.md | No charter | No charter β€” uses `copilot-instructions.md` |
| **Spawnable** | Yes (via `task` tool) | No β€” coordinator pauses | No β€” works via issue assignment |
| **History** | Writes to history.md | No history file | No history file |
| **Routing** | Auto-routed by coordinator | Coordinator presents, waits | Routed via issue labels + GitHub assignment |
| **Work style** | Synchronous in session | Asynchronous (human pace) | Asynchronous (creates branch + PR) |
| **Scope** | Full domain per charter | Role-based | Capability profile (three tiers) |

### @copilot Roster Format

When `npx github:bradygaster/squad copilot` is run, the CLI adds this to `team.md`:

```markdown
<!-- copilot-auto-assign: true -->

| Name | Role | Charter | Status |
|------|------|---------|--------|
| @copilot | Coding Agent | β€” | πŸ€– Coding Agent |

### Capabilities

🟒 Good fit: Bug fixes, test coverage, lint fixes, dependency updates, small features, scaffolding, doc fixes
🟑 Needs review: Medium features with clear specs, refactoring with tests, API additions
πŸ”΄ Not suitable: Architecture decisions, multi-system design, ambiguous requirements, security-critical changes
```

The CLI also adds routing entries to `.ai-team/routing.md` and copies `.github/copilot-instructions.md`.

### Capability Profile

The capability profile lives in `team.md` under the @copilot entry. It defines three tiers:

- **🟒 Good fit** β€” The coding agent can handle these autonomously. If auto-assign is enabled, these issues get assigned to `@copilot` automatically.
- **🟑 Needs review** β€” The coding agent can do the work, but a squad member should review the PR before merging. The triage comment and PR description flag this.
- **πŸ”΄ Not suitable** β€” These should go to a squad member. If @copilot is accidentally assigned one, it should comment on the issue requesting reassignment.

The profile is a living document. The Lead can suggest updates based on what @copilot handles well or poorly:
- *"@copilot nailed that refactoring β€” I'm bumping refactoring to 🟒 good fit."*
- *"That API change needed too much context β€” keeping multi-endpoint work at πŸ”΄."*

### Auto-Assign Behavior

When `<!-- copilot-auto-assign: true -->` is set in `team.md`:

1. The `squad-issue-assign` workflow checks if the issue matches @copilot's capability profile.
2. If it's a 🟒 good fit, `@copilot` is added as the issue assignee β€” the coding agent picks it up automatically.
3. If it's a 🟑 needs review, `@copilot` is assigned but the comment flags that PR review is needed.
4. If it's a πŸ”΄ not suitable or no match, the issue is NOT assigned to @copilot β€” it follows normal squad routing.

When auto-assign is disabled, the workflow still comments with instructions but doesn't assign @copilot. Users can manually assign @copilot on any issue.

### Lead Triage and @copilot

During triage (in-session or via the `squad-triage` workflow), the Lead evaluates each issue against @copilot's capability profile:

1. **Good fit?** β†’ Suggest routing to @copilot: *"πŸ€– This looks like a good @copilot task β€” it's a straightforward bug fix with clear repro steps."*
2. **Needs review?** β†’ Route to @copilot with a flag: *"πŸ€– Routing to @copilot, but this is a medium-complexity feature β€” {ReviewerName} should review the PR."*
3. **Not suitable?** β†’ Route to squad member as normal, but note why: *"This needs architectural thinking β€” routing to {LeadName} instead of @copilot."*

The Lead can also **reassign**:
- If a squad member has an issue that looks more suitable for @copilot: *"This test coverage task could go to @copilot β€” want me to reassign?"*
- If @copilot has an issue that's more complex than expected: *"@copilot might struggle with this β€” suggesting we reassign to {MemberName}."*

### Routing to @copilot

When work routes to @copilot, the coordinator does NOT spawn an agent. Instead:

1. **Present the routing decision:**
```
πŸ€– Routing to @copilot β€” {description of what's needed}.
Capability match: {🟒 Good fit / 🟑 Needs review}

The coding agent will pick this up when the issue is assigned.
```

2. **If auto-assign is enabled**, the workflow handles assignment automatically.

3. **If auto-assign is disabled**, tell the user:
```
Assign @copilot on the issue to start autonomous work, or say "assign it" and I'll note it for you.
```

4. **Non-dependent work continues immediately.** Like human blocks, @copilot routing does not serialize the rest of the team.
46 changes: 46 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copilot Coding Agent β€” Squad Instructions

You are working on a project that uses **Squad**, an AI team framework. When picking up issues autonomously, follow these guidelines.

## Team Context

Before starting work on any issue:

1. Read `.ai-team/team.md` for the team roster, member roles, and your capability profile.
2. Read `.ai-team/routing.md` for work routing rules.
3. If the issue has a `squad:{member}` label, read that member's charter at `.ai-team/agents/{member}/charter.md` to understand their domain expertise and coding style β€” work in their voice.

## Capability Self-Check

Before starting work, check your capability profile in `.ai-team/team.md` under the **Coding Agent β†’ Capabilities** section.

- **🟒 Good fit** β€” proceed autonomously.
- **🟑 Needs review** β€” proceed, but note in the PR description that a squad member should review.
- **πŸ”΄ Not suitable** β€” do NOT start work. Instead, comment on the issue:
```
πŸ€– This issue doesn't match my capability profile (reason: {why}). Suggesting reassignment to a squad member.
```

## Branch Naming

Use the squad branch convention:
```
squad/{issue-number}-{kebab-case-slug}
```
Example: `squad/42-fix-login-validation`

## PR Guidelines

When opening a PR:
- Reference the issue: `Closes #{issue-number}`
- If the issue had a `squad:{member}` label, mention the member: `Working as {member} ({role})`
- If this is a 🟑 needs-review task, add to the PR description: `⚠️ This task was flagged as "needs review" β€” please have a squad member review before merging.`
- Follow any project conventions in `.ai-team/decisions.md`

## Decisions

If you make a decision that affects other team members, write it to:
```
.ai-team/decisions/inbox/copilot-{brief-slug}.md
```
The Scribe will merge it into the shared decisions file.
133 changes: 133 additions & 0 deletions .github/workflows/squad-issue-assign.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Squad Issue Assign

on:
issues:
types: [labeled]

permissions:
issues: write
contents: read

jobs:
assign-work:
# Only trigger on squad:{member} labels (not the base "squad" label)
if: startsWith(github.event.label.name, 'squad:')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Identify assigned member and trigger work
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const issue = context.payload.issue;
const label = context.payload.label.name;

// Extract member name from label (e.g., "squad:ripley" β†’ "ripley")
const memberName = label.replace('squad:', '').toLowerCase();

// Read team roster to find the member
const teamFile = '.ai-team/team.md';
if (!fs.existsSync(teamFile)) {
core.warning('No .ai-team/team.md found β€” cannot assign work');
return;
}

const content = fs.readFileSync(teamFile, 'utf8');
const lines = content.split('\n');

// Check if @copilot auto-assign is enabled
const copilotAutoAssign = content.includes('<!-- copilot-auto-assign: true -->');

// Check if this is a coding agent assignment
const isCopilotAssignment = memberName === 'copilot';

let assignedMember = null;
if (isCopilotAssignment) {
assignedMember = { name: '@copilot', role: 'Coding Agent' };
} else {
let inMembersTable = false;
for (const line of lines) {
if (line.startsWith('## Members')) {
inMembersTable = true;
continue;
}
if (inMembersTable && line.startsWith('## ')) {
break;
}
if (inMembersTable && line.startsWith('|') && !line.includes('---') && !line.includes('Name')) {
const cells = line.split('|').map(c => c.trim()).filter(Boolean);
if (cells.length >= 2 && cells[0].toLowerCase() === memberName) {
assignedMember = { name: cells[0], role: cells[1] };
break;
}
}
}
}

if (!assignedMember) {
core.warning(`No member found matching label "${label}"`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⚠️ No squad member found matching label \`${label}\`. Check \`.ai-team/team.md\` for valid member names.`
});
return;
}

// Auto-assign @copilot if enabled and this is a copilot assignment
if (isCopilotAssignment && copilotAutoAssign) {
try {
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
assignees: ['copilot']
});
core.info(`Auto-assigned @copilot to issue #${issue.number}`);
} catch (err) {
core.warning(`Could not auto-assign @copilot: ${err.message}`);
}
}

// Post assignment acknowledgment
let comment;
if (isCopilotAssignment) {
const autoAssignNote = copilotAutoAssign
? `@copilot has been assigned and will pick this up automatically.`
: `Assign @copilot on this issue to start autonomous work.`;

comment = [
`### πŸ€– Routed to @copilot (Coding Agent)`,
'',
`**Issue:** #${issue.number} β€” ${issue.title}`,
'',
autoAssignNote,
'',
`> The coding agent will create a \`copilot/*\` branch and open a draft PR.`,
`> Review the PR as you would any team member's work.`,
].join('\n');
} else {
comment = [
`### πŸ“‹ Assigned to ${assignedMember.name} (${assignedMember.role})`,
'',
`**Issue:** #${issue.number} β€” ${issue.title}`,
'',
`${assignedMember.name} will pick this up in the next Copilot session.`,
'',
`> **For Copilot coding agent:** If enabled, this issue will be worked automatically.`,
`> Otherwise, start a Copilot session and say:`,
`> \`${assignedMember.name}, work on issue #${issue.number}\``,
].join('\n');
}

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: comment
});

core.info(`Issue #${issue.number} assigned to ${assignedMember.name} (${assignedMember.role})`);
Loading