Skip to content

feat(cli): add /exit slash command - #1201

Merged
flora131 merged 4 commits into
mainfrom
feature/issue-1200-exit-command
Jun 3, 2026
Merged

feat(cli): add /exit slash command#1201
flora131 merged 4 commits into
mainfrom
feature/issue-1200-exit-command

Conversation

@flora131

@flora131 flora131 commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds /exit as a built-in interactive slash command that routes to the same graceful shutdown path as /quit. Also fixes a pre-existing inconsistency where /exit in workflow attached-stage chat would close the overlay — it now falls through as normal text, matching the behavior of /exit <args> in the main TUI.

Changes

Core

  • slash-commands.ts: Registers exit in BUILTIN_SLASH_COMMANDS so it appears in autocomplete and is reserved as a built-in name, hiding any extension command named exit (consistent with other built-ins).
  • interactive-mode.ts: Extends the submit-handler condition from text === "/quit" to text === "/quit" || text === "/exit", routing exact /exit to InteractiveMode.shutdown(). Argument-bearing forms like /exit now fall through to normal input handling.
  • stage-chat-view.ts: Removes /exit from the workflow stage-chat switch — it was previously closing the stage overlay. Now /exit (exact or with args) falls through as normal stage-chat text; only /quit closes the overlay.

Documentation

  • Added /exit to the slash command tables in README.md and docs/usage.md.
  • Added changelog entry under [Unreleased] → Added.

Tests

  • test/unit/slash-commands.test.ts — asserts /exit is in BUILTIN_SLASH_COMMANDS with the correct description.
  • packages/coding-agent/test/interactive-mode-status.test.ts — verifies exact /exit and /quit both call graceful shutdown; /exit now does not; autocomplete for /ex includes built-in exit and deduplicates conflicting extension commands.
  • test/unit/stage-chat-view.test.ts — confirms /exit, /exit now, and /exit 1 do not close the stage overlay and are instead submitted as prompts; /quit still closes the overlay.
  • test/unit/workflow-attach-pane.test.ts — confirms /exit and /exit now from an attached stage reach the stage handle as normal prompts, not as shutdown signals.

Behavior Summary

Context Input Result
Main TUI /exit Graceful shutdown (same as /quit)
Main TUI /exit <args> Normal prompt text
Workflow stage chat /exit Normal prompt text (falls through)
Workflow stage chat /exit <args> Normal prompt text (falls through)
Workflow stage chat /quit Closes the stage overlay (unchanged)

Closes #1200

@flora131

flora131 commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

Implementation Notes

Task: implement the issue #1200

Running Notes

  • Record implementation decisions, deviations from the spec, tradeoffs, blockers, validation notes, and anything else the user should know.

Iteration 1 Implementation Notes (2026-06-02)

Preflight

  • Repository evidence identified a Bun/TypeScript monorepo. Preflight subagent ran bun install; dependencies and prek hooks were initialized successfully. No missing generated artifacts or toolchain blockers were found.

Decisions and tradeoffs

  • Added /exit as a built-in slash command next to /quit, which means extension commands named exit are reserved/hidden in main interactive autocomplete consistently with other built-ins.
  • Main interactive /exit is exact-match only and reuses the same graceful InteractiveMode.shutdown() path as /quit; /exit now remains normal input.
  • Workflow attached-stage /quit remains overlay-close for compatibility, while /exit now requests app shutdown when ExtensionContext.shutdown() is available.
  • Workflow /exit falls back to overlay close if the host lacks a shutdown hook, matching the spec’s degraded-host fallback.
  • No build steps or generated dist outputs were introduced for packages/workflows.

Validation outcomes

  • Implementer reported passing focused tests and typecheck.
  • Independent validator confirmed:
    • bun test test/unit/slash-commands.test.ts passed.
    • bun test test/unit/stage-chat-view.test.ts test/unit/workflow-attach-pane.test.ts passed.
    • cd packages/coding-agent && bun run test interactive-mode-status.test.ts passed.
    • bun run typecheck passed.
  • The spec-listed command form bun --cwd packages/coding-agent run test -- interactive-mode-status.test.ts failed due Bun 1.3.14 argument ordering, but the equivalent package-directory command passed.

Blockers

  • No blocking issues remain for iteration 1.

Iteration 2 Implementation Notes (2026-06-02)

Preflight

  • Preflight subagent confirmed the checkout is already initialized for this Bun/TypeScript monorepo: bun.lock and node_modules are present, no submodules were detected, and no additional setup command was required before implementation.
  • Generated root workflow reports (analysis-report.md, implementation-report.md, preflight-report.md, validation-report.md) were present at the start of iteration 2 and were treated as disposable artifacts.

Decisions and tradeoffs

  • Stage-chat /exit is now destructive only when exact: argument-bearing inputs such as /exit now and /exit 1 return false from the stage slash handler so the normal stage prompt submission path handles them.
  • Existing workflow command semantics were preserved: /quit remains overlay-close only, and /compact continues accepting joined argument text.
  • Added tests at both the StageChatView level and attached-pane level to lock in argument-bearing /exit fallthrough behavior.
  • The untracked spec file remains present; validation subagent flagged it for reviewer decision, but it was not removed because it is the authoritative workflow spec path supplied for this run.

Validation outcomes

  • Final validation subagent confirmed:
    • bun test test/unit/slash-commands.test.ts test/unit/stage-chat-view.test.ts test/unit/workflow-attach-pane.test.ts passed (135 tests).
    • cd packages/coding-agent && bun run test interactive-mode-status.test.ts passed (34 tests).
    • bun run typecheck passed.
    • git status --short shows generated root reports are absent.

Blockers

  • No validation blockers remain for iteration 2.

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown

PR Review: feat(cli): add /exit slash command

Thanks for this! The main-interactive path is clean and the exact-match approach to a destructive command is the right call. I have one significant concern about implementation/spec drift, plus a few smaller notes.

🔴 Implementation diverges from the PR description & committed spec

The PR summary (bullet 3) and the 458-line RFC in specs/2026-06-02-...-1200.md both describe threading workflow attached-stage /exit to host app shutdown via an optional onExitApp callback (Sections 4.1, 5.1, 5.3 spell out StageChatViewOpts.onExitApp, workflow-attach-pane.ts, overlay-adapter.ts, and extension/index.ts wiring, plus exact-only matching with onClose() fallback).

The actual code does none of that. The only change to packages/workflows/src/tui/stage-chat-view.ts is the removal of case "/exit": from the /quit switch:

case "/quit":
  this.onClose();
  return true;
// case "/exit":  <-- removed
default:
  return false;

grep -rn onExitApp packages/ test/ returns nothing outside the spec .md. So in a workflow attached stage chat, /exit now falls through to default: return false and is sent to the agent as a normal prompt — it neither exits the app nor closes the overlay.

This means:

  • The committed spec is now stale/misleading: it documents an onExitApp design that was never implemented. Either update the spec to match the simpler decision actually shipped, or implement what the spec describes.
  • The PR description's "Thread workflow attached-stage /exit to host app shutdown when available" is not accurate for the merged code.

🟡 Behavior change in workflow stage chat — intended?

Previously /exit in an attached stage chat closed the overlay (onClose). Now it's forwarded to the model as the literal text /exit. The tests assert this (promptCalls == [input]), so it's deliberate — but worth confirming the UX. The issue's goal is consistent /exit behavior "where slash commands are supported," yet the result is three different behaviors:

  • main interactive: exits the app
  • workflow stage chat: sent to the agent as text
  • (old) overlay-close: removed

A user in a stage who types /exit to leave will instead prompt the model with "/exit". If forwarding-to-agent is the intended call, fine — but please align the spec/description.

🟡 Doc string inconsistency

  • README.md: | /exit | Exit pi |
  • docs/usage.md: | /exit | Exit Atomic |
  • autocomplete metadata: Exit ${APP_NAME} (resolves to Exit Atomic in this fork)

The README line matches the existing Quit pi row so it's internally consistent, but it drifts from both usage.md and the rendered autocomplete text. The spec's own Open Question #3 flags exactly this. Worth settling on one wording.

🟢 Things that look good

  • Exact-match guard (text === "/exit") correctly lets /exit now fall through as normal input — good defense for a destructive command.
  • Reuses the existing shutdown() path rather than duplicating shutdown logic.
  • Solid test coverage: built-in metadata, autocomplete conflict-hiding for a colliding extension exit, exact-vs-args routing, and /quit overlay-close preserved.
  • CHANGELOG entry correctly placed under ## [Unreleased]### Added.
  • No generated root run-reports leaked into the diff (the spec called this out as a prior regression).

🟢 Minor / nit

  • test/interactive-mode-status.test.ts reaches into (InteractiveMode as any).prototype.setupEditorSubmitHandler.call(fakeThis) with a hand-rolled fake this typed any. Pragmatic for exercising a private method, but brittle against refactors (the fake must keep mirroring onInputCallback, flushPendingBashComponents, etc., or the test can pass without exercising the path), and the any runs against CLAUDE.md's "avoid any" guidance. Consider a typed minimal interface.

Summary

The core /exit feature is well-built and well-tested. The blocker is the gap between the committed spec/PR description (which promise onExitApp host-shutdown threading) and the shipped code (which simply drops /exit from stage-chat handling). Please reconcile the two — implement the spec'd behavior, or update the spec/description so the repo's design doc reflects reality.

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown

PR Review: feat(cli): add /exit slash command

Thanks for the thorough work here — the core change is small, focused, and well-tested, and it reuses the existing /quit graceful-shutdown path rather than introducing a parallel one. The docs/changelog are updated and the README's stale Quit piQuit Atomic got fixed along the way. A few things worth discussing before merge:

🔴 Spec contradicts the implementation

The committed specs/2026-06-02-…-1200.md (458 lines) specifies an onExitApp callback threaded through stage-chat-view.ts, workflow-attach-pane.ts, overlay-adapter.ts, and extension/index.ts, with stage-chat exact /exit calling onExitApp() (and falling back to onClose()). None of that exists in the codegrep -rn onExitApp packages/ test/ returns nothing. The actual implementation just deletes the case "/exit": from the stage-chat switch so it falls through as normal text.

The simpler implementation is arguably the better choice (less coupling, avoids the destructive /exit now bug), but a committed RFC that describes a different design than what shipped is misleading for future readers. Please either update the spec to match what was built, or trim it to the as-implemented design. Its §5.1/§5.3/§8.3 sections and several "Goals" bullets ("workflow stage exact /exit calls onExitApp") are now inaccurate.

🟡 Behavior consistency vs. issue #1200

Issue #1200's acceptance criteria asks for behavior that's "consistent regardless of the active Atomic instance/session type where slash commands are supported." With this PR:

Context /exit behavior
Main TUI Graceful shutdown
Workflow stage chat Sent to the sub-agent as a literal /exit prompt

So exact /exit in a stage went from "close the overlay" (previous behavior) to "submit /exit as a prompt to the agent." A user who previously used /exit to leave a stage overlay will now silently send a confusing message to the agent and have to discover /quit instead. This is a defensible trade-off (it cleanly fixes the /exit now destructive-shutdown bug with minimal surface area), but it's worth confirming it's the intended product call, since it's a slight UX regression for that specific path and is in tension with the "consistent everywhere" criterion. A one-line note in the changelog/docs that /exit is main-chat-only would help set expectations.

🟢 Minor

  • Changelog attribution: per CLAUDE.md, issue-driven entries should link the issue, e.g. … from the main chat ([#1200](https://github.com/bastani-inc/atomic/issues/1200)). The new entry omits the link.
  • any in tests: interactive-mode-status.test.ts's installSubmitHandler leans on fakeThis: any and (InteractiveMode as any).prototype…. CLAUDE.md discourages any; a small typed shape for the faked this (even a local interface) would catch drift if the real handler starts reading new fields. Acceptable for private-method testing, just flagging.
  • The text = text.trim() at the top of the submit handler means /exit (trailing space) also exits — consistent with /quit, so no action needed, just noting it's covered.

Tests

Good coverage of the matrix: built-in metadata, autocomplete dedup of a conflicting extension exit, exact-vs-args routing in main TUI, and stage-chat / attach-pane fall-through for /exit, /exit now, /exit 1 plus /quit still closing the overlay. (I couldn't execute bun test in this environment due to sandbox restrictions — relying on CI here.) Consider one extra assertion documenting that /exit reaching the agent as a prompt is the intended stage-chat contract, since that's the surprising part of the new behavior.

Overall: solid, mergeable once the spec is reconciled with the implementation and the consistency trade-off is confirmed as intended.

@flora131
flora131 merged commit 8697d5d into main Jun 3, 2026
9 checks passed
@lavaman131
lavaman131 deleted the feature/issue-1200-exit-command branch June 21, 2026 00:45
lavaman131 pushed a commit that referenced this pull request Jun 29, 2026
* feat(cli): add exit slash command

* fix(workflows): keep exit scoped to main chat

* docs(coding-agent): update exit command branding

Assistant-model: GPT-5.5
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.

Add /exit command to exit Atomic CLI

1 participant