Skip to content

fix(claude): portable launch commands and host-aware init next steps (#2462, #2453) - #2470

Merged
bradygaster merged 8 commits into
devfrom
squad/2462-2453-claude-init-portable
Nov 12, 2026
Merged

bradygaster merged 8 commits into
devfrom
squad/2462-2453-claude-init-portable

Conversation

@bradygaster

@bradygaster bradygaster commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Two first-run fixes from the Claude Code end-to-end test (epic #2438, checklist #2469).

Closes #2462
Closes #2453

#2462: committed files carry no machine paths

With the Squad CLI installed (global, project, or a source build), squad init and squad upgrade wrote node /abs/path/cli-entry.js state-mcp into .mcp.json, the other hosts' MCP configs and every Claude hook in .claude/settings.json. Any other clone failed, and the commit leaked the local username and layout.

Committed files now use a portable form, chosen in resolveCommittedSquadStateMcpSpec (mcp-spec.ts), which every writer of committed files calls (init, upgrade, syncClients, squad client list, read-only):

Running CLI Committed command
installed (local-entry) node .squad/squad.mjs state-mcp: the repository launcher
standalone bundle squad state-mcp (bundles put squad on PATH and may have no Node)
run through npx (pinned) unchanged: npx -y @bradygaster/squad-cli@<version> state-mcp
  • Repository launcher. It's generated from the same template as the plugin launcher from feat(claude): Squad Claude Code plugin and team dashboard (#2443, #2459, #2460) #2463 (squadRepoLauncher in claude-plugin.ts), so there's one implementation. It resolves SQUAD_CLI, then the repository's node_modules, then squad on PATH, skipping a CLI older than the version that wrote it. It uses npx only with SQUAD_ALLOW_NPX=1. It finds the repository from its own location, not the working directory. A user-authored file at that path is never overwritten; Squad keeps the old spec instead.
  • Found on this machine too. squad init and squad upgrade record the CLI entry in <Squad home>/cli.json (outside the repository). Both launchers try it after PATH, so a CLI that only runs by path (a source build, CI) works on the machine that set up the repository. Package-runner caches (_npx, dlx) are never recorded, and keep the exact-version npx command instead.
  • Safe writes. The launcher path goes through resolveInsideRoot, so symlinks can't redirect the write outside the workspace. It lives at .squad/squad.mjs, not under bin/, which many repositories ignore.
  • Upgrade matrix. .squad/squad.mjs is declared Squad-owned in upgrade-matrix.json and the upgrading guide. That changes the harness hash, so the recorded Linux and Windows runs are refolded from this PR's matrix run.
  • Claude hooks call node "$CLAUDE_PROJECT_DIR/.squad/squad.mjs" hook claude <Event>, the documented form for project hooks. isSquadHookHandler matches that exact shape, so refresh and rollback still strip only Squad's handlers, and squad upgrade replaces old machine-specific handlers.
  • squad doctor gets a new portable squad_state launch check. It fails on a committed path that doesn't exist on this machine or a missing launcher, and warns on this machine's own absolute path.
  • x-squad.machineSpecific is now false for the portable forms.
  • The plugin's scripts/squad.mjs was regenerated from the shared template. The only change is a projectStart() helper, with the same behaviour. It's still pinned to 2.0.0-preview.4.

#2453: init names the host you chose

squad init --client claude ended with "Run copilot --agent squad" and a Copilot-only tip. initNextSteps now builds the closing lines from the chosen hosts:

  • Claude Code: claude --agent squad --mcp-config .mcp.json, or claude in plugin mode, plus a matching claude -p tip.
  • Other hosts: one line each.
  • No --client: Copilot, as before.

Testing

  • New test/portable-launch.test.ts (15 tests) covers the spec mapping, launcher writes (created, unchanged, updated, a user file skipped), hook form, merge and strip, the look-alike guard, and the launcher run for real (project install, older CLI skipped, SQUAD_CLI, hooks a no-op until opted in). It also covers the doctor check.
  • New test/init-next-steps.test.ts (5 tests).
  • Updated: test/cli/upgrade-portability.test.ts now asserts the portable form and that no machine path is present; mcp-spec-init source check; the Copilot snapshot in host-projection guard 1 (.mcp.json hash only: the launch command is the intended change).
  • Manual: squad init --client claude from a source build in a scratch repo writes no /Users/ path anywhere. The hooks come out in $CLAUDE_PROJECT_DIR form and one runs from another directory. The launcher skips the Homebrew preview.3 on PATH and prints install instructions.
  • Full npm test locally: 13,432 passed, 32 failed. The failures are the known macOS ones (export-import, upstream-, memory-, profile-fs-guard), files that fail only under full-suite load (they pass alone: hostile-integration, squad-observer, gh-aw), and three that fail the same way on a clean origin/dev worktree (the squad-20 monorepo-subfolder and satellite-repo bridge scenarios, and upgrade-profile-teamroot).
  • npm run lint clean; docs markdownlint and cspell clean for the changed files.

Docs: features/mcp.md explains the launcher; scenarios/troubleshooting.md covers "squad_state or Claude hooks fail after cloning". There are changesets and [Unreleased] CHANGELOG entries; no version bump.

🤖 Generated with Claude Code

bradygaster and others added 2 commits November 11, 2026 19:05
`squad init --client claude` ended with "Run copilot --agent squad" and a
Copilot-only tip. initNextSteps builds the closing lines from the chosen
hosts: Claude Code gets `claude --agent squad --mcp-config .mcp.json` (or
`claude` in plugin mode) and a matching `claude -p` tip, other hosts get
one line each, and no --client still means Copilot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2462)

With the CLI installed, init and upgrade wrote `node /abs/cli-entry.js
state-mcp` into .mcp.json, the other hosts' MCP configs and every Claude
hook, so other clones failed and the commit leaked local paths.

Committed files now come from resolveCommittedSquadStateMcpSpec:
- an installed CLI becomes `node .squad/bin/squad.mjs state-mcp`, a
  launcher generated from the plugin launcher's template that finds a
  CLI per machine (SQUAD_CLI, the repo's node_modules, squad on PATH;
  npx only with SQUAD_ALLOW_NPX=1) and skips older CLIs
- a standalone bundle becomes `squad state-mcp`; pinned npx is kept
- Claude hooks run the launcher through "$CLAUDE_PROJECT_DIR", and
  isSquadHookHandler recognises that exact form for refresh and rollback

`squad doctor` gains a portable-launch check. The plugin launcher is
regenerated from the shared template (same behaviour, still preview.4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI auto review requested due to automatic review settings November 12, 2026 03:05
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #2470

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 25
Files added 4
Files modified 21
Files deleted 0
Modules touched 6

🎯 Risk Factors

  • 25 files changed (21-50 → HIGH)
  • 6 modules touched (5-8 → HIGH)

📦 Modules Affected

docs (3 files)
  • docs/src/content/docs/features/mcp.md
  • docs/src/content/docs/scenarios/troubleshooting.md
  • docs/src/content/docs/scenarios/upgrading.md
root (5 files)
  • .changeset/init-next-steps.md
  • .changeset/portable-launch.md
  • CHANGELOG.md
  • plugins/squad/scripts/squad.mjs
  • test-fixtures/spec/suite-v0.2/upgrade-matrix.json
scripts (1 file)
  • scripts/docs-site-location.mjs
squad-cli (7 files)
  • packages/squad-cli/src/cli/commands/client.ts
  • packages/squad-cli/src/cli/commands/doctor.ts
  • packages/squad-cli/src/cli/core/clients.ts
  • packages/squad-cli/src/cli/core/init.ts
  • packages/squad-cli/src/cli/core/mcp-root.ts
  • packages/squad-cli/src/cli/core/mcp-spec.ts
  • packages/squad-cli/src/cli/core/upgrade.ts
squad-sdk (2 files)
  • packages/squad-sdk/src/client/adapters/claude-hooks.ts
  • packages/squad-sdk/src/client/adapters/claude-plugin.ts
tests (7 files)
  • test/__snapshots__/host-projection-regression-guards.test.ts.snap
  • test/cli/upgrade-portability.test.ts
  • test/docs-site-location.test.ts
  • test/init-next-steps.test.ts
  • test/mcp-spec-init.test.ts
  • test/portable-launch.test.ts
  • test/squad-20-upgrade-matrix.test.ts

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

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

🏗️ Architectural Review

Authoritative evidence: scanners from the PR base commit, posted by the default-branch workflow_run reporter.
Results for head commit 18e429fb5cc462ecc840bfb704828b0146935c24.

⚠️ Architectural review: 2 warning(s).

Severity Category Finding Files
🟡 warning bootstrap-area 5 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/clients.ts, packages/squad-cli/src/cli/core/init.ts, packages/squad-cli/src/cli/core/mcp-root.ts, packages/squad-cli/src/cli/core/mcp-spec.ts, packages/squad-cli/src/cli/core/upgrade.ts
🟡 warning sweeping-refactor This PR touches 25 files (25 modified/added, 0 deleted). Large PRs are harder to review — consider splitting if possible. —

Automated architectural review — informational only.

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

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

PR Scope: 📦🔧 Mixed (product + infrastructure)

⚠️ 3 item(s) to address before review

Status Check Details
❌ Single commit 8 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 1 active Copilot thread(s) resolved (4 outdated skipped)
❌ CI passing 1 check(s) still running

Files Changed (25 files, +1115 −337)

File +/−
.changeset/init-next-steps.md +5 −0
.changeset/portable-launch.md +6 −0
CHANGELOG.md +9 −1
docs/src/content/docs/features/mcp.md +2 −0
docs/src/content/docs/scenarios/troubleshooting.md +10 −0
docs/src/content/docs/scenarios/upgrading.md +8 −7
packages/squad-cli/src/cli/commands/client.ts +4 −2
packages/squad-cli/src/cli/commands/doctor.ts +78 −0
packages/squad-cli/src/cli/core/clients.ts +2 −2
packages/squad-cli/src/cli/core/init.ts +55 −10
packages/squad-cli/src/cli/core/mcp-root.ts +2 −2
packages/squad-cli/src/cli/core/mcp-spec.ts +142 −2
packages/squad-cli/src/cli/core/upgrade.ts +10 −2
packages/squad-sdk/src/client/adapters/claude-hooks.ts +32 −7
packages/squad-sdk/src/client/adapters/claude-plugin.ts +109 −28
plugins/squad/scripts/squad.mjs +22 −5
scripts/docs-site-location.mjs +0 −5
test-fixtures/spec/suite-v0.2/upgrade-matrix.json +248 −247
test/__snapshots__/host-projection-regression-guards.test.ts.snap +1 −1
test/cli/upgrade-portability.test.ts +9 −5
test/docs-site-location.test.ts +0 −9
test/init-next-steps.test.ts +80 −0
test/mcp-spec-init.test.ts +2 −1
test/portable-launch.test.ts +277 −0
test/squad-20-upgrade-matrix.test.ts +2 −1

Total: +1115 −337


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

…ed (#2462)

The upgrade matrix caught two gaps:
- On a machine where the CLI runs only by path (no `squad` on PATH, no
  project install), the repository launcher found nothing and the
  squad_state probe failed right after upgrade. init/upgrade now record
  the CLI entry in `<Squad home>/cli.json` (outside the repository), and
  both launchers try it after PATH, with the same version floor.
- `.squad/bin/squad.mjs` is a Squad-owned path a plain upgrade may
  create: add it to the matrix fixture's squadOwned list and the
  upgrading guide. The recorded runs are refolded in a follow-up commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bradygaster

Copy link
Copy Markdown
Collaborator Author

Status: the upgrade matrix (ubuntu) caught two real gaps, fixed in f3ac9bd: the launcher now also finds the CLI that ran squad init/upgrade on this machine (recorded in ~/.squad/cli.json, outside the repo), and .squad/bin/squad.mjs is declared Squad-owned in the matrix fixture. Changing the fixture changes the matrix harness hash, so upgrade-matrix.json integrity will fail until the recorded Linux and Windows runs are refolded from this run's artifacts (render-upgrade-matrix.mjs --merge); that commit follows.

🤖 Generated with Claude Code

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.

🟡 Changes recommended

Unresolved launcher flaws permit executable writes outside the workspace through symlinks and can exclude the required script from commits, breaking fresh clones.

5 open issues
What changed in this PR

This PR addresses two Claude Code first-run issues (#2462 and #2453): machine-specific launch commands in committed files and init instructions that recommend the wrong host.

Changes:

  • Introduces a shared repository launcher and routes committed MCP configurations and Claude hooks through portable commands.
  • Adds portability diagnostics to squad doctor and host-aware next steps to squad init.
  • Adds regression coverage, documentation, and release notes.
File Description
test/​portable-launch.test.ts Tests portable specs, launcher behavior, hooks, and diagnostics.
test/​mcp-spec-init.test.ts Accepts the committed-file resolver in init.
test/​init-next-steps.test.ts Tests host-specific instructions and plugin mode.
test/​cli/​upgrade-portability.test.ts Checks portable upgrade output and launcher creation.
test/​__snapshots__/​host-projection-regression-guards.test.ts.snap Updates the MCP configuration snapshot hash.
plugins/​squad/​scripts/​squad.mjs Regenerates the plugin launcher with shared project lookup.
packages/​squad-sdk/​src/​client/​adapters/​claude-plugin.ts Shares launcher generation between plugins and repositories.
packages/​squad-sdk/​src/​client/​adapters/​claude-hooks.ts Generates and recognizes repository-launcher hook commands.
packages/​squad-cli/​src/​cli/​core/​upgrade.ts Uses and describes portable launch specs during upgrade.
packages/​squad-cli/​src/​cli/​core/​mcp-spec.ts Resolves committed specs and writes repository launchers.
packages/​squad-cli/​src/​cli/​core/​mcp-root.ts Marks portable specs as non-machine-specific.
packages/​squad-cli/​src/​cli/​core/​init.ts Uses portable specs and prints host-aware next steps.
packages/​squad-cli/​src/​cli/​core/​clients.ts Uses portable specs for client synchronization.
packages/​squad-cli/​src/​cli/​commands/​doctor.ts Adds committed-launch portability diagnostics.
packages/​squad-cli/​src/​cli/​commands/​client.ts Resolves portable specs without writing during client listing.
docs/​src/​content/​docs/​scenarios/​troubleshooting.md Documents recovery from clone-related launch failures.
docs/​src/​content/​docs/​features/​mcp.md Explains launcher resolution and installation options.
CHANGELOG.md Records portability and host-instruction fixes.
.changeset/​portable-launch.md Adds SDK and CLI patch release notes.
.changeset/​init-next-steps.md Adds CLI release notes for host-aware instructions.

Review effort: Balanced (auto)

Comment thread packages/squad-cli/src/cli/core/init.ts Resolved
bradygaster and others added 3 commits November 11, 2026 19:15
- Launcher writes go through resolveInsideRoot, so a symlinked squad
  directory or a dangling link at the launcher path can't redirect the
  write outside the workspace; Squad keeps the previous spec instead.
- The launcher moves to `.squad/squad.mjs`: many repositories ignore
  `bin/`, which would leave `.squad/bin/squad.mjs` uncommitted.
- A CLI running from a package runner's cache (npm `_npx`, pnpm `dlx`,
  yarn `dlx-`/`xfs-`) keeps the exact-version npx command and is not
  recorded in cli.json: that install is gone after the command exits.
- doctor recognises Windows and POSIX absolute paths on every OS and
  only checks the command on disk when it is absolute (a bare `node` is
  found on PATH).
- init's next steps use the clients and plugin mode .squad/config.json
  records when no flag is given, so re-running init stays accurate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repository moved from bradygaster/squad to github/squad. The API no
longer serves the 1.0.1 and 1.1.0 release assets under the old name
(HTTP 404), so those rows were silently skipping; the asset ids and
sha256s are unchanged under github/squad. Recorded runs are refolded
from this head's matrix run in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Linux and Windows re-recorded from the pull-request matrix run at
9b254ba (both 35 pass, 7 skipped, the same coverage as before), folded
with render-upgrade-matrix.mjs's mergeRun, and the "Tested upgrade paths"
section re-rendered. Only runs data and the generated docs change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bradygaster

Copy link
Copy Markdown
Collaborator Author

Refolded the upgrade matrix (a7f53bdf): Linux and Windows re-recorded at 9b254ba, both 35 pass / 7 skipped, the same coverage as the previous recorded runs. Along the way the 1.0.1 and 1.1.0 release rows were found silently skipping on dev too: the fixture fetched assets from bradygaster/squad, which now 404s since the repository moved; they now fetch from github/squad (same asset ids and sha256s) and pass again.

🤖 Generated with Claude Code

bradygaster and others added 2 commits November 11, 2026 19:42
The refolded upgrade matrix links to github/squad runs and releases, so
its generated section keeps no bradygaster/squad links and the
HISTORICAL_REPO_LINKS entry for upgrading.md was flagged as unused.
Remove it and the test cases that used it as their example; whatsnew.md
still covers section-scoped exemptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bradygaster
bradygaster merged commit 308a7ab into dev Nov 12, 2026
31 checks passed
bradygaster added a commit that referenced this pull request Nov 12, 2026
~/.squad/cli.json (since #2470) is found by the launcher, so tests that
expect the npx fallback or a missing CLI failed on any machine that had
run squad init from a source build. Point SQUAD_HOME at an empty dir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bradygaster added a commit that referenced this pull request Nov 12, 2026
…ested on Windows (#2483)

* fix(claude): repository launcher finds squad on PATH; Windows argv test

The repo launcher skipped every PATH entry because realpath of its
(nonexistent) .squad/bin threw inside the comparison. New
test/launcher-argv.test.ts checks hostile argv round-trips through a
global squad, npx, SQUAD_CLI and a project install, through .cmd shims
on Windows without cmd.exe. A new workflow runs the launcher tests on
Windows and Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(claude): launcher tests ignore the developer's recorded CLI

~/.squad/cli.json (since #2470) is found by the launcher, so tests that
expect the npx fallback or a missing CLI failed on any machine that had
run squad init from a source build. Point SQUAD_HOME at an empty dir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: avoid a retired term in a test name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(release): explain the Windows-only launcher-argv skip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.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

2 participants