feat(watch): next-gen watch with execute redesign, robustness, and PS1 parity#830
feat(watch): next-gen watch with execute redesign, robustness, and PS1 parity#830tamirdresher merged 51 commits intodevfrom
Conversation
…775) Adds --dispatch-mode flag to squad watch --execute with three modes: - task (default): existing Promise.all behavior, unchanged - fleet: all issues dispatched via single /fleet prompt, true parallelism - hybrid: read-heavy issues via /fleet, write-heavy via task tool + worktrees Benchmark results (4 real issues): - Fleet: 116s total (29s/issue avg) - Sequential: 332s total (83s/issue avg) - Fleet is 2.9x faster, ~25% cheaper on premium requests Key finding: /fleet ignores custom agents (.github/agents/) and spawns generic explore agents. This makes fleet ideal for read-only analysis (triage, research, reviews) but NOT for charter-driven code changes. New files: - fleet-dispatch.ts: FleetDispatchCapability with /fleet prompt builder - Updated execute.ts: issue classification (read vs write keywords) - Updated config.ts: DispatchMode type + config field - Updated cli-entry.ts: --dispatch-mode flag parsing Closes #775 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove shell:true and stdio from execSync options (fixes overload mismatch) - Add .changeset/fleet-dispatch-hybrid.md for changelog-gate CI check - Build verified locally: tsc --noEmit passes clean Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. deferredToFleet count: use executable.length in fleet mode (was read-only count) 2. Warning log when fleet/hybrid defers but fleet-dispatch may not be enabled 3. Command injection fix: execFileSync with args array instead of shell command 4. requires: added 'copilot' alongside 'gh' 5. --dispatch-mode validation: error on invalid values, default to task 6. Unit tests: 11 tests for classifyIssue() covering all keyword categories Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…791) - New cleanup capability in housekeeping phase - Clears .squad/.scratch/ temp files - Archives old orchestration-log and session-log entries (>30d) - Warns about stale decision inbox files (>7d) - Configurable: everyNRounds, maxAgeDays - 12 new tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Default 'important': only print rounds with actual work items - 'all': old behavior (every round including empty) - 'none': suppress all round output - Add machine name + repo name to round headers for attribution - Configurable via .squad/config.json watch.notifyLevel - 5 new tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- notification-level.md: full feature doc for --notify-level - ralph.md: add --notify-level + --cleanup to capabilities table - 030-v092-whats-coming.md: blog post covering all 10 features in next release Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
captureBoardOutput now defaults to 'all' mode to preserve existing test behavior. The production default is 'important' but tests need to verify clear-board message formatting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CSpell docs-quality CI was failing because 'WCBED' is an unknown word. Replaced CPC-tamir-WCBED with DEVBOX-01 in the blog post and notification-level feature doc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds --health flag to squad watch that reports: - Whether a watch instance is running (PID tracking) - Uptime, auth account, interval, capabilities - Auth drift detection (expected vs current gh account) - Stale PID cleanup for crashed instances Writes .squad/.watch-pid.json at startup, cleaned on exit/SIGINT. Auth guard captures active gh user at startup and restores on drift. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds StateBackend interface with three implementations: - WorktreeBackend: default, reads/writes .squad/ on disk - GitNotesBackend: stores state in refs/notes/squad - OrphanBranchBackend: stores state on squad-state orphan branch Also adds: - resolveStateBackend() config resolution with fallback - --state-backend CLI flag for init and watch commands - stateBackend field in SquadDirConfig and WatchConfig - Unit tests covering all backends and config resolution Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Moves .squad/.watch-pid.json → {os.tmpdir()}/squad-watch-{hash}.json
to prevent accidental git commits. Uses MD5 hash of repo path for
worktree/multi-clone safety. Cross-platform via os.tmpdir().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add warning log when dispatchMode=fleet/hybrid but fleet-dispatch cap is not enabled - Add comment documenting --dispatch-mode runtime validation - Re-export classifyIssue from watch/index barrel - Add classifyIssue unit tests for dispatch-mode categories - Pass dispatchMode through to capability synthesized config Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove unused fs import from watch/index.ts - Fix SIGINT exit code to 128+2 (130), SIGTERM to 128+15 (143) - Replace (adapter as any) with proper type narrowing via optional method check - Handle EPERM in isProcessAlive (process exists but no permission) - Add startedAt validation guard before computing uptime - Wire --health flag in cli-entry.ts - Add doc comment for probeCurrentGhUser stderr handling - Add EPERM test case for isProcessAlive Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename 'archive' to 'prune' in all log messages and comments - Use rmSync with recursive to handle subdirectories in .scratch/ - Add Number.isFinite and >0 guards to parseConfig validation - Use Date.now() consistently for UTC cutoff calculation - Fix changeset: correct test count from 11 to 12 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add restart message after --self upgrade (stale code warning) - Fix permission handling: only suggest sudo for npm, not pnpm/yarn - Warn when --insider used without --self - Add test verifying selfUpgradeCli is called with correct args - Export selfUpgradeCli, call it from cli-entry before runUpgrade (exit early) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix shell injection in gitExec: use execFileSync with args array
- Add path traversal validation in WorktreeBackend (reject .. in paths)
- Fix GitNotes list('') to return root entries (empty prefix match)
- Fix prototype pollution: use Object.hasOwn() instead of in operator
- Validate --state-backend value and reject flag-as-value
- Add gitExecContent with trimEnd() for content reads
- Add comment documenting config merge-not-overwrite pattern
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add JSDoc to reportBoard() documenting default notifyLevel='important' - Include machine/repo context in 'Board is clear' message for all modes - Add comment noting verbose flag parsing is in PR #782 - Replace console.log direct mutation with vi.spyOn in tests - Update changeset to clarify attribution is in round headers and clear message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es PS1 design) The execute capability now passes ALL squad issues to the agent and lets the agent decide what to work on, matching the PS1 ralph-watch behavior. The prompt includes Task/WHY/Success/Escalation structure and references .squad/ralph-instructions.md for full instructions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es PS1 design) The execute capability now passes ALL squad issues to the agent and lets the agent decide what to work on, matching the PS1 ralph-watch behavior. The prompt includes Task/WHY/Success/Escalation structure and references .squad/ralph-instructions.md for full instructions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4-tier remediation: reset CB → re-probe auth → git pull → pause 30m. Consecutive failures escalate; successes de-escalate. Startup auth check now attempts repair before hard-failing. Sentinel file + overnight window checks at round start. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Self-pull now stashes local changes before pulling and pops after. Detects when watch source files changed and warns to restart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Watch now supports --overnight-start/--overnight-end to pause during off-hours and checks .squad/ralph-stop sentinel file for graceful shutdown. Config interface extended with overnightStart, overnightEnd, sentinelFile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eIssues The PR #776 simplified findExecutableIssues to only filter by squad label (intentionally minimal), but this dropped two pre-existing filters that the test suite expects: - exclude issues already assigned to a human - exclude issues with status:blocked (or similar blocking labels) These filters match the PS1 ralph-watch pre-filter logic and are the right behavior — the agent should only receive clearly actionable issues. Fixes test: CLI: watch execute mode > findExecutableIssues > returns only issues ready for execution
…ntext.ps1 Built-in auth context switching through the adapter abstraction: - GitHubAdapter: parses remote URL, checks gh auth, auto-switches if mismatch - AzureDevOpsAdapter: parses remote URL, configures az devops defaults - Cross-platform (no PS1/bash deps), non-fatal on failure - Tiered remediation Tier 2 now uses adapter.ensureAuth() instead of hardcoded user Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dygaster/squad into squad/watch-next-gen
…attern in execute.ts
🛫 PR Readiness Check
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 51 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 | 6 unresolved Copilot thread(s) — fix and resolve before merging |
| ❌ | CI passing | 14 check(s) still running |
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🏗️ Architectural Review
Automated architectural review — informational only. |
🟠 Impact Analysis — PR #830Risk tier: 🟠 HIGH 📊 Summary
🎯 Risk Factors
📦 Modules Affecteddocs (4 files)
root (7 files)
squad-cli (8 files)
squad-sdk (6 files)
tests (7 files)
|
There was a problem hiding this comment.
Pull request overview
This PR delivers a “next-gen” squad watch experience, expanding the watch loop with new execution/dispatch behaviors, robustness features, health reporting, and related SDK support (scratch dir + git-native state backends), plus docs and test coverage.
Changes:
- Add watch enhancements: notify-level reporting,
/fleetdispatch capability, cleanup capability, auth guard, and health check PID tracking. - Introduce SDK utilities: scratch directory helpers and git-native state backends (git-notes + orphan branch).
- Add CLI self-upgrade support and accompanying docs/changesets/tests.
Reviewed changes
Copilot reviewed 37 out of 38 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
test/watch-notify-level.test.ts |
Adds unit tests for notify-level output suppression/attribution. |
test/watch-fleet-dispatch.test.ts |
Adds unit tests for classifyIssue() read/write categorization. |
test/watch-cleanup.test.ts |
Adds unit tests for the new cleanup capability behavior. |
test/state-backend.test.ts |
Adds unit tests for new SDK state backends + resolver. |
test/scratch-dir.test.ts |
Adds unit tests for scratchDir() / scratchFile() helpers. |
test/ralph-board.test.ts |
Updates existing board-output test helper to pass notifyLevel. |
test/cli/watch-health.test.ts |
Adds tests for watch health check + PID file behaviors. |
test/cli/watch-fleet-dispatch.test.ts |
Adds CLI-level tests for dispatch classification. |
test/cli/watch-execute.test.ts |
Extends execute-mode tests to cover classification and routing expectations. |
test/cli/upgrade.test.ts |
Adds a test for upgrade --self behavior. |
packages/squad-sdk/src/state-backend.ts |
Implements Worktree/GitNotes/OrphanBranch state backends + resolver. |
packages/squad-sdk/src/resolution.ts |
Adds scratchDir() and scratchFile() utilities and stateBackend config field. |
packages/squad-sdk/src/platform/types.ts |
Extends platform adapter interface with optional ensureAuth(). |
packages/squad-sdk/src/platform/github.ts |
Implements GitHub ensureAuth() auto-switch behavior. |
packages/squad-sdk/src/platform/azure-devops.ts |
Implements ADO ensureAuth() org default configuration. |
packages/squad-sdk/src/index.ts |
Exports scratch helpers and state backend APIs from SDK barrel. |
packages/squad-sdk/src/config/init.ts |
Ensures .squad/.scratch/ exists and is gitignored on init. |
packages/squad-cli/src/cli/core/upgrade.ts |
Adds selfUpgradeCli() for upgrading the CLI package itself. |
packages/squad-cli/src/cli/commands/watch/index.ts |
Adds notify-level reporting, auth guard, PID file integration, remediation tiers, and re-exports. |
packages/squad-cli/src/cli/commands/watch/health.ts |
Implements --health status output via PID file in OS temp. |
packages/squad-cli/src/cli/commands/watch/config.ts |
Extends watch config with notifyLevel/verbose/dispatch/etc. |
packages/squad-cli/src/cli/commands/watch/capabilities/self-pull.ts |
Enhances self-pull to stash/pull/pop + restart warning. |
packages/squad-cli/src/cli/commands/watch/capabilities/index.ts |
Registers new fleet-dispatch and cleanup capabilities. |
packages/squad-cli/src/cli/commands/watch/capabilities/fleet-dispatch.ts |
Adds /fleet batching dispatch capability. |
packages/squad-cli/src/cli/commands/watch/capabilities/execute.ts |
Redesigns execute behavior to build a richer prompt and classify issues. |
packages/squad-cli/src/cli/commands/watch/capabilities/cleanup.ts |
Adds housekeeping cleanup capability for scratch/log/inbox. |
packages/squad-cli/src/cli-entry.ts |
Adds --health, --self/--insider, and begins parsing new watch flags. |
packages/squad-cli/package.json |
Exports the new watch health module entrypoint. |
docs/src/content/docs/features/ralph.md |
Updates Ralph/watch docs to mention notify-level and cleanup. |
docs/src/content/docs/features/notification-level.md |
Adds a dedicated feature doc page for notify-level. |
docs/src/content/blog/030-v092-whats-coming.md |
Adds a draft blog post highlighting the upcoming features. |
.gitignore |
Minor formatting change (blank line). |
.changeset/watch-health.md |
Changeset entry for watch health feature. |
.changeset/scratch-dir-utility.md |
Changeset entry for scratch dir utility. |
.changeset/quiet-notifications.md |
Changeset entry for notify-level feature. |
.changeset/git-state-backends.md |
Changeset entry for git-native state backends. |
.changeset/fleet-dispatch-hybrid.md |
Changeset entry for fleet/hybrid dispatch. |
.changeset/cleanup-ceremony.md |
Changeset entry for cleanup capability. |
| */ | ||
| export function isProcessAlive(pid: number): boolean { | ||
| try { | ||
| process.kill(pid, 0); | ||
| return true; | ||
| } catch { |
There was a problem hiding this comment.
isProcessAlive() treats any process.kill(pid, 0) error as “dead”, but on some platforms (or when lacking permissions) kill throws EPERM even though the process exists. This breaks the intended behavior (and the added test). Handle EPERM as “alive” and only return false for ESRCH (and other definitive non-existence cases).
| */ | |
| export function isProcessAlive(pid: number): boolean { | |
| try { | |
| process.kill(pid, 0); | |
| return true; | |
| } catch { | |
| * `EPERM` means the process exists but cannot be signaled by this user. | |
| */ | |
| export function isProcessAlive(pid: number): boolean { | |
| try { | |
| process.kill(pid, 0); | |
| return true; | |
| } catch (error) { | |
| const code = typeof error === 'object' && error !== null && 'code' in error | |
| ? (error as { code?: string }).code | |
| : undefined; | |
| if (code === 'EPERM') { | |
| return true; | |
| } | |
| if (code === 'ESRCH') { | |
| return false; | |
| } |
| function probeCurrentGhUser(): string | undefined { | ||
| try { | ||
| const result = execFileSync('gh', ['auth', 'status', '--active'], { | ||
| encoding: 'utf-8', | ||
| stdio: ['pipe', 'pipe', 'pipe'], | ||
| }); | ||
| const match = result.match(/account\s+(\S+)/); | ||
| return match?.[1]; | ||
| } catch (e) { | ||
| const stderr = (e as { stderr?: string }).stderr ?? ''; | ||
| const match = stderr.match(/account\s+(\S+)/); | ||
| return match?.[1]; | ||
| } |
There was a problem hiding this comment.
probeCurrentGhUser() (and the similar helper in watch/index.ts) only inspects stdout from gh auth status --active, but execFileSync() does not provide stderr on success. Since gh auth status frequently writes its status text to stderr even with exit code 0, this can return undefined even when authenticated. Consider using spawnSync/execFileSync via spawnSync result to parse both stdout+stderr on success as well as failure.
packages/squad-cli/src/cli-entry.ts
Outdated
| @@ -377,6 +443,12 @@ async function main(): Promise<void> { | |||
| if (args.includes(`--no-${cap.name}`)) capabilities[cap.name] = false; | |||
| } | |||
|
|
|||
| // --state-backend flag for watch command | |||
| const watchStateBackendIdx = args.indexOf('--state-backend'); | |||
| const rawWatchStateBackend = (watchStateBackendIdx !== -1 && args[watchStateBackendIdx + 1]) | |||
| ? args[watchStateBackendIdx + 1] as string | |||
| : undefined; | |||
|
|
|||
| // Legacy flag compat: --board-project sets board sub-option | |||
| const boardProjectIdx = args.indexOf('--board-project'); | |||
| if (boardProjectIdx !== -1 && args[boardProjectIdx + 1]) { | |||
| @@ -386,6 +458,14 @@ async function main(): Promise<void> { | |||
| : { projectNumber: parseInt(args[boardProjectIdx + 1]!, 10) }; | |||
| } | |||
|
|
|||
| // Notify level: --notify-level all|important|none (default: important) | |||
| const notifyLevelIdx = args.indexOf('--notify-level'); | |||
| const notifyLevelArg = (notifyLevelIdx !== -1 && args[notifyLevelIdx + 1]) ? args[notifyLevelIdx + 1] : undefined; | |||
| const notifyLevel = (notifyLevelArg === 'all' || notifyLevelArg === 'important' || notifyLevelArg === 'none') | |||
| ? notifyLevelArg : undefined; | |||
|
|
|||
| // verbose flag parsing is in PR #782 (--verbose) | |||
|
|
|||
| // Load config: .squad/config.json merged with CLI overrides | |||
| const config = loadWatchConfig(process.cwd(), { | |||
| interval, | |||
| @@ -394,6 +474,7 @@ async function main(): Promise<void> { | |||
| timeout, | |||
| copilotFlags, | |||
| agentCmd, | |||
| stateBackend: rawWatchStateBackend as any, | |||
| capabilities: Object.keys(capabilities).length > 0 ? capabilities : undefined, | |||
| }); | |||
There was a problem hiding this comment.
Several new watch CLI flags are parsed (--notify-level, --overnight-start/--overnight-end, --sentinel-file, --auth-user), but the parsed values are never included in the cliOverrides passed to loadWatchConfig(). As a result, the flags have no effect at runtime. Also, --dispatch-mode is documented in the PR but is not parsed here at all, so config.dispatchMode can only be set via config file.
packages/squad-cli/src/cli-entry.ts
Outdated
| // --state-backend: write stateBackend into .squad/config.json on init | ||
| const stateBackendIdx = args.indexOf('--state-backend'); | ||
| const stateBackendVal = (stateBackendIdx !== -1 && args[stateBackendIdx + 1]) | ||
| ? args[stateBackendIdx + 1] | ||
| : undefined; | ||
|
|
||
| // Global init: suppress workflows (no GitHub CI in ~/.config/squad/) and bootstrap personal squad | ||
| runInit(dest, { includeWorkflows: !noWorkflows && !hasGlobal, sdk, roles, isGlobal: hasGlobal }).catch(err => { | ||
| runInit(dest, { includeWorkflows: !noWorkflows && !hasGlobal, sdk, roles, isGlobal: hasGlobal }).then(async () => { | ||
| if (stateBackendVal) { | ||
| const { join } = await import('node:path'); | ||
| const { existsSync, readFileSync, writeFileSync, mkdirSync } = await import('node:fs'); | ||
| const squadDir = join(dest, '.squad'); | ||
| if (!existsSync(squadDir)) mkdirSync(squadDir, { recursive: true }); | ||
| const configPath = join(squadDir, 'config.json'); | ||
| // Read existing config first, then merge (avoids overwriting unrelated keys) | ||
| let config: Record<string, unknown> = {}; | ||
| try { | ||
| if (existsSync(configPath)) { | ||
| config = JSON.parse(readFileSync(configPath, 'utf-8')); | ||
| } | ||
| } catch { /* fresh config */ } | ||
| config['stateBackend'] = stateBackendVal; | ||
| writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n'); | ||
| console.log(`✓ State backend set to '${stateBackendVal}' in .squad/config.json`); | ||
| } |
There was a problem hiding this comment.
--state-backend is written verbatim into .squad/config.json without validation. This allows invalid values that will later be ignored/fall back, but it’s better UX to validate upfront (worktree|external|git-notes|orphan) and fail fast with a clear message rather than persisting a bad config.
| export async function selfUpgradeCli(options: { insider?: boolean; force?: boolean } = {}): Promise<void> { | ||
| const { execSync } = await import('node:child_process'); | ||
| const tag = options.insider ? 'insider' : 'latest'; | ||
| const pkg = `@bradygaster/squad-cli@${tag}`; | ||
| const pm = detectPackageManager(); | ||
|
|
||
| let cmd: string; | ||
| switch (pm) { | ||
| case 'pnpm': | ||
| cmd = `pnpm add -g ${pkg}`; | ||
| break; | ||
| case 'yarn': | ||
| cmd = `yarn global add ${pkg}`; | ||
| break; | ||
| default: | ||
| cmd = `npm install -g ${pkg}`; | ||
| break; | ||
| } | ||
|
|
||
| info(`Self-upgrading via ${pm}: ${cmd}`); | ||
|
|
||
| try { | ||
| execSync(cmd, { stdio: 'inherit' }); | ||
| } catch { | ||
| // Only suggest sudo for npm — pnpm/yarn rarely need it | ||
| if (pm === 'npm') { | ||
| warn(`Permission denied. Try: sudo ${cmd}`); | ||
| } else { | ||
| warn(`Upgrade failed. Check ${pm} permissions or try running manually: ${cmd}`); | ||
| } | ||
| } |
There was a problem hiding this comment.
selfUpgradeCli() builds a single shell command string and runs it via execSync(cmd), which (1) routes through the shell, (2) makes it harder to test/mocks (the added test spies on execFileSync, so it won’t observe this), and (3) ignores the force option entirely. Prefer execFileSync() with an args array (npm/pnpm/yarn + args), and wire options.force into the appropriate flag (e.g. --force for npm) so behavior matches the function signature and tests can reliably assert the invocation.
| @@ -0,0 +1,152 @@ | |||
| /** | |||
There was a problem hiding this comment.
File appears to start with a UTF-8 BOM / zero-width character before /** (shown as /**). This can cause noisy diffs and occasional tooling issues (linters, shebang parsing, etc.). Please remove the BOM so the file begins with a plain /**.
| /** | |
| /** |
| @@ -0,0 +1,12 @@ | |||
| --- | |||
There was a problem hiding this comment.
This changeset file also appears to start with a UTF-8 BOM (---). Please remove the BOM so frontmatter parsing remains consistent across tools and platforms.
| --- | |
| --- |
|
|
||
| Adds `squad watch --health` to display the status of a running watch | ||
| instance: PID, uptime, auth account, capabilities, and auth drift | ||
| detection. Writes `.squad/.watch-pid.json` at startup for instance |
There was a problem hiding this comment.
The changeset text says the PID file is written to .squad/.watch-pid.json, but the implementation writes to an OS temp file (os.tmpdir()/squad-watch-{hash}.json) via getPidPath(). Please update the changeset (and any docs) to reflect the actual location, or change the implementation to match the documented .squad/ path.
| detection. Writes `.squad/.watch-pid.json` at startup for instance | |
| detection. Writes a watch PID file in the OS temp directory | |
| (`os.tmpdir()/squad-watch-{hash}.json`) at startup for instance |
| // 3. Switch | ||
| try { | ||
| execFileSync('gh', ['auth', 'switch', '--user', targetUser], EXEC_OPTS); | ||
| console.log(`✅ Auth context switched to ${targetUser}`); |
There was a problem hiding this comment.
ensureAuth() logs directly to stdout (console.log("✅ Auth context switched…")). Since this is in the SDK adapter layer, it introduces an unconditional user-facing side effect from a library call (and will also trigger the repo’s no-console eslint warning for console.log). Prefer returning a status (e.g., switched/targetUser) and letting the CLI decide when/how to display it (or only log under a passed-in verbose/logger option).
| console.log(`✅ Auth context switched to ${targetUser}`); |
| export function getActiveGhUser(): string | undefined { | ||
| try { | ||
| const result = execFileSync('gh', ['auth', 'status', '--active'], { | ||
| encoding: 'utf-8', | ||
| stdio: ['pipe', 'pipe', 'pipe'], | ||
| }); | ||
| // gh auth status outputs: "Logged in to github.com account XXXXX ..." | ||
| // Capture stderr too — gh writes status info to both streams | ||
| const match = result.match(/account\s+(\S+)/); | ||
| return match?.[1]; | ||
| } catch (e) { | ||
| // gh auth status exits non-zero when not logged in, but still | ||
| // writes account info to stderr — try to parse it | ||
| const stderr = (e as { stderr?: string }).stderr ?? ''; | ||
| const match = stderr.match(/account\s+(\S+)/); | ||
| return match?.[1]; | ||
| } |
There was a problem hiding this comment.
getActiveGhUser() claims to “capture stderr too”, but execFileSync() only returns stdout on success; stderr is only accessible on thrown errors. Since gh auth status often writes its status to stderr even with exit code 0, this function can return undefined even when logged in, weakening auth-drift detection and PID info. Consider using spawnSync (so you can inspect both stdout and stderr on success) or a gh api user -q .login style query.
- Print 'Starting round N...' and 'Next poll at HH:MM' for user feedback - Print 'Running first check now...' before first round - --log-file mirrors all output to file with ISO timestamps (ANSI stripped) - Log file supports append mode for multi-session diagnostics Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
execFile buffers all output until the process exits, making the terminal appear frozen during agency runs. spawn with stdio:'inherit' streams output in real-time, matching the PS1's Start-Process -NoNewWindow behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bradygaster
left a comment
There was a problem hiding this comment.
once copilot's suggestions [that you agree with] are done i'm good here. thanks!
|
|
||
| Add cleanup watch capability for stale file housekeeping (#791) | ||
|
|
||
| - New `cleanup` capability in the `housekeeping` phase |
There was a problem hiding this comment.
i think we should make sure to differentiate between the cleanup and nap commands but, maybe i just need to RTFM. :)
- Fix isProcessAlive EPERM handling in watch/health.ts - Fix watch-cleanup test assertions (pruned vs archived) - Add vi import to upgrade.test.ts - Fix security review (git add . in blog prose) - Revert CHANGELOG.md (write-protected) - Add DEVBOX/myaccount to cspell dictionary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep both our selfUpgradeCli test and dev's casting tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Squad Watch — Next Generation
Combines 6 PRs into one cohesive watch feature set. This brings the CLI watch to near-parity with the PS1 ralph-watch reference implementation.
What's included
Execute redesign (was PR #776)
Robustness (was PR #782)
Capabilities (was PRs #794, #805)
Health (was PR #809)
State backends (was PR #810)
Design decisions
Closes #775 #781 #792 #793 #804 #807 #808
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com