Skip to content

feat: non-blocking version check on CLI startup (Phase 1)#367

Merged
bradygaster merged 1 commit intobradygaster:mainfrom
diberry:squad/self-update-startup-check
Mar 13, 2026
Merged

feat: non-blocking version check on CLI startup (Phase 1)#367
bradygaster merged 1 commit intobradygaster:mainfrom
diberry:squad/self-update-startup-check

Conversation

@diberry
Copy link
Copy Markdown
Collaborator

@diberry diberry commented Mar 13, 2026

Summary

Adds a background update check that runs when the interactive shell starts. If a newer version of Squad is available on npm, a passive notification banner is displayed.

Changes

  • New: packages/squad-cli/src/cli/self-update.ts — self-contained update check module
  • Modified: packages/squad-cli/src/cli-entry.ts — wired fire-and-forget check into no-args shell startup path

Design

Aspect Decision
Blocking Never — fire-and-forget promise, shell starts immediately
Cache 24-hour TTL in OS config dir (%APPDATA%, ~/.config, ~/Library/Application Support)
Timeout 3s fetch timeout via AbortController
Opt-out SQUAD_NO_UPDATE_CHECK=1 environment variable
Error handling Triple-wrapped — silent on any failure, never crashes CLI
Output Yellow banner: ⚡ Squad vX.Y.Z available (you have vA.B.C). Run: npm i -g @bradygaster/squad-cli

Roadmap: Phase 1 of 3

Phase 1 — Background check + notify ✅ (this PR)

Passive, non-blocking startup notification. No mutations, no prompts. The safest possible foundation.


Phase 2 — Explicit squad self-update command

Adds a user-invoked CLI command that updates the installed Squad CLI package itself, separate from the existing squad upgrade (which upgrades project files/templates).

Command surface:

  • squad self-update — update CLI to latest stable
  • squad self-update --check — check only, don't install
  • squad self-update --channel insider — update from a specific channel
  • squad upgrade --self — compatibility alias

Key design decisions:

  • CLI only — SDK updates remain explicit via package manager
  • Supported npm-global installs first — unsupported contexts (npx, local deps, pnpm/yarn globals, Homebrew) fall back to showing manual instructions
  • Never auto-elevate — if global install location isn't writable, print the manual command instead of prompting for sudo/admin
  • Global user config, not repo config — self-update settings live in user-scoped config, never in .squad/ or squad.config.ts
  • No hot-swap — installs the new package, then tells the user to restart Squad
  • Rollback built in — displays exact rollback command (e.g., npm install -g @bradygaster/squad-cli@<previous-version>)

Security:

  • Install exact versions only (no ranges)
  • Verify integrity hash for the exact package/version
  • Verify publish provenance/signatures where available
  • Enterprise support: version pinning, disablement, internal registries/mirrors, proxy/custom CA

Implementation approach:

  • Extends the existing upgrade.ts pluggable architecture (setVersionFetcher(), add setUpgradeInstaller())
  • Detects install context to determine if self-update is supported
  • Maps channels via npm dist-tags: latest, preview, insider
  • Spawns npm install -g @bradygaster/squad-cli@<exact-version> as child process

Prerequisites: Phase 1 plumbing (this PR), package-name consistency cleanup, global settings system.


Phase 3 — Optional auto-apply (user opt-in)

If the user or enterprise explicitly opts in, Squad can check for a new CLI version on startup and automatically apply it in supported environments.

⚠️ This phase is controversial — team consensus ranges from "feasible with strict guardrails" (EECOM, Network, RETRO) to "Squad should stop at prompt-to-install" (Flight). Final decision deferred until Phase 2 ships and real-world feedback is collected.

Key design decisions:

  • Off by default — notify remains the default policy forever
  • Explicit opt-in only — global user config or enterprise policy, never repo-local
  • CLI only — SDK never auto-updates
  • Stable channel only — preview/insider auto-apply requires separate explicit override
  • Apply after exit — don't replace in-process; especially important on Windows (file/shim locking)
  • Restart required — current process assumes it's still running old code
  • Concurrency protection — lock file with exclusive create + stale expiry

Security (strict requirements):

  • Everything from Phase 2, plus:
  • Integrity verification mandatory before any auto-apply
  • Never auto-elevate privileges from startup
  • Fail closed if elevation would be required
  • Enterprise controls: disable checks, block installs, pin exact version, force internal registry

Global settings schema:

{
  "selfUpdate": {
    "enabled": true,
    "mode": "notify | auto | prompt",
    "channel": "stable",
    "ttlHours": 24,
    "pinnedVersion": null,
    "registry": null
  }
}

Fallback behavior:

  • Unsupported install context → notify/manual only
  • Permission issue → show manual command, no elevation
  • Offline → skip silently
  • Failed auto-apply → passive error banner, don't block usage

Prerequisites: Phase 2 fully shipped, global settings/policy system, install-context detection, integrity + provenance verification, enterprise policy support, package identity cleanup.


No issue — this was identified through team analysis (Flight, EECOM, Network, RETRO) as a high-value minimal addition.

cc @bradygaster for review

Adds a background update check that runs when the interactive shell starts.
On startup, Squad checks the npm registry for a newer version and displays
a passive notification banner if one is available.

Key design decisions:
- Fire-and-forget: never blocks or delays shell startup
- 24-hour cache: avoids repeated network calls
- 3-second fetch timeout via AbortController
- Opt-out via SQUAD_NO_UPDATE_CHECK=1 env var
- Triple-wrapped error handling: silent on any failure

New file: packages/squad-cli/src/cli/self-update.ts
Modified: packages/squad-cli/src/cli-entry.ts (wired into no-args shell path)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bradygaster bradygaster merged commit 8795543 into bradygaster:main Mar 13, 2026
1 check passed
tamirdresher pushed a commit to tamirdresher/squad that referenced this pull request Mar 16, 2026
* chore(squad): quality review findings — 7 issues filed

Quality audit complete: 5 agents assessed CLI across testing, coverage, stability, accessibility, UX.
Results: 4 P0 blockers (bradygaster#365bradygaster#368), 3 P1 items (bradygaster#369bradygaster#371).
Blocking: Waingro dead sessions, ErrorBoundary, dropped input; Marquez help text consistency.

Changes:
- Logged session summary to .squad/log/2026-02-24T0205-quality-review-complete.md
- Updated .squad/identity/now.md with quality review findings and new issue numbers

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

* chore(squad): merge decision — Marquez UX audit findings

Quality assessment merged from inbox (Grade B): 11 improvements (3 P0, 4 P1, 4 P2). help text, stub commands, vocabulary, separators, roster.

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

* docs: update waingro history with hostile test coverage sprint

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

* fix: P0 quality fixes — ErrorBoundary, session eviction, input buffer, stub removal (closes bradygaster#365, closes bradygaster#366, closes bradygaster#367, closes bradygaster#371)

- Add ErrorBoundary class component wrapping App in Ink render call (bradygaster#365)
  Shows friendly message on crash, logs error to stderr
- Dead session eviction in dispatchToAgent/dispatchToCoordinator (bradygaster#366)
  Deletes dead session from Map on error so next attempt creates fresh one
- Input buffering while disabled in InputPrompt (bradygaster#367)
  Buffers keystrokes via useRef, restores on re-enable, no auto-submit
- Remove stub commands: loop, hire; wire triage/watch to real runWatch (bradygaster#371)
  Remove stub help text entries for loop and hire
- 10 new tests across repl-ux and cli-shell-comprehensive

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tamirdresher pushed a commit to tamirdresher/squad that referenced this pull request Mar 16, 2026
* chore(squad): quality review findings — 7 issues filed

Quality audit complete: 5 agents assessed CLI across testing, coverage, stability, accessibility, UX.
Results: 4 P0 blockers (bradygaster#365bradygaster#368), 3 P1 items (bradygaster#369bradygaster#371).
Blocking: Waingro dead sessions, ErrorBoundary, dropped input; Marquez help text consistency.

Changes:
- Logged session summary to .squad/log/2026-02-24T0205-quality-review-complete.md
- Updated .squad/identity/now.md with quality review findings and new issue numbers

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

* chore(squad): merge decision — Marquez UX audit findings

Quality assessment merged from inbox (Grade B): 11 improvements (3 P0, 4 P1, 4 P2). help text, stub commands, vocabulary, separators, roster.

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

* fix: P0 quality fixes — ErrorBoundary, session eviction, input buffer, stub removal (closes bradygaster#365, closes bradygaster#366, closes bradygaster#367, closes bradygaster#371)

- Add ErrorBoundary class component wrapping App in Ink render call (bradygaster#365)
  Shows friendly message on crash, logs error to stderr
- Dead session eviction in dispatchToAgent/dispatchToCoordinator (bradygaster#366)
  Deletes dead session from Map on error so next attempt creates fresh one
- Input buffering while disabled in InputPrompt (bradygaster#367)
  Buffers keystrokes via useRef, restores on re-enable, no auto-submit
- Remove stub commands: loop, hire; wire triage/watch to real runWatch (bradygaster#371)
  Remove stub help text entries for loop and hire
- 10 new tests across repl-ux and cli-shell-comprehensive

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

---------

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