Skip to content

Non-TTY guard from init (#41) should cover the other prompting commands #49

Closed

Description

@ManningWorks

Adjacent finding from the PR #48 review (non-blocking there, pre-existing): init now has an explicit non-TTY guard for its overwrite prompt (explicit refusal + --yes escape hatch, landed via #48 / issue #41), but the other prompting commands (add, edit-project, remove-project, remove-learning) still rely on the incidental event-loop drain when stdin is non-interactive — the same bug class #41 fixed for init.

Provenance: #48 (comment) ("Adjacent (not a finding on this PR)").

Ask:

  1. Audit each prompting command for what happens with non-TTY stdin today (piped / /dev/null): does a prompt hang, drain out mid-promise, or exit cleanly? Document per-command.
  2. Where a destructive or overwrite action can be reached non-interactively, apply the same explicit guard pattern init uses (see packages/core/src/commands/init.ts:66-75): refuse with a clear message + flag escape hatch, rather than depending on the drain.
  3. Where the command is purely informational (no destructive path), record that no guard is needed — the audit result is the deliverable, not blanket refactors.

Constraints per AGENTS.md: build/lint/test only, no dev servers; gate green before any PR.

Activity

  1. ManningWorks commented on Oct 14, 2026

    @ManningWorks
    OwnerAuthor

    This was generated by AI during triage.

    Triage result

    Category: bug · State: ready-for-agent

    Verified against the live CLI (built from current main, stdin from /dev/null, 2026-10-14):

    Command Non-TTY behavior observed Needs work?
    init (baseline) explicit refusal + --yes escape hatch no — guard landed via #48
    add → theme re-add prompt renders, then silent exit 0 mid-promise — no cancel message, no overwrite, no error yes
    add → component re-add raw failure: "✖ Failed to add component — User force closed the prompt with 0 null", exit 1; no overwrite yes — safe but misreported
    edit project (no flags) "✖ Cancelled.", exit 0 no
    remove project (no --force) "✖ Cancelled.", exit 0 (also verified with an open-pipe stdin) no
    remove learning (no --index) "✖ Cancelled.", exit 0 no

    Correction to the original premise: edit-project, remove-project and remove-learning already handle prompt cancellation explicitly — they do not rely on an incidental event-loop drain, and no destructive action is reachable non-interactively through them (each also has a non-interactive escape hatch: flags, --force, --index). The surviving instance of the #41 bug class is in add's theme path: an empty catch {} around the overwrite confirm swallows ExitPromptError and the process drains mid-promise — the exact pattern #41 fixed for init. The component path never overwrites either, but a cancellation escapes to the generic error handler and is reported as "Failed to add component".

    Per the issue's own ask #3, the audit above is the deliverable for the already-safe commands — no guards there.

    Agent Brief

    Category: bug
    Summary: Make add's overwrite prompts behave explicitly under non-TTY stdin, matching the guard pattern init uses.

    Current behavior:

    • Theme path: the overwrite confirm() sits inside an empty catch {}; under non-TTY stdin the prompt's ExitPromptError is swallowed and the process exits 0 silently mid-promise (no message, no cancel notice, nothing written).
    • Component path: ExitPromptError from the overwrite confirm() reaches the generic error handler → "✖ Failed to add component" + internal prompt error text, exit 1.

    Desired behavior:

    • Theme path: when an existing theme file would be overwritten and stdin is non-interactive without --force, refuse up front with a clear message (run in a TTY to confirm, or pass --force) — the same explicit-guard shape init uses for config overwrite. Prompt cancellation (ExitPromptError) prints "✖ Add cancelled." and returns cleanly; non-prompt errors surface instead of being swallowed.
    • Component path: ExitPromptError is caught explicitly → "✖ Add cancelled." + clean return, mirroring how remove-project handles it. The generic "Failed to add component" handler never sees a prompt cancellation.

    Acceptance criteria:

    • add <theme> with an existing theme file, stdin /dev/null, no --force → explicit refusal message (not silent), file untouched
    • add <theme> with an existing file + --force, stdin /dev/null → overwrites without prompting
    • add <component> re-add under /dev/null stdin → "✖ Add cancelled." (not "Failed to add component"), no partial writes
    • New tests cover all three paths; full gate green (pnpm install && pnpm build && pnpm typecheck && pnpm lint && pnpm test)

    Out of scope:

    • Guards for edit-project / remove-project / remove-learning (verified already-safe; audit recorded above)
    • Changes to prompt text/UX of other commands
    • The .d.ts-only component source quirk in getSourceFiles (dist ships only .d.ts/.js for components — separate concern worth its own issue)

    Delegation: filed on the Hermes kanban board (coder card + reviewer card, serially gated).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions