Skip to content

fix(cli): honour the non-interactive contract in stash init (#600)#605

Merged
coderdan merged 1 commit into
mainfrom
fix/init-non-interactive-prompts
Jul 9, 2026
Merged

fix(cli): honour the non-interactive contract in stash init (#600)#605
coderdan merged 1 commit into
mainfrom
fix/init-non-interactive-prompts

Conversation

@coderdan

@coderdan coderdan commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Closes #600.

Problem

stash init advertises (in manifest --json and the command help) that "every prompt has a non-interactive escape hatch, so init never blocks waiting on a TTY (CI, agents, pipes)." In practice, three init steps called clack prompts with no interactivity gate. In a non-TTY context each prompt returns a cancel symbol, which the step treats as CancelledError, so init aborts at the first prompt without acting.

Two consequences:

  1. stash init is unusable from agents / CI / pipes (it stops at the first prompt and exits 0 without doing anything).
  2. Because it aborts before the EQL step, it never scaffolds stash.config.ts, so downstream read commands dead-end on Could not find stash.config.ts.

Fix

Gate the three blocking prompts on the shared isInteractive() helper (config/tty.ts), matching the existing resolve-proxy-choice pattern, and take a safe default in non-interactive contexts:

Step Non-interactive default
install-deps Install the packages (init is a setup command; installing its own deps is expected)
install-eql Install EQL — which runs the config scaffold (scaffoldConfig: 'ensure'), closing the missing-stash.config.ts gap
build-schema Keep an existing encryption client (never clobber the user's file unasked)

Interactive runs are unchanged — the prompts still show in a TTY. The other init steps (authenticate via --region/STASH_REGION, resolve-database, resolve-proxy-choice, gather-context, and the final "continue to plan?") were already non-interactive-safe.

Tests

Added step-level tests for install-deps, install-eql, and build-schema covering the interactive / non-interactive / already-satisfied paths (9 tests). Full src/commands/init suite passes (182 tests); biome check clean.

Found via a CipherStash integration eval on cipherstash/supabase-nextjs-starter (hono-pg surface), which is also where #599 and #601 came from.

`stash init` advertises that "every prompt has a non-interactive escape hatch,
so init never blocks waiting on a TTY (CI, agents, pipes)", but three steps
called clack prompts with no interactivity gate. In a non-TTY context each
prompt returned a cancel symbol, which the step treated as `CancelledError`, so
`init` aborted at the first prompt without acting — and never reached the EQL
step that scaffolds `stash.config.ts` (so downstream `stash status` / `eql
status` dead-ended on "Could not find stash.config.ts").

Gate the three prompts on `isInteractive()` (the shared `config/tty.ts` helper),
matching the existing `resolve-proxy-choice` pattern, and take a safe default in
non-interactive contexts:

- install-deps: install the packages by default (init is a setup command).
- install-eql: install EQL by default — which also runs the config scaffold
  (`scaffoldConfig: 'ensure'`), closing the missing-`stash.config.ts` gap.
- build-schema: keep an existing encryption client (never clobber it unasked).

Interactive runs are unchanged. Adds step-level tests covering the
interactive / non-interactive / already-satisfied paths for all three.
@coderdan coderdan requested a review from a team as a code owner July 9, 2026 09:33
@coderdan coderdan self-assigned this Jul 9, 2026
@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a994ed0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@coderdan, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 30050cb8-a014-4278-a43c-87a1da6d02c6

📥 Commits

Reviewing files that changed from the base of the PR and between a91c0db and a994ed0.

📒 Files selected for processing (6)
  • packages/cli/src/commands/init/steps/__tests__/build-schema.test.ts
  • packages/cli/src/commands/init/steps/__tests__/install-deps.test.ts
  • packages/cli/src/commands/init/steps/__tests__/install-eql.test.ts
  • packages/cli/src/commands/init/steps/build-schema.ts
  • packages/cli/src/commands/init/steps/install-deps.ts
  • packages/cli/src/commands/init/steps/install-eql.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/init-non-interactive-prompts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

This PR fixes stash init so it properly honors the CLI’s documented non-interactive contract (CI/agents/pipes), by preventing clack prompts from aborting the init flow and ensuring the EQL step can still scaffold stash.config.ts.

Changes:

  • Gate three previously-unconditional prompts (install-deps, install-eql, build-schema) on isInteractive() and apply safe non-interactive defaults.
  • Add step-level Vitest coverage for interactive vs non-interactive behavior (and already-satisfied paths) for the affected steps.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/cli/src/commands/init/steps/install-eql.ts Avoids blocking prompt in non-interactive contexts; defaults to installing EQL so config scaffolding can proceed.
packages/cli/src/commands/init/steps/install-deps.ts Avoids blocking prompt in non-interactive contexts; defaults to installing required deps.
packages/cli/src/commands/init/steps/build-schema.ts Avoids blocking prompt in non-interactive contexts; defaults to keeping an existing client file to prevent clobbering.
packages/cli/src/commands/init/steps/tests/install-eql.test.ts Adds tests covering interactive prompt vs non-interactive default install + config scaffold behavior.
packages/cli/src/commands/init/steps/tests/install-deps.test.ts Adds tests covering interactive prompt vs non-interactive default install and “already installed” short-circuit.
packages/cli/src/commands/init/steps/tests/build-schema.test.ts Adds tests covering interactive overwrite/keep choice vs non-interactive “keep existing” behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderdan

coderdan commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Minor change. Self and AI assisted review.

@coderdan coderdan merged commit 8ac66a8 into main Jul 9, 2026
10 checks passed
@coderdan coderdan deleted the fix/init-non-interactive-prompts branch July 9, 2026 10:06
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.

stash init ignores its non-interactive contract (blocks on prompts in non-TTY); no --yes; doesn't scaffold stash.config.ts

2 participants