name: "squad-version-check"
description: "Internals of how @bradygaster/squad-cli stamps its version, how squad upgrade works (what it preserves vs overwrites), and how to probe the npm registry for the latest version from a coordinator prompt."
allowedTools: []
confidence: medium
domain: squad-internals
source: "Discovered by Data; validated in bradygaster/squad#1173 recon (2026-05-26)."
SKILL: Squad CLI Internals — Version Stamping & Upgrade Mechanics
Confidence: medium Discovered by: Data Date: 2026-05-26 Validated in: Issue #1173 recon (bradygaster/squad)
What This Skill Covers
Reusable knowledge about how @bradygaster/squad-cli stamps its version into squad.agent.md, how squad upgrade works, what it preserves vs. overwrites, and how to probe the npm registry for the latest version from a coordinator prompt.
Package & Registry Facts
- Package name:
@bradygaster/squad-cli - Registry: npm (public)
- CLI binary:
squad(registered viapackage.json#bin.squad) - Node version requirement: Node ≥22.5.0 (ESM-only codebase)
Version Stamping Mechanism
Source file: dist/cli/core/version.js
Three functions:
getPackageVersion()
Walks up from the compiled JS file to find package.json. Returns pkg.version. Works from both dist/cli/core/version.js and a bundled root cli.js. Returns '0.0.0' as fallback if not found.
stampVersion(filePath, version)
Mutates squad.agent.md in three places:
- HTML comment:
<!-- version: {version} -->(must be on the line immediately after frontmatter---) - Identity line:
- **Version:** {version} - Greeting instruction: backtick-quoted
`Squad v{version}`
Called by: both init and upgrade — after copying the template to the destination.
readInstalledVersion(filePath)
Reads the stamped version back from squad.agent.md:
- First tries HTML comment format:
/<!-- version: ([0-9.]+(?:-[a-z]+(?:\.\d+)?)?) -->/ - Falls back to old frontmatter format:
/^version:\s*"([^"]+)"/m - Returns
'0.0.0'on any error
squad upgrade Behavior
Source file: dist/cli/core/upgrade.js
What gets overwritten:
squad.agent.md— full overwrite from template, thenstampVersion()- Files with
overwriteOnUpgrade: trueinTEMPLATE_MANIFEST: casting JSON files, template .md files,copilot-instructions.md(if @copilot enabled) - GitHub Actions workflows — from
templates/workflows/; non-npm projects get type-aware stubs - Runs
runMigrations()after file copy
What is PRESERVED:
team.md,routing.md,decisions.md,ceremonies.md(user-owned)agents/*/history.md(individual agent memory).squad/config.json— never touched;stateBackendsurvives intact- User-added files not in TEMPLATE_MANIFEST
Self-upgrade path (selfUpgradeCli()):
Detects npm/pnpm/yarn via npm_execpath and npm_config_user_agent. Runs:
- npm:
npm install -g @bradygaster/squad-cli@latest - pnpm:
pnpm add -g @bradygaster/squad-cli@latest - yarn:
yarn global add @bradygaster/squad-cli@latestUse@insidertag for insider builds.
compareSemver(a, b) utility (in upgrade.js):
Returns -1/0/1. Handles pre-release: strips pre-release for base comparison, then treats pre-release as less than release (e.g., 0.9.5-insider.1 < 0.9.5). Can be ported directly if needed in prompt logic.
.squad/config.json — What It Holds
{
"version": 1,
"stateBackend": "worktree"
}
Other optional fields added by the coordinator at runtime:
defaultModel— global model override for all agent spawnsagentModelOverrides.{agentName}— per-agent model override
The file is read-only from the upgrade path's perspective. Only the coordinator writes to it (for model preferences).
Version-Check Probe (npm Registry)
Use this one-liner from inside a coordinator prompt to fetch dist-tags:
npm view @bradygaster/squad-cli dist-tags --json
- Timeout: 5 seconds. If no response within 5 seconds, abandon and show normal greeting.
- On success: extract
dist-tags[channel](e.g.,dist-tags["insider"]). - On any error (network failure, registry unreachable, parse error): show normal greeting.
Upstream OS-Specific Cache
The CLI (self-update.ts) writes latest version info to an OS-specific path with a 24h TTL.
One-liner to read the upstream cache:
node -e "const p=require('path'),o=require('os');const b=process.env.APPDATA||(process.platform==='darwin'?p.join(o.homedir(),'Library','Application Support'):p.join(o.homedir(),'.config'));const f=p.join(b,'squad-cli','update-check.json');try{const d=JSON.parse(require('fs').readFileSync(f,'utf8'));const age=Date.now()-d.checkedAt;if(age<86400000)console.log(JSON.stringify(d));else console.log('STALE')}catch{console.log('MISS')}"
Output semantics:
- Valid JSON
{"latestVersion":"X.Y.Z","checkedAt":N}→ cache hit; uselatestVersion STALE→ cache expired (older than 24h); treat as no dataMISS→ cache missing or corrupt; treat as no data
OS-specific cache path:
- Windows:
%APPDATA%\squad-cli\update-check.json - Linux:
~/.config/squad-cli/update-check.json - macOS:
~/Library/Application Support/squad-cli/update-check.json
Repo-Local Cache Convention: .squad/.cache/version-check.json
Used by coordinator for insider/preview channels (the upstream cache only stores latest).
Schema:
{
"checkedAt": "2026-05-26T14:13:28.492Z",
"currentVersion": "0.9.6-insider.2",
"channel": "insider",
"channelVersion": "0.9.7-insider.1"
}
TTL: 24 hours from checkedAt.
Gitignore: .squad/.cache/ is listed in .gitignore — cache files are never committed.
Key File Paths (installed CLI)
| Purpose | Path |
|---|---|
| Version utilities | dist/cli/core/version.js |
| Upgrade logic | dist/cli/core/upgrade.js |
| Init logic | dist/cli/core/init.js |
| Template manifest | dist/cli/core/templates.js |
| Copilot install helper | dist/cli/copilot-install.js |
| squad.agent.md template | templates/squad.agent.md.template |
| Session init reference | templates/session-init-reference.md |
| All templates | templates/ |