Skip to content

Commit a4d8331

Browse files
bradygasterclaude
andauthored
fix(plugin): ship an icon; restore the human-members template (#2493)
* fix(plugin): ship an icon; restore the human-members template - plugins/squad/.claude-plugin/icon.png: the Squad logo at 512 px, the one remaining `claude plugin validate` warning. The build script copies static files named by CLAUDE_PLUGIN_STATIC_FILES and checks them byte for byte; images are marked binary so the plugins/** LF rule never rewrites them. - .squad-templates/human-members.md: listed in the template index but missing since the #2030 recast. Restored, with the sign-off gate wording from the coordinator digest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(gitattributes): unset eol for images so the shebang lint skips the icon `binary` turns off text handling but keeps eol=lf from the plugins/** rule, which the shebang EOL lint reads, so it flagged the PNG as a CRLF blob. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 1a7ff45 commit a4d8331

12 files changed

Lines changed: 387 additions & 7 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@bradygaster/squad-sdk": patch
3+
"@bradygaster/squad-cli": patch
4+
---
5+
6+
The Claude Code plugin ships an icon, so `claude plugin validate` reports no warnings, and `.squad/templates/human-members.md`, listed in the template index but missing, ships again with the human sign-off gate.

‎.gitattributes‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ scripts/release/manifest.schema.json text eol=lf
6868
# no extension. Pin LF so Windows checkouts neither break it nor drift (#2443).
6969
plugins/** text eol=lf
7070
.claude-plugin/** text eol=lf
71+
# Images are binary, including inside plugins/** above (the plugin icon): never convert line endings.
72+
*.png binary -eol
73+
*.jpg binary -eol
74+
*.gif binary -eol

‎.squad-templates/human-members.md‎

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Human Members
2+
3+
Reference for adding human team members to the Squad roster.
4+
5+
## Triggers
6+
7+
The coordinator enters the human-member flow when:
8+
9+
- User says **"add {name} as {role}"** or **"add a human for {domain}"**
10+
- User says **"make {name} a reviewer"**
11+
- Routing detects a human member as the target for work
12+
13+
## Comparison: AI Agent vs. Human Member
14+
15+
| | AI Agent | Human Member |
16+
|---|----------|-------------|
17+
| Badge | Role-specific emoji | 👤 Human |
18+
| Charter | ✅ | ❌ |
19+
| History | ✅ | ❌ |
20+
| Spawned as sub-agent | ✅ | ❌ |
21+
| Can review work | ✅ | ✅ |
22+
| Casting (fictional name) | ✅ | ❌ (real name) |
23+
24+
## Adding a Human Member
25+
26+
Follow these steps:
27+
28+
1. **Use real name** — no casting. Badge: 👤 Human.
29+
2. **Add to team.md roster:**
30+
```markdown
31+
| {Name} | {Role} | — | 👤 Human |
32+
```
33+
3. **Add routing entries to routing.md:**
34+
```markdown
35+
| {domain} | {Name} 👤 | {example tasks} |
36+
```
37+
4. **Do not create** `charter.md` or `history.md` for humans.
38+
5. **Do not add** to casting registry.
39+
40+
## Routing to a Human
41+
42+
When work routes to a human member, the coordinator pauses. It never does that work itself or hands it to an AI member.
43+
44+
**Sign-off gates.** When `team.md`, `routing.md` or a decision says a human must approve or sign off on a kind of work (a release, version bump, tag, publish, merge or deploy), a request to do that work is not the sign-off. Before any gated step the coordinator names the rule and the human, asks whether the user is that human or has their explicit sign-off, proceeds only on an explicit yes, and records it in the decisions inbox. In a non-interactive run it stops at the gate and reports what is waiting and for whom.
45+
46+
47+
1. **Present the work to the user:**
48+
```
49+
"{Name} needs to review this. [Context about what needs review]"
50+
```
51+
2. **Non-dependent work continues immediately** — other agents proceed with unrelated tasks.
52+
3. **Stale reminder** after >1 turn without response:
53+
```
54+
"📌 Still waiting on {Name} for {thing}."
55+
```
56+
57+
## Reviewing as a Human
58+
59+
- Humans can serve as reviewers in the review process.
60+
- **Rejection lockout applies normally** — original author is locked out when reviewer rejects.
61+
- User relays the human's verdict to the coordinator ("approved" or "rejected with changes").
62+
63+
## Decision Framework: When to Add a Human Member
64+
65+
| Scenario | Add to roster? | Why |
66+
|----------|---------------|-----|
67+
| Approves architecture decisions before implementation | ✅ Yes | Decision gate — team routes and waits |
68+
| Reviews all docs PRs as standing reviewer | ✅ Yes | Recurring review gate |
69+
| Occasionally reviews PRs when tagged | ❌ No | Use @mention on the PR |
70+
| Files issues and contributes code | ❌ No | Normal GitHub collaboration |
71+
| Makes final ship/no-ship call | ✅ Yes | Approval gate |
72+
73+
**Litmus test:** If you want agents to *stop and wait* for someone's input before proceeding on a specific type of work, add them. If they review asynchronously via GitHub, don't bother.
74+
75+
**Who is driving:** `git config user.name` tells the team who is probably at the keyboard. It is a hint, not a sign-off: for a gated step the coordinator still asks. Adding yourself to the roster is optional; it formalizes routing and tracking.
76+
77+
## Removing a Human
78+
79+
Same as removing any member:
80+
81+
1. Move entry to alumni section in team.md.
82+
2. Remove from active roster.
83+
3. Update routing.md to remove all routing entries.

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ Claude Code is the first host after GitHub Copilot where Squad uses the host's o
8080
- `squad doctor` flags machine-specific launch commands and a missing launcher. Run `squad upgrade` once to fix an existing repository.
8181
- **Coordinators wait for a human's sign-off.** When `team.md` or `routing.md` says a human must sign off on work (a release, tag, publish, merge or deploy), the Claude, Kiro, OpenCode, Cursor, Codex and Antigravity coordinators no longer treat the request itself as the sign-off. They name the rule and the human, ask whether you are that human or have their sign-off, go ahead only on an explicit yes, and record it in the decisions inbox. In a non-interactive run they stop at the gate. Seen in a live test: asked to "cut the 1.0.0 release", the Claude coordinator used to bump, tag and run `npm publish` itself.
8282
- `squad_state` works when the team folder is reached through a symlink, such as macOS `/tmp` (really `/private/tmp`) or a linked team root. Squad stores the folder's resolved path but was given paths in the original spelling, so it refused reads and writes there with "Path traversal blocked". Paths that really leave the team folder, including through a symlink inside it, are still refused.
83+
- The Claude Code plugin has an icon (the Squad logo, 512 px), so `claude plugin validate` reports no warnings. Images are marked binary in `.gitattributes`, so the plugin folder's LF rule never rewrites them.
84+
- `.squad/templates/human-members.md` ships again. The template index listed it, but it had been missing since 2025. It describes adding a human to the roster and now includes the sign-off gate: a request to do gated work is not the sign-off.
8385
- `squad doctor` no longer fails the `squad_state` bridge check when the team folder has two spellings, such as macOS `/var` and `/private/var`. It compares the real paths.
8486
- When `squad_state` is unavailable and `.squad/config.json` sets a non-local `stateBackend`, the non-Copilot coordinators stop and say so instead of writing `.squad/decisions.md` directly, which that backend never reads. Seen in a live test with `two-layer`.
8587
- `squad init --client claude` tells you to run `claude --agent squad --mcp-config .mcp.json`, not Copilot. With several hosts it gives one line per host (#2453).
147 KB
Loading
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Human Members
2+
3+
Reference for adding human team members to the Squad roster.
4+
5+
## Triggers
6+
7+
The coordinator enters the human-member flow when:
8+
9+
- User says **"add {name} as {role}"** or **"add a human for {domain}"**
10+
- User says **"make {name} a reviewer"**
11+
- Routing detects a human member as the target for work
12+
13+
## Comparison: AI Agent vs. Human Member
14+
15+
| | AI Agent | Human Member |
16+
|---|----------|-------------|
17+
| Badge | Role-specific emoji | 👤 Human |
18+
| Charter | ✅ | ❌ |
19+
| History | ✅ | ❌ |
20+
| Spawned as sub-agent | ✅ | ❌ |
21+
| Can review work | ✅ | ✅ |
22+
| Casting (fictional name) | ✅ | ❌ (real name) |
23+
24+
## Adding a Human Member
25+
26+
Follow these steps:
27+
28+
1. **Use real name** — no casting. Badge: 👤 Human.
29+
2. **Add to team.md roster:**
30+
```markdown
31+
| {Name} | {Role} | — | 👤 Human |
32+
```
33+
3. **Add routing entries to routing.md:**
34+
```markdown
35+
| {domain} | {Name} 👤 | {example tasks} |
36+
```
37+
4. **Do not create** `charter.md` or `history.md` for humans.
38+
5. **Do not add** to casting registry.
39+
40+
## Routing to a Human
41+
42+
When work routes to a human member, the coordinator pauses. It never does that work itself or hands it to an AI member.
43+
44+
**Sign-off gates.** When `team.md`, `routing.md` or a decision says a human must approve or sign off on a kind of work (a release, version bump, tag, publish, merge or deploy), a request to do that work is not the sign-off. Before any gated step the coordinator names the rule and the human, asks whether the user is that human or has their explicit sign-off, proceeds only on an explicit yes, and records it in the decisions inbox. In a non-interactive run it stops at the gate and reports what is waiting and for whom.
45+
46+
47+
1. **Present the work to the user:**
48+
```
49+
"{Name} needs to review this. [Context about what needs review]"
50+
```
51+
2. **Non-dependent work continues immediately** — other agents proceed with unrelated tasks.
52+
3. **Stale reminder** after >1 turn without response:
53+
```
54+
"📌 Still waiting on {Name} for {thing}."
55+
```
56+
57+
## Reviewing as a Human
58+
59+
- Humans can serve as reviewers in the review process.
60+
- **Rejection lockout applies normally** — original author is locked out when reviewer rejects.
61+
- User relays the human's verdict to the coordinator ("approved" or "rejected with changes").
62+
63+
## Decision Framework: When to Add a Human Member
64+
65+
| Scenario | Add to roster? | Why |
66+
|----------|---------------|-----|
67+
| Approves architecture decisions before implementation | ✅ Yes | Decision gate — team routes and waits |
68+
| Reviews all docs PRs as standing reviewer | ✅ Yes | Recurring review gate |
69+
| Occasionally reviews PRs when tagged | ❌ No | Use @mention on the PR |
70+
| Files issues and contributes code | ❌ No | Normal GitHub collaboration |
71+
| Makes final ship/no-ship call | ✅ Yes | Approval gate |
72+
73+
**Litmus test:** If you want agents to *stop and wait* for someone's input before proceeding on a specific type of work, add them. If they review asynchronously via GitHub, don't bother.
74+
75+
**Who is driving:** `git config user.name` tells the team who is probably at the keyboard. It is a hint, not a sign-off: for a gated step the coordinator still asks. Adding yourself to the roster is optional; it formalizes routing and tracking.
76+
77+
## Removing a Human
78+
79+
Same as removing any member:
80+
81+
1. Move entry to alumni section in team.md.
82+
2. Remove from active roster.
83+
3. Update routing.md to remove all routing entries.

‎packages/squad-sdk/src/client/adapters/claude-plugin.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ export const CLAUDE_PLUGIN_AGENT = `${CLAUDE_PLUGIN_NAME}:squad`;
2727
export const CLAUDE_PLUGIN_MCP_PREFIX = `mcp__plugin_${CLAUDE_PLUGIN_NAME}_squad_state__`;
2828
/** Where the generated plugin lives in the Squad repository; the marketplace points here. */
2929
export const CLAUDE_PLUGIN_SOURCE_DIR = 'plugins/squad';
30+
31+
/**
32+
* Binary files the plugin ships as they are, keyed by their path in the plugin, valued by their
33+
* source in the repository. `scripts/build-claude-plugin.mjs` copies them; they are not generated.
34+
* The icon (512 px square PNG) is the one `claude plugin validate` looks for at `.claude-plugin/icon.png`.
35+
*/
36+
export const CLAUDE_PLUGIN_STATIC_FILES: Readonly<Record<string, string>> = {
37+
'.claude-plugin/icon.png': 'docs/src/assets/images/squad-plugin-icon.png',
38+
};
3039
/** Marketplace name: users install `squad@squad`. */
3140
export const CLAUDE_MARKETPLACE_NAME = 'squad';
3241

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Human Members
2+
3+
Reference for adding human team members to the Squad roster.
4+
5+
## Triggers
6+
7+
The coordinator enters the human-member flow when:
8+
9+
- User says **"add {name} as {role}"** or **"add a human for {domain}"**
10+
- User says **"make {name} a reviewer"**
11+
- Routing detects a human member as the target for work
12+
13+
## Comparison: AI Agent vs. Human Member
14+
15+
| | AI Agent | Human Member |
16+
|---|----------|-------------|
17+
| Badge | Role-specific emoji | 👤 Human |
18+
| Charter | ✅ | ❌ |
19+
| History | ✅ | ❌ |
20+
| Spawned as sub-agent | ✅ | ❌ |
21+
| Can review work | ✅ | ✅ |
22+
| Casting (fictional name) | ✅ | ❌ (real name) |
23+
24+
## Adding a Human Member
25+
26+
Follow these steps:
27+
28+
1. **Use real name** — no casting. Badge: 👤 Human.
29+
2. **Add to team.md roster:**
30+
```markdown
31+
| {Name} | {Role} | — | 👤 Human |
32+
```
33+
3. **Add routing entries to routing.md:**
34+
```markdown
35+
| {domain} | {Name} 👤 | {example tasks} |
36+
```
37+
4. **Do not create** `charter.md` or `history.md` for humans.
38+
5. **Do not add** to casting registry.
39+
40+
## Routing to a Human
41+
42+
When work routes to a human member, the coordinator pauses. It never does that work itself or hands it to an AI member.
43+
44+
**Sign-off gates.** When `team.md`, `routing.md` or a decision says a human must approve or sign off on a kind of work (a release, version bump, tag, publish, merge or deploy), a request to do that work is not the sign-off. Before any gated step the coordinator names the rule and the human, asks whether the user is that human or has their explicit sign-off, proceeds only on an explicit yes, and records it in the decisions inbox. In a non-interactive run it stops at the gate and reports what is waiting and for whom.
45+
46+
47+
1. **Present the work to the user:**
48+
```
49+
"{Name} needs to review this. [Context about what needs review]"
50+
```
51+
2. **Non-dependent work continues immediately** — other agents proceed with unrelated tasks.
52+
3. **Stale reminder** after >1 turn without response:
53+
```
54+
"📌 Still waiting on {Name} for {thing}."
55+
```
56+
57+
## Reviewing as a Human
58+
59+
- Humans can serve as reviewers in the review process.
60+
- **Rejection lockout applies normally** — original author is locked out when reviewer rejects.
61+
- User relays the human's verdict to the coordinator ("approved" or "rejected with changes").
62+
63+
## Decision Framework: When to Add a Human Member
64+
65+
| Scenario | Add to roster? | Why |
66+
|----------|---------------|-----|
67+
| Approves architecture decisions before implementation | ✅ Yes | Decision gate — team routes and waits |
68+
| Reviews all docs PRs as standing reviewer | ✅ Yes | Recurring review gate |
69+
| Occasionally reviews PRs when tagged | ❌ No | Use @mention on the PR |
70+
| Files issues and contributes code | ❌ No | Normal GitHub collaboration |
71+
| Makes final ship/no-ship call | ✅ Yes | Approval gate |
72+
73+
**Litmus test:** If you want agents to *stop and wait* for someone's input before proceeding on a specific type of work, add them. If they review asynchronously via GitHub, don't bother.
74+
75+
**Who is driving:** `git config user.name` tells the team who is probably at the keyboard. It is a hint, not a sign-off: for a gated step the coordinator still asks. Adding yourself to the roster is optional; it formalizes routing and tracking.
76+
77+
## Removing a Human
78+
79+
Same as removing any member:
80+
81+
1. Move entry to alumni section in team.md.
82+
2. Remove from active roster.
83+
3. Update routing.md to remove all routing entries.
147 KB
Loading

‎scripts/build-claude-plugin.mjs‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ if (!fs.existsSync(SDK)) {
2525
console.error(`build-claude-plugin: ${path.relative(ROOT, SDK)} is missing; run \`npm run build\` first.`);
2626
process.exit(1);
2727
}
28-
const { buildClaudePlugin, buildClaudeMarketplace, CLAUDE_PLUGIN_SOURCE_DIR } = await import(pathToFileURL(SDK).href);
28+
const { buildClaudePlugin, buildClaudeMarketplace, CLAUDE_PLUGIN_SOURCE_DIR, CLAUDE_PLUGIN_STATIC_FILES } = await import(pathToFileURL(SDK).href);
2929

3030
const args = process.argv.slice(2);
3131
const check = args.includes('--check');
@@ -56,21 +56,27 @@ function listTree(dir, rel = '') {
5656
}
5757

5858
const wanted = new Map(files.map((f) => [f.path, f]));
59+
// Binary files shipped as they are (the icon): copied from the repository, compared byte for byte.
60+
const statics = Object.entries(CLAUDE_PLUGIN_STATIC_FILES).map(([rel, src]) => ({ rel, bytes: fs.readFileSync(path.join(ROOT, src)) }));
5961
const stale = [];
60-
for (const rel of listTree(pluginDir)) if (!wanted.has(rel)) stale.push(rel);
62+
for (const rel of listTree(pluginDir)) if (!wanted.has(rel) && !(rel in CLAUDE_PLUGIN_STATIC_FILES)) stale.push(rel);
6163
const changed = [];
6264
for (const f of files) {
6365
const abs = path.join(pluginDir, f.path);
6466
const current = fs.existsSync(abs) ? fs.readFileSync(abs, 'utf8') : undefined;
6567
if (current !== f.content) changed.push(f.path);
6668
}
69+
for (const { rel, bytes } of statics) {
70+
const abs = path.join(pluginDir, rel);
71+
if (!fs.existsSync(abs) || !fs.readFileSync(abs).equals(bytes)) changed.push(rel);
72+
}
6773
const marketplaceStale = !fs.existsSync(marketplacePath) || fs.readFileSync(marketplacePath, 'utf8') !== marketplace;
6874

6975
if (check) {
7076
const problems = [...changed.map((p) => `changed: ${p}`), ...stale.map((p) => `extra: ${p}`)];
7177
if (marketplaceStale) problems.push('changed: .claude-plugin/marketplace.json');
7278
// Marketplace installs clone the repository: a generated file git ignores never reaches users.
73-
const ignored = spawnSync('git', ['check-ignore', '--no-index', '--', ...files.map((f) => path.join(CLAUDE_PLUGIN_SOURCE_DIR, f.path))], {
79+
const ignored = spawnSync('git', ['check-ignore', '--no-index', '--', ...[...files.map((f) => f.path), ...statics.map((x) => x.rel)].map((p) => path.join(CLAUDE_PLUGIN_SOURCE_DIR, p))], {
7480
cwd: ROOT,
7581
encoding: 'utf8',
7682
});
@@ -90,8 +96,13 @@ for (const f of files) {
9096
fs.writeFileSync(abs, f.content, 'utf8');
9197
if (f.executable) fs.chmodSync(abs, 0o755);
9298
}
99+
for (const { rel, bytes } of statics) {
100+
const abs = path.join(pluginDir, rel);
101+
fs.mkdirSync(path.dirname(abs), { recursive: true });
102+
fs.writeFileSync(abs, bytes);
103+
}
93104
fs.mkdirSync(path.dirname(marketplacePath), { recursive: true });
94105
fs.writeFileSync(marketplacePath, marketplace, 'utf8');
95106
console.log(
96-
`build-claude-plugin: wrote ${files.length} files to ${CLAUDE_PLUGIN_SOURCE_DIR} (${changed.length} changed, ${stale.length} removed), version ${version}.`,
107+
`build-claude-plugin: wrote ${files.length + statics.length} files to ${CLAUDE_PLUGIN_SOURCE_DIR} (${changed.length} changed, ${stale.length} removed), version ${version}.`,
97108
);

0 commit comments

Comments
 (0)