feat(cli): make plan --complete-rollout automatable with --yes + honest exit (rc.2 M2)#686
Conversation
…nest exit (rc.2 M2) `--complete-rollout` skips the production-deploy gate, so it requires explicit consent. Previously that was an interactive p.confirm with no bypass: a non-interactive run auto-cancelled (default-no) and exited 0 via CancelledError — so CI/agents got exit 0 but no plan was drafted, and would assume one existed. - New `--yes` flag confirms the gate-skip without a prompt (automation path). - Non-interactive `--complete-rollout` WITHOUT `--yes` now refuses with a non-zero exit (CliExit(1)) and points at --yes, instead of silently cancelling with exit 0. An interactive decline stays exit 0 (a deliberate 'no' is not a failure). - isInteractive is computed once up front (the confirm needs it too). - Registry + help + `stash-cli` skill updated; messages.plan.* leaders added for e2e stability. Tests: 2 pty-less e2e (refuses+exit-1 without --yes; --yes confirms + proceeds), plus manifest resolves the new flag. Suite 540 unit / 64 e2e green, code:check clean. Changeset: stash minor. Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
🦋 Changeset detectedLatest commit: 9aa6ec7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough
ChangesComplete-rollout consent
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Automation
participant planCommand
participant confirmCompleteRollout
participant messages
Automation->>planCommand: Run --complete-rollout
planCommand->>confirmCompleteRollout: Pass --yes and interactivity state
alt Non-interactive without --yes
confirmCompleteRollout->>messages: Emit needs --yes error
confirmCompleteRollout-->>Automation: Exit 1 without drafting
else With --yes
confirmCompleteRollout->>messages: Emit confirmation
confirmCompleteRollout-->>Automation: Continue with exit 0
end
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds an explicit non-interactive consent path for stash plan --complete-rollout, so automation can safely request a complete-rollout plan and reliably detect when no plan was drafted.
Changes:
- Add
--yesto confirm--complete-rolloutgate-skip without prompting, and refuse non-interactive runs without--yeswith a non-zero exit. - Centralize new user-facing leaders in
messages.plan.*and assert them in new non-PTY E2E coverage. - Update CLI registry help/examples and the shipped
skills/stash-clidocumentation to reflect the new scoped--yes.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| skills/stash-cli/SKILL.md | Documents the scoped plan --complete-rollout --yes escape hatch and the non-interactive refusal behavior. |
| packages/cli/tests/e2e/plan-complete-rollout.e2e.test.ts | Adds piped-stdio E2E tests for refusal (exit 1) without --yes and success path with --yes. |
| packages/cli/src/messages.ts | Adds stable message leaders for complete-rollout refusal/confirmation output. |
| packages/cli/src/commands/plan/index.ts | Implements --yes consent + non-interactive refusal for --complete-rollout; computes interactivity once for downstream gating. |
| packages/cli/src/cli/registry.ts | Registers plan --yes flag and updates examples/descriptions accordingly. |
| .changeset/plan-complete-rollout-yes.md | Records a minor CLI change for the new flag + exit-code behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Review follow-up on the M2 complete-rollout change (finding #1 + #3; also Copilot's sole inline comment): - plan's interactivity gate re-inlined `process.stdin.isTTY && CI !== 'true'`, which only matches exact lowercase `true` — it misses `CI=1`/`CI=TRUE` and so diverged from the shared `isInteractive()` (config/tty.ts, whose `isCiEnv()` matches `/^(1|true)$/i`) and from the case-insensitive contract the stash-cli skill documents. Swap the inline for `isInteractiveTty()` (compute-once preserved) so a CI=1 runner with a TTY takes the non-interactive path. - Drop the unused `cli` parameter from `confirmCompleteRollout`'s opts (the refusal hardcodes the `--yes` hint; `cli` was threaded but never read). No behaviour change on the documented CI=true / TTY paths. plan-complete-rollout e2e 2/2 and plan unit 22/22 green; build + biome clean. Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
|
Human and AI review (Copilot and Claude/Opus 4.8). |
Problem (rc.2 M2)
stash plan --complete-rolloutskips the production-deploy gate, so it asks for confirmation. But that was an interactivep.confirm(default-no) with no bypass — so a non-interactive run (CI, agent, piped stdin) auto-cancelled and exited 0 viaCancelledError. Automation asked for a complete-rollout plan, got exit 0, and had to assume a plan existed when none was drafted. Two gaps: not automatable, and a dishonest exit code.Fix
--yesconfirms the gate-skip without a prompt (the automation path). It still logs the two safety warnings first, so the record shows what was skipped.--complete-rolloutwithout--yesnow refuses with a non-zero exit (CliExit(1)) and points at--yes, instead of silently cancelling with 0. An interactive decline stays exit 0 — a deliberate "no" isn't a failure.isInteractiveis computed once up front (the confirm needs it, and so does the downstream agent-target gate).Scope notes
--yesonly governs the complete-rollout gate; it has no effect otherwise (documented). The no---targetnon-interactive path keeps its existing "here's how to proceed" hint + exit 0 — that's the established pattern acrossplan/init, not what M2 is about.stash-cliskill updated (incl. correcting the "no global --yes" line to note the scoped one).messages.plan.*leaders added for e2e stability.Verification
--yes;--yesconfirms and proceeds past the refusal), in a temp dir with a minimal.cipherstash/context.json— no DB/network.stash manifest --jsonresolves the new--yesflag onplan.code:checkclean. Changeset:stashminor.https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
Summary by CodeRabbit
New Features
--yesoption for non-interactiveplan --complete-rolloutto confirm skipping the production-deploy gate.Bug Fixes
plan --complete-rolloutruns without--yesnow exit non-zero and refuse to proceed, rather than silently succeeding without drafting a plan.Documentation
Tests
--complete-rollout--yesconsent flow.