Files
OpenSpec/.changeset
Clay GoodandClaude Opus 4.8 9ae75c86ef fix(archive): don't write ANSI escape codes to a redirected (non-TTY) stdout (#1603)
* fix(archive): stop non-TTY confirm prompts from writing ANSI escapes to stdout

`openspec archive` asks up to three yes/no questions through @inquirer's
`confirm`, which renders by writing ANSI cursor-movement escape sequences —
and emits them even when stdout is not a TTY. When archive runs with its
output captured to a file or pipe (an agent's background task, CI), those
escapes are noise, and in some non-TTY hosts the render loop never settles
and repeats `ESC[NNG` moves until the disk fills (reporter hit 19.8 GB).

Add `confirmPrompt` in interactive.ts: a real terminal (stdin AND stdout
TTY) still gets @inquirer's rich prompt; every other case reads one plain
line via node:readline with `terminal:false`, emitting no escapes. Parsing
mirrors @inquirer/confirm exactly (prefix match on y/yes and n/no, else the
default), and an unreadable stdin rejects with an ExitPromptError-shaped
error so the existing #1479 "rerun with --yes" guidance is unchanged.
archive's confirmOrBlock now calls confirmPrompt.

Closes #1526

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

* test(interactive): cover Windows CRLF and drained-stdin paths; doc note

Adds two regression tests surfaced by adversarial review of the #1526 fix:
- Windows CRLF piped input (`y\r\n`) parses as a clean yes with no ANSI —
  the reporter's platform, previously untested (all inputs used `\n`).
- A second prompt after stdin was already drained blocks with an
  ExitPromptError instead of hanging, exercising the readableEnded guard.

Also documents in troubleshooting.md that a redirected/agent archive run
that pipes an answer no longer writes terminal escape codes into the capture.

Refs #1526

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

* fix(interactive): align non-interactive classification and handle readline errors

Addresses two review findings on the #1526 confirm-prompt fix:

- confirmPrompt drops to the plain reader whenever either stream is not a
  TTY, but isNonInteractivePromptError only checked stdin. A stdin-TTY /
  stdout-redirected run that hit EOF leaked the raw ExitPromptError instead
  of the #1479 "rerun with --yes" guidance. Classification now also counts a
  redirected stdout, matching how the prompt mode is chosen. (isInteractive,
  used broadly elsewhere, is left untouched.)

- readYesNo never listened for the readline/input 'error' event, so a stdin
  error would hang the promise (and go unhandled). It now settles with the
  underlying fault, guarded so the promise resolves or rejects exactly once.

Tests: TTY-stdin/redirected-stdout EOF is classified non-interactive; an
erroring input stream rejects instead of hanging; the archive usable-terminal
test now models a full terminal (both streams TTY).

Refs #1526

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

* fix(archive): gate the change picker on a TTY and tidy the reader

Follow-ups from a second review round:

- selectChange (the no-argument change picker) called @inquirer's `select`
  unconditionally. `select` writes ANSI escapes to stdout even when redirected
  — the same #1526 mechanism the confirm prompts were fixed for — so
  `openspec archive > log.txt` with no change name still spewed cursor moves
  into the capture before blocking. Refuse before rendering when either stream
  is not a TTY, with the same "pass a change name / --yes" guidance the caught
  ExitPromptError already gives. A new test asserts the picker is never
  reached in a non-terminal run.

- readYesNo now removes its input-stream 'error' listener on every settle path
  (it lives on the long-lived process.stdin) and closes the readline interface
  on error too, so nothing accumulates across archive's sequential prompts.

- troubleshooting.md now notes the picker also stays clean.

Refs #1526

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

* chore(changeset): add patch changeset for the archive non-TTY fix (#1526)

User-facing patch note for the archive ANSI/disk-fill fix. Also drops an
unnecessary optional-chain on the non-nullable readline handle in readYesNo
(the listener is only attached after the interface exists).

Refs #1526
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-11 20:53:41 +00:00
..

Changesets

This directory is managed by Changesets.

Quick Start

pnpm changeset

Follow the prompts to select version bump type and describe your changes.

Workflow

  1. Choose the release path: Maintainers decide whether a PR follows the normal release cadence or gets dedicated release tracking.
  2. Add dedicated release tracking: When a maintainer asks for a changeset, run pnpm changeset locally before or after your PR.
  3. Version PR: CI opens/updates a "Version Packages" PR when changesets merge to main.
  4. Release: Merging the Version PR triggers npm publish and GitHub Release.

Note: The default path is the normal release cadence. Add a changeset when a maintainer or release owner wants dedicated release notes and version tracking for the PR. Versioning (changeset version) and publishing happen automatically in CI.

Template

Use this structure for your changeset content:

---
"@fission-ai/openspec": patch
---

### New Features

- **Feature name** — What users can now do

### Bug Fixes

- Fixed issue where X happened when Y

### Breaking Changes

- `oldMethod()` has been removed, use `newMethod()` instead

### Deprecations

- `legacyOption` is deprecated and will be removed in v2.0

### Other

- Internal refactoring of X for better performance

Include only the sections relevant to your change.

Version Bump Guide

Type When to use Example
patch Release-tracked bug fixes, small improvements Fixed crash when config missing
minor New features, non-breaking additions Added --verbose flag
major Breaking changes, removed features Renamed init to setup

When to Create a Changeset

Use dedicated release tracking for:

  • New features or commands selected for release
  • Notable bug fixes or hotfixes requested by a maintainer/release owner
  • Breaking changes or deprecations
  • Performance improvements users would notice and that are planned for release

Use the normal release cadence for:

  • Routine bug fixes that fit the normal release cadence
  • Documentation-only changes
  • Test additions/fixes
  • Internal refactoring that preserves user behavior
  • CI/tooling changes

Writing Good Descriptions

Do: Write for users, not developers

- **Shell completions** — Tab completion now available for Bash, Fish, and PowerShell

Don't: Write implementation details

- Added ShellCompletionGenerator class with Bash/Fish/PowerShell subclasses

Do: Explain the impact

- Fixed config loading to respect `XDG_CONFIG_HOME` on Linux

Don't: Just reference the fix

- Fixed #123