Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Claude Code is the first host after GitHub Copilot where Squad uses the host's o
- `squad doctor` flags machine-specific launch commands and a missing launcher. Run `squad upgrade` once to fix an existing repository.
- **Squad's own tools no longer prompt in Claude Code.** In a demo dry run, a fresh `squad init --client claude` project hit 13 permission prompts while casting the team, and the coordinator ended up hand-writing charters and asking the user to run `squad cast` themselves. `squad init` and `squad upgrade` now add a scoped allowlist to `.claude/settings.json`: the `squad_state` tools the coordinator uses, by name (never the experimental backend's approval tools), and `squad cast`, `squad client sync`, `squad lockout reject` and `squad lockout status` (the steps that release a lockout, `assign`, `submit`, `approve` and `override`, still ask), as `squad …` and, when `.mcp.json` runs Squad's own launcher, as `node .squad/squad.mjs …`. Nothing else is allowed, and `squad cast` now refuses to write through a symbolic link. Your own rules come first and are never touched; Squad records the rules it added in `.claude/.squad-permissions.json`, and rollback removes only those, and `"claudePermissions": "off"` in `.squad/config.json` turns it off. Claude Code applies project allow rules only after the folder is trusted (the trust prompt on your first interactive `claude` run there). `squad init` now says so, and if a cast is still denied the coordinator stops and asks instead of retrying variants or hand-writing the team.
- **`squad_decide` says where a git-branch backend stored the decision.** In a live run on the `orphan` backend, the coordinator told the user the decision was in `.squad/decisions/inbox/…`, but it was only on the `squad-state` branch. The tool result now says the path is a key on that branch, so the coordinator reports it correctly.
- **Claude Code gets only its own Init Mode skill.** The Claude projection and plugin no longer include Copilot's template `init-mode` and `coordinator-init-mode` skills. Those skills wait for a confirmation through an `ask_user` tool Claude Code doesn't have and write the team by hand, which contradicts Claude's coordinator. Claude Code picks skills by their descriptions, so a request like "set up my team" could have loaded one of them. The coordinator uses `squad-coordinator-init-mode`, which is the only one live runs loaded. A Squad-generated copy left in `.claude/skills/` is removed on the next `squad upgrade`. Copilot is unchanged.
- **The reviewer gate runs on its own and means it.** In the same dry run, the coordinator declared a bug fix done without the lead's review even though `routing.md` named the lead as the approval gate; when asked, the lead listed six known gaps against the issue and still approved; and a reviewer's own fix went unreviewed. The non-Copilot coordinators now send every code or test change to the reviewer before calling it done, unasked; the reviewer is never an author, judges against the issue and the team's recorded decisions (a known gap against either is a REJECT, not an advisory), and code a reviewer writes needs a different reviewer. The coordinator also names a recorded decision when it shapes the work ("Per the team decision of <date>: …").
- **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.
- `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.
Expand Down
3 changes: 2 additions & 1 deletion packages/squad-sdk/src/client/adapters/claude-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { buildCoordinatorInstructions, claudeCoordinatorSkills } from './coordinator.js';
import { CLAUDE_HOOK_EVENTS, claudeHookMatcher, type ClaudeHookEvent } from './claude-hooks.js';
import { CLAUDE_SKIPPED_TEMPLATE_SKILLS } from './claude-skills.js';
export { SQUAD_REPO_LAUNCHER_BOOTSTRAP, SQUAD_REPO_LAUNCHER_PATH, squadRepoLauncherArgs, squadRepoLauncherScript } from './repo-launcher.js';

/** Plugin name: the namespace of its agents, skills, commands and MCP server. */
Expand Down Expand Up @@ -588,7 +589,7 @@ export function buildClaudePlugin(opts: BuildClaudePluginOptions): ClaudePluginF
}
const templates = fs
.readdirSync(opts.templateSkillsDir, { withFileTypes: true })
.filter((e) => e.isDirectory() && NAME_RE.test(e.name) && !skillNames.has(e.name))
.filter((e) => e.isDirectory() && NAME_RE.test(e.name) && !skillNames.has(e.name) && !CLAUDE_SKIPPED_TEMPLATE_SKILLS.has(e.name))
.map((e) => e.name)
.sort();
for (const name of templates) {
Expand Down
10 changes: 9 additions & 1 deletion packages/squad-sdk/src/client/adapters/claude-skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export const CLAUDE_SKILLS_DIR = '.claude/skills';

const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;

/**
* Template skills never projected for Claude Code. They are Copilot's Init Mode: wait for a
* confirmation through an `ask_user` tool Claude Code lacks, and write the team by hand. Claude's
* coordinator uses `squad-coordinator-init-mode` instead (auto-confirm, `squad cast`, stop on a
* denied cast), and a request like "set up my team" could otherwise load the wrong one.
*/
export const CLAUDE_SKIPPED_TEMPLATE_SKILLS: ReadonlySet<string> = new Set(['init-mode', 'coordinator-init-mode']);

function dirs(abs: string): string[] {
try {
return fs
Expand Down Expand Up @@ -64,7 +72,7 @@ export function projectClaudeSkills(
const teamSkills = new Set(dirs(path.join(squadAbs, 'skills')));
const templatesAbs = path.join(squadAbs, 'templates', 'skills');
for (const name of opts.plugin ? [] : dirs(templatesAbs)) {
if (teamSkills.has(name) || current.has(name)) continue;
if (teamSkills.has(name) || current.has(name) || CLAUDE_SKIPPED_TEMPLATE_SKILLS.has(name)) continue;
let skill: string;
try {
// A SKILL.md (or directory) linked outside the team directory is never copied into .claude/.
Expand Down
100 changes: 0 additions & 100 deletions plugins/squad/skills/coordinator-init-mode/SKILL.md

This file was deleted.

Loading
Loading