Skip to content

feat(dist): npm-free MCP spec, squad-init action, and container image (#1593) - #1610

Merged
bradygaster merged 3 commits into
devfrom
squad/1593-standalone-consumers
Aug 5, 2026
Merged

bradygaster merged 3 commits into
devfrom
squad/1593-standalone-consumers

Conversation

@tamirdresher

Copy link
Copy Markdown
Collaborator

What

Second slice of the standalone distribution work, following #1595 (now merged). Adds the pieces that turn the bundle mechanism into something you can actually consume: a composite action, a container image, and a fix for a gap that made the first slice incomplete.

Thanks for merging #1595, @bradygaster. This is rebased onto current dev, so it targets dev directly rather than stacking.

Why

While verifying what squad init actually produces, I found the first slice only solved half the problem.

Installing from a bundle removed npm from installation. But squad init still wrote this into .mcp.json:

{ "command": "npx", "args": ["-y", "@bradygaster/squad-cli@0.11.0", "state-mcp"] }

…and called registry.npmjs.org to decide which version to pin. So on a firewalled machine the CLI would install cleanly and then the squad_state MCP server would silently fail to launch on first use. That's exactly the failure this work exists to prevent, and the npm-free install would have shipped looking complete while being broken.

How

Tier-0 in the MCP resolver. resolveSquadStateMcpSpec gains a tier ahead of the two npx ones. When SQUAD_STANDALONE_HOME is set — exported by the bundle launchers — it emits the bundle's own launcher:

{ "command": "/opt/squad/squad", "args": ["state-mcp"] }

Two deliberate details:

  • It short-circuits before the registry probe, so a machine with no route to npm makes no network call at all. There's an explicit test asserting the probe never fires.
  • The path is absolute, because Copilot spawns the MCP server in its own environment where neither PATH nor that env var is guaranteed.

describeMcpSpec bug caught on the way. It read args[1] to name the package. Standalone specs have nothing there, so the success message would have printed <unknown>. Now reports the launcher path.

squad-init composite action. Installs from a release bundle and runs init — the direct replacement for npx @bradygaster/squad-cli init in the gh-aw activation job. It also fails the step if the resulting .mcp.json still mentions npx, so this can't silently regress.

Dockerfile. Built from a bundle rather than npm install -g, following the contract already documented in reference/container-image.md (WORKDIR /app, non-root uid 1001, SIGTERM, squad watch --execute). Uses --include-optional because that contract documents OTLP export, which lives in the optional OpenTelemetry packages.

Verification

Container, on linux/amd64:

Check Result
squad --version as non-root squad user 0.11.0
which squad /opt/squad/squad
squad init → .mcp.json command /opt/squad/squad — no npx
squad state-mcp JSON-RPC initialize {"serverInfo":{"name":"squad-state",...}}

That last one is the one I cared about: the MCP server completes a real handshake inside a container that cannot reach npm.

Also: 13 new tests (23/23 in mcp-spec-init.test.ts) · bundle on win32-x64 writes command=<bundle>\squad.cmd · npm run lint ✅ · docs-build, mcp-root-write, state-mcp ✅ · cspell + markdownlint ✅

What is NOT verified

The Dockerfile builder stage has never run. My machine intercepts TLS, so every container fails to reach registry.npmjs.org:

$ docker run --rm node:22-slim npm view lodash
npm error ... ssl3_read_bytes:ssl/tls alert handshake failure

That's environmental, not a Dockerfile defect — and there's some irony in being blocked by the exact firewall condition this PR addresses. But it means the full image has never been built end to end, and CI needs to prove it. I verified the runtime stage by feeding it a host-built bundle, which covers every line where I made a decision, but not the npm ci + build layer.

The squad-init action has never run either — it downloads a release asset, and no release with bundle assets exists yet. Same chicken-and-egg as #1595.

Changeset

Included (patch for squad-cli) — unlike #1595, this slice does touch packages/squad-cli/src/.


⚠️ Quick Check

  • Changeset added (.changeset/standalone-mcp-spec-1593.md)

PR Readiness Checklist

Branch & Commit

  • Branch created from dev
  • Branch is up to date with dev (rebased onto 543d0eac)
  • Verified diff contains only intended changes
  • PR is not in draft mode
  • Commit history is clean (single commit)

Build & Test

Docs

  • Docs feature page updated — added the MCP spec behavior, the action, and the container section

Exports

  • N/A — no new SDK modules

Breaking Changes

None. The npx tiers are untouched when SQUAD_STANDALONE_HOME is unset, which is every existing npm install.

Waivers

None requested. Flagging rather than waiving: the Dockerfile builder stage and the composite action are both unproven end to end, for the environmental reasons above. I would rather that be stated plainly than have CI be the first place anyone finds out.

…#1593)

Second slice of the standalone distribution work. Builds on the bundle
mechanism from the previous commit and closes the gap that made it
incomplete.

The gap: installing Squad from a bundle removed npm from *installation*, but
`squad init` still wrote an npx-based `squad_state` MCP entry into
`.mcp.json`, and probed registry.npmjs.org to choose the version. On a
firewalled machine the CLI installed fine and then the MCP server silently
failed to launch on first use — the exact failure mode this work exists to
prevent.

What changed:

- mcp-spec.ts gains a tier-0 ahead of the two npx tiers. When
  SQUAD_STANDALONE_HOME is set (exported by the bundle launchers) the spec
  points at that bundle's launcher by absolute path with args ['state-mcp'].
  It short-circuits before the registry probe, so a machine with no route to
  npm makes no network call here at all. Absolute because Copilot spawns the
  MCP server in its own environment, where neither PATH nor that variable is
  guaranteed.
- describeMcpSpec() previously read args[1] to name the package; standalone
  specs have no package there and would have printed '<unknown>'. It now
  reports the launcher path.
- build-standalone.mjs launchers (sh, cmd, ps1) export SQUAD_STANDALONE_HOME.
- .github/actions/squad-init — composite action that installs from a release
  bundle and runs squad init. This is the direct replacement for the
  `npx @bradygaster/squad-cli init` step in the gh-aw activation job (#1587).
  It also fails the build if the resulting .mcp.json still mentions npx.
- Dockerfile + .dockerignore — image built from a bundle instead of
  `npm install -g`, honoring the existing container contract in
  docs/reference/container-image.md (WORKDIR /app, non-root uid 1001,
  SIGTERM, `squad watch --execute`). Sets SQUAD_STANDALONE_HOME=/opt/squad.
  Built with --include-optional because the contract documents OTLP export,
  which lives in the optional OpenTelemetry packages.
- .gitattributes pins LF for Dockerfile and .dockerignore. CRLF breaks RUN
  line continuations when the image builds on Linux.

Verified:

- 13 new tests in mcp-spec-init.test.ts, including an explicit assertion that
  the registry probe never fires in standalone mode. 23/23 pass.
- Built a bundle and ran `squad init` from it on win32-x64: the generated
  .mcp.json contains command=<bundle>/squad.cmd, args=['state-mcp'], no npx.
- Runtime stage of the Dockerfile built and exercised on linux/amd64 with a
  host-built bundle: `squad --version` returns 0.11.0 as the non-root squad
  user, `which squad` resolves to /opt/squad/squad, `squad init` writes
  command=/opt/squad/squad, and `squad state-mcp` answers a JSON-RPC
  initialize handshake with serverInfo.name=squad-state.
- lint, docs-build, mcp-root-write, state-mcp, cspell, markdownlint all clean.

Not verified: the Dockerfile builder stage. This machine intercepts TLS, so
every container gets ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE reaching
registry.npmjs.org — reproducible with a bare
`docker run node:22-slim npm view lodash`. Not a Dockerfile defect, but the
full image has never been built end to end and CI must prove it. Likewise the
squad-init action cannot run until a release with bundle assets exists.

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 5, 2026 06:49
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🏗️ Architectural Review

⚠️ Architectural review: 1 warning(s).

Severity Category Finding Files
🟡 warning bootstrap-area 2 file(s) in the bootstrap area (packages/squad-cli/src/cli/core/) were modified. These files must maintain zero external dependencies. Review carefully. packages/squad-cli/src/cli/core/mcp-spec.ts, packages/squad-cli/src/cli/core/upgrade.ts

Automated architectural review — informational only.

@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

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

PR Scope: 📦🔧 Mixed (product + infrastructure)

⚠️ 2 item(s) to address before review

Status Check Details
❌ Single commit 3 commits — consider squashing before review
✅ 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 Changeset file found
✅ Scope clean No .squad/ or docs/proposals/ files
✅ No merge conflicts No merge conflicts
✅ Copilot threads resolved 0 active Copilot thread(s) resolved (2 outdated skipped)
✅ CI passing All checks passing

Files Changed (12 files, +675 −23)

File +/−
.changeset/standalone-cross-build-fix.md +5 −0
.changeset/standalone-mcp-spec-1593.md +5 −0
.dockerignore +15 −0
.gitattributes +5 −0
.github/actions/squad-init/action.yml +189 −0
Dockerfile +71 −0
docs/src/content/docs/features/standalone-install.md +69 −3
packages/squad-cli/src/cli/core/mcp-spec.ts +62 −6
packages/squad-cli/src/cli/core/upgrade.ts +6 −1
scripts/build-standalone.mjs +33 −11
test/mcp-spec-init.test.ts +106 −2
test/standalone-build.test.ts +109 −0

Total: +675 −23


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 5, 2026 •

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #1610

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 12
Files added 6
Files modified 6
Files deleted 0
Modules touched 6

🎯 Risk Factors

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

📦 Modules Affected

ci-workflows (1 file)
  • .github/actions/squad-init/action.yml
docs (1 file)
  • docs/src/content/docs/features/standalone-install.md
root (5 files)
  • .changeset/standalone-cross-build-fix.md
  • .changeset/standalone-mcp-spec-1593.md
  • .dockerignore
  • .gitattributes
  • Dockerfile
scripts (1 file)
  • scripts/build-standalone.mjs
squad-cli (2 files)
  • packages/squad-cli/src/cli/core/mcp-spec.ts
  • packages/squad-cli/src/cli/core/upgrade.ts
tests (2 files)
  • test/mcp-spec-init.test.ts
  • test/standalone-build.test.ts

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

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

Adds an npm-free path for the squad_state MCP server spec when running Squad from a standalone bundle, plus distribution helpers (composite action + container image) to consume those bundles in CI and containers.

Changes:

  • Add a new “standalone bundle” tier in the MCP spec resolver (driven by SQUAD_STANDALONE_HOME) and fix describeMcpSpec for standalone specs.
  • Add a squad-init composite action that installs from release bundles and runs squad init.
  • Add a root Dockerfile + docs updates for building/running an image from a bundle (no npm registry dependency at runtime).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/mcp-spec-init.test.ts Adds tests for standalone-tier resolution, standalone launcher detection, and describeMcpSpec behavior.
scripts/build-standalone.mjs Exports SQUAD_STANDALONE_HOME from bundle launchers (cmd/ps1/sh).
packages/squad-cli/src/cli/core/mcp-spec.ts Adds standalone tier + launcher detection and exposes STANDALONE_HOME_ENV.
packages/squad-cli/src/cli/core/upgrade.ts Fixes describeMcpSpec to correctly describe standalone launcher specs.
docs/src/content/docs/features/standalone-install.md Documents squad-init, container usage, and the npx-free .mcp.json behavior.
Dockerfile Builds a container image from a standalone bundle (multi-stage).
.github/actions/squad-init/action.yml Composite action to install from bundle + run squad init, and summarize results.
.gitattributes Forces LF endings for Docker-related files to prevent build issues.
.dockerignore Reduces build context / prevents host artifacts from leaking into the image build.
.changeset/standalone-mcp-spec-1593.md Changeset for the squad-cli patch release.

Comment thread .github/actions/squad-init/action.yml Resolved
Comment thread .github/actions/squad-init/action.yml Resolved
tamirdresher and others added 2 commits August 5, 2026 16:52
Addresses both Copilot review comments on #1610.

1. The action fetched scripts/install.sh from the repository HEAD over the
   network. That is unpinned remote code execution, and the installer could
   drift from the release tag being installed. It now runs the install.sh that
   ships alongside the action, resolved from github.action_path. Consumers pin
   the action to a SHA, so the installer is pinned with it.

2. The npx check only wrote a warning to the step summary and exited 0, which
   did not match what the PR claimed. An npx-based squad_state entry is
   unrunnable on exactly the firewalled runners this action targets, and it
   fails later at Copilot start rather than here, so it now fails the step.

Verified the action_path walk resolves correctly on Linux (alpine container:
/repo/.github/actions/squad-init -> /repo/scripts/install.sh) rather than
assuming the relative depth.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
…tests

Found while running the installer end to end for the first time: building a
linux or darwin bundle from a Windows host failed.

  node-v22.20.0-linux-x64/bin/npx: Can't create '...': Invalid argument
  tar: Error exit delayed from previous errors.

The builder extracted the whole Node.js archive, and the posix archives carry
symlinks (bin/npm, bin/npx, bin/corepack) that Windows cannot create. Only the
node binary is ever shipped, so extraction is now scoped to that single
member. Extraction failures also report the underlying tar error instead of a
bare non-zero exit.

This would have bitten anyone building non-linux targets from a Windows
runner, and the release workflow's structural checks would not have caught it.

Also adds test/standalone-build.test.ts (11 cases) covering the behaviors that
break silently: selective extraction, SQUAD_STANDALONE_HOME in all three
launchers, symlink resolution in the posix launcher, optional-dep omission,
checksum verification, VERSION/PREFIX/REPO overrides, and an assertion that
install.sh never reaches the npm registry.

Verified the full install path in a container with no Node and no npm: served
a packaged bundle over HTTP, ran the real install.sh against it, and got
squad --version -> 0.11.0 through the PATH symlink. Corrupting the asset
made the installer abort with a checksum mismatch, as intended.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
@tamirdresher

Copy link
Copy Markdown
Collaborator Author

Pushed two follow-ups: the review fixes (b121030) and a real bug I found while testing (4756266).

Both review comments addressed

The unpinned raw.githubusercontent.com/.../HEAD fetch is gone — the action now runs the install.sh that ships beside it, so it is pinned by whatever SHA a consumer pins the action to. And the npx check now exits 1 instead of writing a warning, which is what the description claimed all along. Replies are on the individual threads.

curl | bash is now actually proven

I built a fake release and ran the real installer against it in a container with no Node and no npm:

→ Downloading squad-linux-x64.tar.gz (v9.9.9)
→ Checksum verified
→ Installing to /opt/squadtest/lib/squad
✓ Squad v9.9.9 installed to /opt/squadtest/bin/squad

which squad: /opt/squadtest/bin/squad
squad --version: 0.11.0

Then corrupted the asset to confirm the checksum path actually rejects it:

error: Checksum mismatch for squad-linux-x64.tar.gz. Expected 5736…, got 1bb8…

So download, checksum verification, unpack, symlink resolution and PATH all work. That was speculation in #1595 and it isn't anymore.

The bug that found

Getting there surfaced a genuine defect in the builder. Cross-building a linux bundle from Windows fails:

node-v22.20.0-linux-x64/bin/npx: Can't create '...': Invalid argument
tar: Error exit delayed from previous errors.

The posix Node archives contain symlinks (bin/npm, bin/npx, bin/corepack) that Windows cannot create, and the builder was extracting the whole archive. Only the node binary is ever shipped, so extraction is now scoped to that one member.

Worth noting the release workflow would not have caught this — it builds on ubuntu-latest, where extracting the full archive works fine. It would only have bitten someone building non-linux targets from a Windows runner or locally on Windows. That is exactly the class of thing that sits latent for months.

New tests

Added test/standalone-build.test.ts (11 cases) for the behaviors that fail silently rather than loudly: selective extraction, SQUAD_STANDALONE_HOME in all three launchers, symlink resolution in the posix launcher, optional-dep omission, checksum verification, VERSION/PREFIX/REPO overrides, and an assertion that install.sh never reaches the npm registry.

One of those tests failed on the first run because I asserted against the raw file and install.sh mentions registry.npmjs.org in a comment explaining what it avoids. Fixed the test to check executable lines.

Still unverified

Unchanged from the PR description: the Dockerfile builder stage (my machine's TLS interception blocks npm from any container) and the composite action itself, which needs a release with bundle assets to exist. Both need CI.

@bradygaster
bradygaster merged commit 8738d1a into dev Aug 5, 2026
15 checks passed
@bradygaster
bradygaster deleted the squad/1593-standalone-consumers branch August 5, 2026 18:16
bradygaster added a commit that referenced this pull request Aug 5, 2026
…) (#1611)

* feat(dist): npm-free MCP spec, squad-init action, and container image (#1593)

Second slice of the standalone distribution work. Builds on the bundle
mechanism from the previous commit and closes the gap that made it
incomplete.

The gap: installing Squad from a bundle removed npm from *installation*, but
`squad init` still wrote an npx-based `squad_state` MCP entry into
`.mcp.json`, and probed registry.npmjs.org to choose the version. On a
firewalled machine the CLI installed fine and then the MCP server silently
failed to launch on first use — the exact failure mode this work exists to
prevent.

What changed:

- mcp-spec.ts gains a tier-0 ahead of the two npx tiers. When
  SQUAD_STANDALONE_HOME is set (exported by the bundle launchers) the spec
  points at that bundle's launcher by absolute path with args ['state-mcp'].
  It short-circuits before the registry probe, so a machine with no route to
  npm makes no network call here at all. Absolute because Copilot spawns the
  MCP server in its own environment, where neither PATH nor that variable is
  guaranteed.
- describeMcpSpec() previously read args[1] to name the package; standalone
  specs have no package there and would have printed '<unknown>'. It now
  reports the launcher path.
- build-standalone.mjs launchers (sh, cmd, ps1) export SQUAD_STANDALONE_HOME.
- .github/actions/squad-init — composite action that installs from a release
  bundle and runs squad init. This is the direct replacement for the
  `npx @bradygaster/squad-cli init` step in the gh-aw activation job (#1587).
  It also fails the build if the resulting .mcp.json still mentions npx.
- Dockerfile + .dockerignore — image built from a bundle instead of
  `npm install -g`, honoring the existing container contract in
  docs/reference/container-image.md (WORKDIR /app, non-root uid 1001,
  SIGTERM, `squad watch --execute`). Sets SQUAD_STANDALONE_HOME=/opt/squad.
  Built with --include-optional because the contract documents OTLP export,
  which lives in the optional OpenTelemetry packages.
- .gitattributes pins LF for Dockerfile and .dockerignore. CRLF breaks RUN
  line continuations when the image builds on Linux.

Verified:

- 13 new tests in mcp-spec-init.test.ts, including an explicit assertion that
  the registry probe never fires in standalone mode. 23/23 pass.
- Built a bundle and ran `squad init` from it on win32-x64: the generated
  .mcp.json contains command=<bundle>/squad.cmd, args=['state-mcp'], no npx.
- Runtime stage of the Dockerfile built and exercised on linux/amd64 with a
  host-built bundle: `squad --version` returns 0.11.0 as the non-root squad
  user, `which squad` resolves to /opt/squad/squad, `squad init` writes
  command=/opt/squad/squad, and `squad state-mcp` answers a JSON-RPC
  initialize handshake with serverInfo.name=squad-state.
- lint, docs-build, mcp-root-write, state-mcp, cspell, markdownlint all clean.

Not verified: the Dockerfile builder stage. This machine intercepts TLS, so
every container gets ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE reaching
registry.npmjs.org — reproducible with a bare
`docker run node:22-slim npm view lodash`. Not a Dockerfile defect, but the
full image has never been built end to end and CI must prove it. Likewise the
squad-init action cannot run until a release with bundle assets exists.

Refs #1593, #1587

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(action): run the bundled installer and fail on npx MCP specs

Addresses both Copilot review comments on #1610.

1. The action fetched scripts/install.sh from the repository HEAD over the
   network. That is unpinned remote code execution, and the installer could
   drift from the release tag being installed. It now runs the install.sh that
   ships alongside the action, resolved from github.action_path. Consumers pin
   the action to a SHA, so the installer is pinned with it.

2. The npx check only wrote a warning to the step summary and exited 0, which
   did not match what the PR claimed. An npx-based squad_state entry is
   unrunnable on exactly the firewalled runners this action targets, and it
   fails later at Copilot start rather than here, so it now fails the step.

Verified the action_path walk resolves correctly on Linux (alpine container:
/repo/.github/actions/squad-init -> /repo/scripts/install.sh) rather than
assuming the relative depth.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(dist): cross-build posix bundles from Windows, add build/install tests

Found while running the installer end to end for the first time: building a
linux or darwin bundle from a Windows host failed.

  node-v22.20.0-linux-x64/bin/npx: Can't create '...': Invalid argument
  tar: Error exit delayed from previous errors.

The builder extracted the whole Node.js archive, and the posix archives carry
symlinks (bin/npm, bin/npx, bin/corepack) that Windows cannot create. Only the
node binary is ever shipped, so extraction is now scoped to that single
member. Extraction failures also report the underlying tar error instead of a
bare non-zero exit.

This would have bitten anyone building non-linux targets from a Windows
runner, and the release workflow's structural checks would not have caught it.

Also adds test/standalone-build.test.ts (11 cases) covering the behaviors that
break silently: selective extraction, SQUAD_STANDALONE_HOME in all three
launchers, symlink resolution in the posix launcher, optional-dep omission,
checksum verification, VERSION/PREFIX/REPO overrides, and an assertion that
install.sh never reaches the npm registry.

Verified the full install path in a container with no Node and no npm: served
a packaged bundle over HTTP, ran the real install.sh against it, and got
squad --version -> 0.11.0 through the PATH symlink. Corrupting the asset
made the installer abort with a checksum mismatch, as intended.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* feat(dist): Homebrew and winget packaging, plus a real squad.exe (#1593)

Third slice of the distribution work. Adds the two package managers people
actually expect, and the Windows executable that makes one of them possible.

The blocker I found first: winget's portable installer only creates command
aliases for .exe targets — .cmd and .bat are explicitly unsupported. Our
Windows bundle's entry point was squad.cmd, so a winget manifest would have
installed something that could not be invoked. Homebrew has no such
restriction and works with the existing shell launcher.

So Windows bundles now ship a real squad.exe, built with Node's single
executable application support. It is the vendored runtime with a small
launcher embedded, so it REPLACES runtime/node.exe rather than adding to the
bundle — a Windows bundle is still 114 MB. The launcher resolves the bundle
from its own location and follows a symlink if it finds one, because winget
installs portables by symlinking the exe into a links directory.

Also here:

- scripts/generate-packaging.mjs renders the Homebrew cask and the three
  winget manifests from a release's SHA256SUMS.txt. Both ecosystems embed a
  version and a per-artifact SHA-256, so hand-maintaining them guarantees they
  go stale — this is exactly the standing cost flagged in #1593.
- The release workflow generates them after publishing and attaches them as an
  artifact for a maintainer to submit.
- mcp-spec now prefers squad.exe over squad.cmd on Windows. Since the fix for
  CVE-2024-27980 Node refuses to spawn a .cmd without shell:true, so an MCP
  client spawning the command directly would fail to start squad_state.

Two real bugs found while testing, both of which CI would have hit:

1. GNU tar cannot read zip archives; bsdtar (Windows, macOS) can. The release
   workflow builds every target on ubuntu-latest, so every win32 bundle would
   have failed to unpack its Node runtime. Now uses unzip on posix hosts, and
   the workflow installs it for win32 targets.
2. execFileSync with shell:true splits an unquoted command path on spaces, so
   invoking process.execPath from "C:\Program Files\nodejs" failed. The run()
   helper now quotes.

Verified:

- squad.exe, squad.cmd and squad.ps1 all return 0.11.0 from a real bundle
- squad init from squad.exe scaffolds 5 agents and writes an npx-free
  .mcp.json pointing at the bundle launcher
- SEA blob is version-locked: a blob built by Node 23.5 injected into the
  vendored 22.20.0 produced an exe that died with an access violation
  (0xC0000005). The builder now downloads a matching host Node to generate it.
- Cross-build works: a Linux container unzipped the Windows runtime, generated
  the blob with Linux Node 22.20.0, injected it with postject, and the
  resulting PE ran correctly on Windows. That is the production path, since
  the workflow builds all six targets on ubuntu-latest.
- Generator output parses as YAML, all three winget manifests agree on
  identifier/version/schema, and both installers point at .exe
- 67 tests pass (18 new for the generator, 8 new for the exe and extraction)

Dropped the shebang from generate-packaging.mjs so vitest can import it on
Windows; scripts/pr-readiness.mjs, the repo's other importable script, has no
shebang either. Worth noting separately: the same shebang interaction is why
test/promote-insider-tag.test.ts is one of the pre-existing Windows failures.
Left alone as out of scope.

Not verified: no winget or Homebrew submission has been made, because neither
can be tested without a published release carrying bundle assets. Code signing
and notarization are still absent, and injecting the SEA blob invalidates the
Node runtime's Authenticode signature, so SmartScreen will warn on Windows.
That is the main gap before either package should be recommended widely.

Refs #1593

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(ci): suppress shellcheck SC2016 on Actions expression in release workflow

The single-quoted backticks are intentional markdown formatting in the
step summary, not shell expressions. Disable SC2016 for those lines.

Closes #1611

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Tamir Dresher <342800+tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Brady Gaster <41929050+bradygaster@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
bradygaster added a commit that referenced this pull request Aug 5, 2026
Distribution surface for Squad's agentic workflows:
- workflows/squad.md: unified /squad slash command (stub)
- workflows/shared/squad.md: bootstrap component (installs Squad, bundles state)

Uses squad-init composite action (from #1610) instead of npx.
Frontmatter is complete; implementation bodies are stubs pending #1613-#1617.

Closes #1620

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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