feat: add SQUAD_HOME env var and preset system#1041
feat: add SQUAD_HOME env var and preset system#1041bradygaster merged 9 commits intobradygaster:devfrom
Conversation
Add SQUAD_HOME environment variable support for a roaming squad root directory, and a preset system for reusable agent configurations. SQUAD_HOME: - New resolveSquadHome() resolves SQUAD_HOME env var or falls back to ~/.squad/ - ensureSquadHome() creates the directory if it doesn't exist - resolvePresetsDir() returns the presets subdirectory within SQUAD_HOME Preset System: - Presets are named collections of agent charters in a standard layout - Built-in 'default' preset ships with 5 agents: lead, reviewer, devrel, security, docs - Users can install presets to SQUAD_HOME and apply them to any project - Collision policy: skip existing agents by default, --force to overwrite CLI Commands: - squad preset list - list available presets - squad preset show <name> - show preset details - squad preset apply <name> - apply preset agents to current project - squad preset init - seed built-in presets to SQUAD_HOME Resolves bradygaster#1038 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds support for a user-level “roaming” Squad home directory (SQUAD_HOME, defaulting to ~/.squad) and a reusable preset system (manifests + agent charters), including a new squad preset CLI command for listing/showing/applying/seeding presets.
Changes:
- Introduce
resolveSquadHome(),ensureSquadHome(), andresolvePresetsDir()in the SDK resolution layer. - Add SDK preset APIs (
listPresets,loadPreset,applyPreset,seedBuiltinPresets) plus a built-indefaultpreset (5 agents). - Add
squad presetCLI subcommands and comprehensive Vitest coverage for the new behavior.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| test/presets.test.ts | Adds tests for SQUAD_HOME resolution and preset listing/loading/applying/seeding. |
| packages/squad-sdk/src/resolution.ts | Implements SQUAD_HOME + presets directory resolution helpers. |
| packages/squad-sdk/src/presets/types.ts | Defines preset manifest/apply result types. |
| packages/squad-sdk/src/presets/index.ts | Implements preset discovery, loading, application, and built-in seeding. |
| packages/squad-sdk/src/presets/builtin/default/preset.json | Adds the built-in default preset manifest. |
| packages/squad-sdk/src/presets/builtin/default/agents/lead/charter.md | Adds built-in lead agent charter. |
| packages/squad-sdk/src/presets/builtin/default/agents/reviewer/charter.md | Adds built-in reviewer agent charter. |
| packages/squad-sdk/src/presets/builtin/default/agents/devrel/charter.md | Adds built-in devrel agent charter. |
| packages/squad-sdk/src/presets/builtin/default/agents/security/charter.md | Adds built-in security agent charter. |
| packages/squad-sdk/src/presets/builtin/default/agents/docs/charter.md | Adds built-in docs agent charter. |
| packages/squad-sdk/src/index.ts | Exposes new resolution exports from the public SDK entrypoint. |
| packages/squad-sdk/package.json | Adds ./presets export + build step to ship built-in preset assets to dist. |
| packages/squad-cli/src/cli/commands/preset.ts | Implements `squad preset {init |
| packages/squad-cli/src/cli-entry.ts | Wires new preset command into CLI help + dispatch. |
| .changeset/squad-home-presets.md | Declares a minor bump for SDK + CLI for the new feature set. |
Add 'squad preset save <name>' command that captures your current project's agents as a reusable preset in SQUAD_HOME. This completes the quick-start loop: 1. squad preset apply default # start with a preset 2. (customize agents) # make it yours 3. squad preset save my-team # save as your preset 4. cd other-repo && squad preset apply my-team # reuse everywhere Also adds 4 new tests including full round-trip test (save → apply). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
squad init --preset default # init + apply preset in one command squad init --preset my-team # use your custom preset Automatically seeds built-in presets if needed. The quick start is now: 1. squad init --preset default 2. (customize agents) 3. squad preset save my-team 4. squad init --preset my-team # any new repo, one command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tamirdresher
left a comment
There was a problem hiding this comment.
I'm good with this but you might want to consider the copilot comment about the env var pointing to something which is not a folder
…shots - Show preset save path clearly in output - Add tip about 'squad export' for full squad snapshots (casting, skills, routing) — useful for sharing configured squads or publishing to agent toolboxes - Update module docs with save path and export distinction Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update: preset save messaging + export distinctionChanges in latest push:
Design decision (agreed with @bradygaster): |
- 'squad preset init --remote' creates a private GitHub repo (squad-home) via gh CLI and sets up ~/.squad/ as a clone. On a second machine, the same command detects the existing repo and clones it. - All 'preset not found' errors now nudge users toward 'squad preset init --remote' - Plain 'squad preset init' tips about --remote for roaming - Handles all cases: fresh setup, existing ~/.squad/, second machine clone Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When no presets directory exists, 'squad init --preset default' now automatically initializes squad home and seeds built-in presets instead of failing. Tips user about '--remote' for roaming. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address Copilot review comments flagged by Tamir: - resolveSquadHome() now throws if path exists but is not a directory - resolvePresetsDir() checks isDirectorySync before returning - Added validateName() to reject path traversal in preset/agent names - Build step now cleans dist/presets/builtin before copying (prevents stale files) - Fixed misleading test name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks @tamirdresher! Great catch 🙏 Addressed the Copilot review comment you flagged plus all 9 suggestions in 099d55eb:
All 24 tests passing ✅ |
- Use lstatSync in copyDirRecursive to skip symlinks (prevents following symlinks into unintended locations) - Clean dest dir before copy on --force (prevents stale files from renamed/deleted agents lingering) - Remove duplicate glyphs in CLI output (success()/warn() already add markers) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🧪 E2E Test Results — Quick Start + Roaming WorkflowRan 13 end-to-end tests against the branch using a temp Quick Start Flow (the README happy path)
Preset Commands
Collision Policy
Security / Edge Cases
Roaming Simulation
Tested on: macOS, Node 22, branch |
Split long pipe-separated usage strings into multi-line format in both preset.ts (error message) and cli-entry.ts (help output) so the ux-gates.test.ts line-length check passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bradygaster
left a comment
There was a problem hiding this comment.
no big concerns but flagging some stuff. ;)
| if (seeded.length > 0) { | ||
| info(` Built-in presets installed: ${seeded.join(', ')}`); | ||
| } | ||
| info(` Run 'squad preset list' to see available presets.`); |
There was a problem hiding this comment.
so this and init related features don't collide with the --roles switch, you think?
There was a problem hiding this comment.
I checked all good.
| if (!repoExists) { | ||
| info(`Creating private repo ${repoFullName}...`); | ||
| try { | ||
| execSync(`gh repo create ${repoName} --private --source "${homeDir}" --push --description "Squad home — presets and config"`, { |
There was a problem hiding this comment.
i presume we don't want a git-provider unspecific way?
| if (!presetsDir) { | ||
| info('No presets directory found.'); | ||
| info(' Run `squad preset init --remote` to set up with a GitHub repo (recommended).'); | ||
| info(' Or `squad preset init` for local-only setup.'); |
There was a problem hiding this comment.
maybe add --no-workflows here, too
There was a problem hiding this comment.
making a new issue to track.
| @@ -0,0 +1,30 @@ | |||
| # devrel — Developer Relations | |||
|
|
|||
| > I make your project approachable — if a developer can't get started in 5 minutes, that's on me. | |||
|
thanks @paulyuk ! |
Summary
Adds SQUAD_HOME env var support and a preset system for reusable agent configurations, with auto-init and
--remotefor roaming across machines.Recent Changes
squad init --presetauto-creates squad home — if no presets directory exists, it initializes one automatically and seeds built-in presets. No setup step needed.squad preset init --remote— backs squad home with a privatesquad-homeGitHub repo viagh. On a second machine, same command clones it.preset savetips aboutsquad exportfor full squad snapshots (casting, skills, routing) — e.g. to publish to agent toolboxes.Quick Start
Roam presets across machines
SQUAD_HOME Environment Variable
A roaming squad root directory for user-level squad assets. Presets live here.
--presetuse)~/.squad/presets/--remote(recommended)~/.squad/presets/backed by<you>/squad-homerepoSQUAD_HOME$SQUAD_HOME/presets/SDK functions:
resolveSquadHome()— resolvesSQUAD_HOMEenv var or falls back to~/.squad/ensureSquadHome()— creates the directory if neededresolvePresetsDir()— returns thepresets/subdirectory within SQUAD_HOMEPreset System
Presets are named, reusable collections of agent charters stored in
SQUAD_HOME/presets/<name>/.Built-in default preset ships with 5 agents: lead, reviewer, devrel, security, docs.
CLI Commands:
Collision policy: existing agents are skipped by default; use
--forceto overwrite.Presets vs Export
SQUAD_HOME/presets/<name>/squad-export.json(portable file)--remoteor SQUAD_HOME backed by git/syncTesting
Changeset
Included — minor version bump for both
@bradygaster/squad-sdkand@bradygaster/squad-cli.Resolves #1038