Skip to content

feat(dist): standalone npm-free bundles for squad-cli (#1593) - #1595

Merged
bradygaster merged 1 commit into
devfrom
squad/1593-standalone-distribution
Aug 4, 2026
Merged

bradygaster merged 1 commit into
devfrom
squad/1593-standalone-distribution

Conversation

@tamirdresher

Copy link
Copy Markdown
Collaborator

What

Adds a build path that produces self-contained Squad bundles which vendor their own Node.js runtime. Installing from one touches github.com only — never registry.npmjs.org — and needs neither Node nor npm present on the target machine.

Four pieces: a bundle builder, a POSIX installer, a release workflow, and docs. npm install -g @bradygaster/squad-cli is untouched and stays the recommended developer path.

Why

Closes the limitation @bradygaster documented and explicitly deferred in #1587:

npm required in the activation job — works on GitHub-hosted runners (open egress); a self-hosted runner behind a corporate npm-blocking firewall would fail. Out of scope.

That blocks gh-aw on self-hosted runners inside a corp network. There is currently no plan B: npm i -g and npx are the only ways to get Squad onto a machine.

This is the foundation layer proposed in #1593 — the artifacts every other channel (install script, container, winget, Homebrew, a squad-init action, and an npm stub) would consume.

Worth noting the precedent: the CLI we depend on already ships five ways (winget, Homebrew, curl | bash, npm, and direct binaries), so Squad is now the only npm requirement left in the stack.

Refs #1593. Does not close it — #1593 asks for a go/no-go on the whole direction, and this is one slice of it.

How

Why a directory tree, not a single compiled executable. squad-cli and squad-sdk locate templates/, presets/builtin/ and package.json by walking up from import.meta.url at runtime (getTemplatesDir, getPackageVersion, getBuiltinPresetsDir). Preserving a real on-disk layout keeps every one of those lookups working, which is why this needs zero source changes. It is also the same shape GitHub's own Copilot CLI publishes.

squad-<platform>-<arch>/
├── squad               launcher (squad.cmd + squad.ps1 on Windows)
├── runtime/            vendored Node.js runtime
├── app/                squad-cli, squad-sdk, templates, presets
└── BUNDLE-INFO.json

Size. A naive build came out at 546 MB. @github/copilot-sdk transitively vendors the entire Copilot CLI platform binary (318 MB), plus node-pty prebuilds (61 MB), OpenTelemetry (22 MB) and sql.js (18 MB). Omitting optional dependencies by default brings it to 114 MB, in line with Copilot CLI's own ~110 MB archives. Squad already requires copilot on PATH and Copilot CLI ships its own npm-free installers, so shipping a second copy would add only weight and version skew. --include-optional restores the fat build.

--ignore-scripts is safe here. squad-cli's postinstall patches vscode-jsonrpc for strict-ESM resolution, but cli-entry.js applies the same fix at runtime via a Module._resolveFilename hook, so the bundle never depends on the install-time patch having run.

Verification

Built and run locally on win32-x64:

Check Result
squad --version from bundle, on its vendored runtime 0.11.0
squad init --preset default --state-backend local in a clean dir, no npm ✅ 5 agents, 177 files scaffolded
Bundle size, optional deps omitted 546 MB → 114 MB
--skip-runtime variant 32.3 MB, runs on system node

Fidelity check. Generated .github/workflows/squad-heartbeat.yml:

Source SHA256
repo template on dev 5CE0F5D1…
bundle output 5CE0F5D1… ✅ identical
npx @bradygaster/squad-cli@0.11.0 A31D7C75…

A full A/B against npx showed 148 differing files — all attributable to dev having moved ahead of published 0.11.0 (e.g. squad.agent.md is 1088 lines on dev vs 1047 published), not to bundling. The byte-identical match against the repo's own template source is the meaningful control.

Repo checks: npm run build ✅ · npm run lint ✅ · npm run lint:eslint ✅ 0 errors · test/docs-build.test.ts ✅ 6 passed · cspell ✅ · markdownlint ✅

On npm test: the suite does not fully pass on Windows, on this branch or on clean dev. I measured both:

Test files Tests failed Tests passed
dev (clean baseline) 41 failed / 226 passed 242 6743
this branch 41 failed / 226 passed 225 6761

Same 41 failing files either way — these are pre-existing Windows path-separator failures (e.g. watch-capabilities.test.ts expects .squad/ralph-instructions.md but gets \some\repo\.squad\ralph-instructions.md), which I reproduced on clean dev to confirm. This branch introduces no new failures. The small delta is flaky timing-sensitive tests, not a real improvement. CI on ubuntu-latest is the authority here, not my box.

Known limitations — stated honestly

  • Only win32-x64 has actually been executed. The other five targets are cross-built and verified structurally; the workflow smoke-tests linux-x64 by invoking the CLI on a real runner. The workflow itself has never run — it needs a maintainer to dispatch it.
  • No code signing or notarization. macOS Gatekeeper will quarantine downloads until that is added. This is the biggest gap before anyone should be told to use the macOS bundles.
  • --include-optional is untested. I only exercised the default lean build.
  • The installer is POSIX-only. Windows is download-and-unpack; winget/Homebrew are not in scope here.
  • fix(cli): squad-sdk dependency is unpinned — CLI version pinning is not reproducible #1588 is still an open prerequisite. squad-cli declares "@bradygaster/squad-sdk": ">=0.11.0", so these bundles are not yet reproducible across an SDK minor bump. That issue gates the reproducibility claim, not the mechanism.

Scope

New files plus three one-line additions (.gitignore, docs/src/navigation.ts, test/docs-build.test.ts — the last two are the required sync for a new docs page). Also pins *.sh text eol=lf in .gitattributes: install.sh was authored on Windows and CRLF would break the shebang on Linux.

No packages/*/src/ changes → no changeset required.


⚠️ Quick Check

  • No SDK/CLI source files changed — changeset not required

PR Readiness Checklist

Branch & Commit

  • Branch created from dev (not main)
  • Branch is up to date with dev
  • Verified diff contains only intended changes (git diff --cached --stat)
  • PR is not in draft mode
  • Commit history is clean (single commit)

Build & Test

  • npm run build passes
  • npm test passes — see the note above. 41 test files fail identically on clean dev on Windows; this branch adds none. Deferring to CI.
  • npm run lint passes (type check clean)
  • npm run lint:eslint passes (0 errors; 2089 pre-existing warnings, none introduced here)
  • N/A — not a migration PR

Changeset

  • N/A — no packages/*/src/ changes. Happy to add skip-changelog if the bot wants it.

Docs

  • README section updated — not done. Tell me if a distribution section belongs there and I will add it.
  • Docs feature page added (features/standalone-install), nav + docs-build test synced

Exports

  • N/A — no new SDK modules

Breaking Changes

None. Purely additive; the npm install path is unchanged.

Waivers

None requested. Flagging rather than waiving: the release workflow has never executed, so the six-target matrix is unproven beyond YAML validation and a locally-verified win32-x64 build. I would rather a maintainer dispatch it once before this merges than have me assert it works.

Adds a build path that produces self-contained Squad bundles vendoring their
own Node.js runtime, so installing Squad touches github.com only and never
registry.npmjs.org.

This is the foundation for the distribution work proposed in #1593, and it
closes the limitation Brady documented and deferred in #1587: the gh-aw
activation job currently requires npx, so it cannot run on a self-hosted
runner behind a firewall that blocks the npm registry.

What is here:

- scripts/build-standalone.mjs — builds a bundle for a platform/arch pair.
  Packs the workspace packages, installs them with --omit=dev --omit=optional,
  vendors an official Node runtime, and writes a launcher.
- scripts/install.sh — POSIX installer that resolves the platform, downloads
  the matching release asset, verifies it against SHA256SUMS.txt, and links
  squad onto PATH. Mirrors the shape of gh.io/copilot-install.
- .github/workflows/squad-standalone-release.yml — 6-target matrix build that
  attaches archives plus a combined SHA256SUMS.txt to a release.
- docs/src/content/docs/features/standalone-install.md — feature docs, with
  known limitations stated.

Design notes:

squad-cli and squad-sdk locate templates/, presets/builtin/ and package.json
by walking up from import.meta.url at runtime. The bundle therefore preserves
a real directory layout instead of compiling to a single executable, which is
why this needs zero source changes. It is also the same shape GitHub's own
Copilot CLI publishes.

Optional dependencies are omitted by default. That drops the Copilot CLI
platform binary pulled in transitively through @github/copilot-sdk (~318 MB),
node-pty prebuilds, the OpenTelemetry SDK and sql.js — 546 MB down to 114 MB,
in line with Copilot CLI's own ~110 MB archives. Squad already requires the
copilot CLI on PATH and Copilot CLI ships npm-free installers of its own, so
vendoring a second copy would only add weight and version skew.

--ignore-scripts is safe here: the vscode-jsonrpc ESM fix that squad-cli's
postinstall applies is also applied at runtime by cli-entry.js via a
Module._resolveFilename hook, so the bundle does not depend on it having run.

Verified locally on win32-x64:

- squad --version from the bundle returns 0.11.0 on its own vendored runtime
- squad init --preset default --state-backend local scaffolds 5 agents in a
  clean directory with no npm involved
- generated .github/workflows/squad-heartbeat.yml is byte-identical (sha256
  5CE0F5D1...) to the repo template source, confirming the bundle reproduces
  its source faithfully
- docs-build test passes, cspell and markdownlint clean on the new page

No packages/*/src changes, so no changeset is required.

Refs #1593, #1587

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
Copilot AI lite review requested due to automatic review settings August 4, 2026 17:30
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #1595

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 8
Files added 4
Files modified 4
Files deleted 0
Modules touched 5

🎯 Risk Factors

  • 8 files changed (6-20 → MEDIUM)
  • 5 modules touched (5-8 → HIGH)

📦 Modules Affected

ci-workflows (1 file)
  • .github/workflows/squad-standalone-release.yml
docs (2 files)
  • docs/src/content/docs/features/standalone-install.md
  • docs/src/navigation.ts
root (2 files)
  • .gitattributes
  • .gitignore
scripts (2 files)
  • scripts/build-standalone.mjs
  • scripts/install.sh
tests (1 file)
  • test/docs-build.test.ts

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 78d5a82

PR Scope: 🔧 Infrastructure

⚠️ 2 item(s) to address before review

Status Check Details
✅ Single commit 1 commit — clean history
✅ Not in draft Ready for review
✅ Branch up to date Up to date with dev
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present No source files changed — changeset not required
✅ Scope clean No .squad/ or docs/proposals/ files
✅ No merge conflicts No merge conflicts
❌ Copilot threads resolved 3 unresolved Copilot thread(s) — fix and resolve before merging
✅ CI passing All checks passing

Files Changed (8 files, +778 −0)

File +/−
.gitattributes +3 −0
.github/workflows/squad-standalone-release.yml +180 −0
.gitignore +1 −0
docs/src/content/docs/features/standalone-install.md +140 −0
docs/src/navigation.ts +1 −0
scripts/build-standalone.mjs +331 −0
scripts/install.sh +121 −0
test/docs-build.test.ts +1 −0

Total: +778 −0


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🏗️ Architectural Review

⚠️ Architectural review: 1 info.

Severity Category Finding Files
ℹ️ info template-sync Template files changed in .github/workflows/ but not in other template locations. If these templates should stay in sync, consider updating the others too. Changed: .github/workflows/, Unchanged: templates/, .squad-templates/, packages/squad-cli/templates/

Automated architectural review — informational only.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🔒 Security Review

🔒 Security review: 1 info.

Severity Category Finding Location
ℹ️ info workflow-permissions Workflow grants write permission — verify this is the minimum required scope. .github/workflows/squad-standalone-release.yml:158

Automated security review — informational only.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new distribution path for standalone, npm-free Squad CLI bundles (including a vendored Node.js runtime), plus a POSIX installer, release automation, and documentation updates to make the new install method discoverable.

Changes:

  • Add a standalone bundle builder script that stages workspace tarballs into a self-contained on-disk layout and optionally vendors a Node.js runtime.
  • Add a POSIX curl | sh installer that downloads release artifacts (and optionally verifies checksums) and installs them under a prefix.
  • Add a release workflow that builds, packages, smoke-tests (native target only), and optionally uploads the bundles + SHA256SUMS.txt, plus docs/nav/test updates.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/docs-build.test.ts Adds the new docs feature slug to the expected list so docs-build tests stay in sync.
scripts/install.sh New POSIX installer for downloading and installing standalone bundles from GitHub Releases.
scripts/build-standalone.mjs New bundle builder that packs workspace tarballs, installs runtime deps, and vendors a Node runtime.
docs/src/navigation.ts Adds the standalone install page to the docs navigation.
docs/src/content/docs/features/standalone-install.md New documentation page describing standalone install and bundle contents/limitations.
.gitignore Ignores dist-standalone/ output directory.
.github/workflows/squad-standalone-release.yml New workflow to build/package/upload standalone bundles on release or manual dispatch.
.gitattributes Enforces LF line endings for .sh to prevent shebang breakage on POSIX systems.

Comment on lines +188 to +189
step('Extracting runtime');
run('tar', ['-xf', archivePath, '-C', stagingDir], stagingDir);
Comment thread scripts/install.sh
Comment on lines +80 to +94
if fetch "https://github.com/${REPO}/releases/download/${VERSION}/SHA256SUMS.txt" "$tmp/SHA256SUMS.txt" 2>/dev/null; then
if command -v sha256sum >/dev/null 2>&1; then
expected="$(grep " ${asset}\$" "$tmp/SHA256SUMS.txt" | awk '{print $1}')"
actual="$(sha256sum "$tmp/$asset" | awk '{print $1}')"
elif command -v shasum >/dev/null 2>&1; then
expected="$(grep " ${asset}\$" "$tmp/SHA256SUMS.txt" | awk '{print $1}')"
actual="$(shasum -a 256 "$tmp/$asset" | awk '{print $1}')"
else
expected=""; actual=""
fi
if [ -n "$expected" ] && [ "$expected" != "$actual" ]; then
err "Checksum mismatch for ${asset}. Expected ${expected}, got ${actual}."
fi
[ -n "$expected" ] && info "Checksum verified"
fi
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) total += directorySize(full);
else if (entry.isFile()) total += readFileSync(full).byteLength;
@bradygaster
bradygaster merged commit bd9c512 into dev Aug 4, 2026
17 checks passed
@bradygaster
bradygaster deleted the squad/1593-standalone-distribution branch August 4, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants