Skip to content

Conversation

@TabishB
Copy link
Contributor

@TabishB TabishB commented Dec 23, 2025

Summary

  • Fixes validate command hanging when run via pre-commit hooks
  • The spinner was starting regardless of --no-interactive flag
  • Correctly handles Commander.js --no-* flag syntax (sets interactive=false, not noInteractive=true)
  • Adds CI environment variable check for industry standard compliance

Root Cause

When using --no-interactive, Commander.js sets options.interactive = false, NOT options.noInteractive = true. The runBulkValidation method was:

  1. Not receiving the interactive state at all
  2. Starting the ora spinner unconditionally (only checking --json flag)

Test plan

  • Run openspec validate --specs --strict --no-interactive directly - completes without hanging
  • Run pre-commit run openspec-validate -a - completes without hanging
  • Build passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Explicit support for interactive and non-interactive modes for validation commands, including handling of the CLI --no-interactive flag.
    • Automatic CI environment detection disables interactive behavior for smoother automated runs and suppresses interactive UI (spinners/prompts) when non-interactive.
  • Tests

    • Added tests verifying non-interactive flag behavior and interactive-utility logic across environments.

✏️ Tip: You can customize this high-level summary in your review settings.

The validate command's spinner was starting regardless of the
--no-interactive flag, causing hangs in pre-commit hooks.

Changes:
- Pass noInteractive option to runBulkValidation
- Handle Commander.js --no-* flag syntax (sets interactive=false)
- Only start ora spinner when in interactive mode
- Add CI environment variable check to isInteractive() for industry
  standard compliance
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Walkthrough

Adds CLI handling for --no-interactive via a new resolveNoInteractive export; threads a noInteractive flag into ValidateCommand.runBulkValidation and spinner logic; isInteractive now short-circuits to non-interactive when CI env is detected. Tests added for CLI flag and interactive utilities.

Changes

Cohort / File(s) Summary
Validate command
src/commands/validate.ts
Added interactive?: boolean to ExecuteOptions; pass noInteractive: resolveNoInteractive(options) into runBulkValidation; extended runBulkValidation opts to accept noInteractive?: boolean; spinner rendering now suppressed when json or noInteractive is true.
Interactive utilities
src/utils/interactive.ts
Exported InteractiveOptions type and resolveNoInteractive helper; isInteractive() now returns false early when a CI environment variable is present; preserved existing OPEN_SPEC_INTERACTIVE and stdin TTY checks.
Tests — validate CLI
test/commands/validate.test.ts
Added test verifying --no-interactive CLI flag is parsed and prevents interactive prompt when running validate with --specs.
Tests — interactive utils
test/utils/interactive.test.ts
New suite covering resolveNoInteractive and isInteractive across combinations of flags, environment vars (OPEN_SPEC_INTERACTIVE, CI), and mocked stdin.isTTY.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (commander)
    participant Validate as ValidateCommand
    participant Utils as interactive utils
    participant Spinner as Spinner/Renderer

    CLI->>Validate: parse options (includes --no-interactive / interactive)
    Validate->>Utils: resolveNoInteractive(options, env)
    note right of Utils `#f8f3d7`: resolveNoInteractive\nchecks explicit flags,\nOPEN_SPEC_INTERACTIVE,\nCI, stdin.isTTY
    Utils-->>Validate: boolean noInteractive
    Validate->>Validate: decide bulk run path
    alt noInteractive or json
        Validate->>Spinner: suppress spinner rendering
        Spinner-->>Validate: no spinner shown
    else interactive allowed
        Validate->>Spinner: show spinner during bulk validation
        Spinner-->>Validate: spinner updates
    end
    Validate->>Validate: runBulkValidation(..., noInteractive)
    Validate-->>CLI: exit code / output
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main fix: making the validate command respect the --no-interactive flag to prevent hanging in non-interactive contexts.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/pre-commit-hook-hanging

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 and usage tips.

- Export resolveNoInteractive() helper for reuse
- Add InteractiveOptions type export for testing
- Refactor validate.ts to use resolveNoInteractive()
- Add 17 unit tests for isInteractive() and resolveNoInteractive()
- Add CLI integration test for --no-interactive flag

This prevents future regressions where Commander.js --no-* flag
parsing is not properly handled.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
test/utils/interactive.test.ts (1)

40-80: Comprehensive test coverage for resolveNoInteractive.

The tests cover all input shapes (object with noInteractive/interactive, boolean, undefined) and correctly verify the OR logic.

Optional: Clarify the "prioritize" comment

The comment at line 74 says "should prioritize noInteractive over interactive when both set," but the function uses OR logic (noInteractive === true || interactive === false), so both test cases return true. Consider rephrasing:

-    it('should prioritize noInteractive over interactive when both set', () => {
-      // noInteractive: true should win
+    it('should return true when either condition for non-interactive is met', () => {
+      // Either noInteractive: true OR interactive: false triggers non-interactive mode
       expect(resolveNoInteractive({ noInteractive: true, interactive: true })).toBe(true);
-      // If noInteractive is false, check interactive
       expect(resolveNoInteractive({ noInteractive: false, interactive: false })).toBe(true);
     });

The current assertions are correct; this just clarifies the intent.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2c2799 and 6b3f64e.

📒 Files selected for processing (4)
  • src/commands/validate.ts
  • src/utils/interactive.ts
  • test/commands/validate.test.ts
  • test/utils/interactive.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/interactive.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Fission-AI/OpenSpec PR: 0
File: openspec/AGENTS.md:0-0
Timestamp: 2025-11-25T01:08:19.004Z
Learning: Run `openspec validate [change-id] --strict` before requesting approval
📚 Learning: 2025-11-25T01:08:19.004Z
Learnt from: CR
Repo: Fission-AI/OpenSpec PR: 0
File: openspec/AGENTS.md:0-0
Timestamp: 2025-11-25T01:08:19.004Z
Learning: Run `openspec validate [change-id] --strict` before requesting approval

Applied to files:

  • src/commands/validate.ts
🧬 Code graph analysis (3)
test/commands/validate.test.ts (1)
test/helpers/run-cli.ts (1)
  • runCLI (75-137)
test/utils/interactive.test.ts (1)
src/utils/interactive.ts (2)
  • resolveNoInteractive (17-20)
  • isInteractive (22-28)
src/commands/validate.ts (1)
src/utils/interactive.ts (1)
  • resolveNoInteractive (17-20)
🔇 Additional comments (7)
test/commands/validate.test.ts (1)

134-146: LGTM! Excellent test coverage for the CLI flag.

The test correctly verifies that --no-interactive prevents the command from hanging and prompting. The comment explaining Commander.js behavior is helpful, and explicitly unsetting OPEN_SPEC_INTERACTIVE ensures the test validates the flag itself rather than environment-derived behavior.

test/utils/interactive.test.ts (2)

1-38: LGTM! Clean environment isolation.

The setup and teardown properly preserve and restore environment variables and stdin.isTTY, ensuring test isolation.


82-124: LGTM! Thorough coverage of isInteractive behavior.

The tests validate all the short-circuit conditions (explicit flags, OPEN_SPEC_INTERACTIVE, CI env var, stdin.isTTY) and confirm the fallback to TTY detection.

src/commands/validate.ts (4)

4-4: LGTM! Correct import for the fix.


18-18: LGTM! Matches Commander.js --no- convention.*

The comment correctly documents that Commander.js sets interactive = false (not noInteractive = true) for the --no-interactive flag.


39-39: LGTM! Correctly threads noInteractive into bulk validation.

Using resolveNoInteractive(options) properly handles both Commander.js style (interactive: false) and direct noInteractive flags.


184-185: LGTM! Spinner is correctly gated for non-interactive mode.

The spinner only starts when both conditions are met:

  • Not in JSON output mode
  • Not in non-interactive mode

This prevents the hang reported in pre-commit hooks. All subsequent spinner usages (lines 217, 250, 272) safely handle undefined via optional chaining or conditionals.

@TabishB TabishB merged commit 9ac6330 into main Dec 23, 2025
7 checks passed
appboypov added a commit to appboypov/pew-pew-plx that referenced this pull request Dec 24, 2025
* feat(cli): add openspec config command for global configuration management (Fission-AI#382)

* feat(cli): add openspec config command for global configuration management

Implements the `openspec config` command with subcommands:
- `path`: Show config file location
- `list [--json]`: Show all current settings
- `get <key>`: Get a specific value (raw output for scripting)
- `set <key> <value> [--string]`: Set a value with auto type coercion
- `unset <key>`: Remove a key (revert to default)
- `reset --all [-y]`: Reset configuration to defaults
- `edit`: Open config in $EDITOR/$VISUAL

Key features:
- Dot notation for nested key access (e.g., featureFlags.someFlag)
- Auto type coercion (true/false → boolean, numbers → number)
- --string flag to force string storage
- Zod schema validation with unknown field passthrough
- Reserved --scope flag for future project-local config
- Windows-compatible editor spawning with proper path quoting
- Shell completion registry integration

* test(config): add additional unit tests for validation and coercion

- Add tests for unknown fields with various types
- Add test to verify error message path for featureFlags
- Add test for number values rejection in featureFlags
- Add config set simulation tests to verify full coerce → set → validate flow

* fix(config): avoid shell parsing in config edit to handle paths with spaces

Use spawn with shell: false and pass configPath as an argument instead
of building a shell command string. This correctly handles spaces in
both the EDITOR path and config file path on all platforms.

* chore(openspec): archive add-config-command and create cli-config spec

Move completed change to archive and apply spec deltas to create
the cli-config specification documenting the config command interface.

* Validate config keys on set

* Add changeset for config command and shell completions (Fission-AI#388)

* chore(release): version packages (Fission-AI#389)

* Version Packages

* chore: trigger CI

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <[email protected]>

* feat(ci): migrate to npm OIDC trusted publishing (Fission-AI#390)

Replace classic npm token authentication with OIDC trusted publishing:

- Add `id-token: write` permission for OIDC token generation
- Upgrade to Node 24 (includes npm 11.5.1+ required for OIDC)
- Remove NPM_TOKEN/NODE_AUTH_TOKEN env vars (OIDC replaces them)

This eliminates the need for rotating npm access tokens and provides
cryptographically verified publisher identity with automatic provenance
attestation.

Requires configuring trusted publisher on npmjs.com:
- Organization: Fission-AI
- Repository: OpenSpec
- Workflow: release-prepare.yml

* fix(cli): use dynamic import for @inquirer/prompts in config command (Fission-AI#392)

* fix(cli): use dynamic import for @inquirer/prompts in config command

The config command (added in Fission-AI#382) reintroduced the pre-commit hook hang
issue that was fixed in Fission-AI#380. The static import of @inquirer/prompts at
module load time causes stdin event listeners to be registered even when
running non-interactive commands, preventing clean process exit when
stdin is piped (as pre-commit does).

Convert the static import to a dynamic import that only loads inquirer
when the `config reset` command is actually used interactively.

Fixes Fission-AI#367

* chore: add ESLint with no-restricted-imports rule for @InQuirer

Add ESLint configuration that prevents static imports of @inquirer/*
modules. This prevents future regressions of the pre-commit hook hang
issue fixed in this PR.

The rule shows a helpful error message pointing to issue Fission-AI#367 for context.
init.ts is exempted since it's already dynamically imported from the CLI.

* ci: add ESLint step to lint job

Run `pnpm lint` in CI to enforce the no-restricted-imports rule
that prevents static @InQuirer imports.

* Add changeset for config command dynamic import fix (Fission-AI#393)

* chore(release): version packages (Fission-AI#394)

* Version Packages

* chore: trigger CI

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <[email protected]>

* fix(cli): respect --no-interactive flag in validate command (Fission-AI#395)

* fix(cli): respect --no-interactive flag in validate command

The validate command's spinner was starting regardless of the
--no-interactive flag, causing hangs in pre-commit hooks.

Changes:
- Pass noInteractive option to runBulkValidation
- Handle Commander.js --no-* flag syntax (sets interactive=false)
- Only start ora spinner when in interactive mode
- Add CI environment variable check to isInteractive() for industry
  standard compliance

* test: add unit tests for interactive utilities and CLI flag

- Export resolveNoInteractive() helper for reuse
- Add InteractiveOptions type export for testing
- Refactor validate.ts to use resolveNoInteractive()
- Add 17 unit tests for isInteractive() and resolveNoInteractive()
- Add CLI integration test for --no-interactive flag

This prevents future regressions where Commander.js --no-* flag
parsing is not properly handled.

* Add changeset for --no-interactive flag fix (Fission-AI#396)

* chore(release): version packages (Fission-AI#397)

* Version Packages

* chore: trigger CI

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <[email protected]>

* docs: add artifact POC analysis document (Fission-AI#398)

Add internal documentation for the artifact-based approach to OpenSpec
core. This document outlines design decisions, terminology, and the
philosophy behind treating dependencies as enablers rather than gates.

* fix(archive): allow REMOVED requirements when creating new spec files (Fission-AI#403) (Fission-AI#404)

When creating a new spec file, REMOVED requirements are now ignored
with a warning instead of causing archive to fail. This enables
refactoring scenarios where old fields are removed while documenting
a capability for the first time.

Fixes Fission-AI#403

---------

Co-authored-by: Tabish Bidiwale <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tabish Bidiwale <[email protected]>
Co-authored-by: Eunsong-Park <[email protected]>
appboypov pushed a commit to appboypov/pew-pew-plx that referenced this pull request Dec 24, 2025
…AI#395)

* fix(cli): respect --no-interactive flag in validate command

The validate command's spinner was starting regardless of the
--no-interactive flag, causing hangs in pre-commit hooks.

Changes:
- Pass noInteractive option to runBulkValidation
- Handle Commander.js --no-* flag syntax (sets interactive=false)
- Only start ora spinner when in interactive mode
- Add CI environment variable check to isInteractive() for industry
  standard compliance

* test: add unit tests for interactive utilities and CLI flag

- Export resolveNoInteractive() helper for reuse
- Add InteractiveOptions type export for testing
- Refactor validate.ts to use resolveNoInteractive()
- Add 17 unit tests for isInteractive() and resolveNoInteractive()
- Add CLI integration test for --no-interactive flag

This prevents future regressions where Commander.js --no-* flag
parsing is not properly handled.
appboypov pushed a commit to appboypov/pew-pew-plx that referenced this pull request Dec 24, 2025
…AI#395)

* fix(cli): respect --no-interactive flag in validate command

The validate command's spinner was starting regardless of the
--no-interactive flag, causing hangs in pre-commit hooks.

Changes:
- Pass noInteractive option to runBulkValidation
- Handle Commander.js --no-* flag syntax (sets interactive=false)
- Only start ora spinner when in interactive mode
- Add CI environment variable check to isInteractive() for industry
  standard compliance

* test: add unit tests for interactive utilities and CLI flag

- Export resolveNoInteractive() helper for reuse
- Add InteractiveOptions type export for testing
- Refactor validate.ts to use resolveNoInteractive()
- Add 17 unit tests for isInteractive() and resolveNoInteractive()
- Add CLI integration test for --no-interactive flag

This prevents future regressions where Commander.js --no-* flag
parsing is not properly handled.
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.

2 participants