A terminal chat UI for Claude Code. ask spawns the claude CLI as a
subprocess and wraps it in a Bubble Tea TUI with inline markdown,
image attachments, a scrollable history, session resume, and a
custom MCP server that replaces the built-in AskUserQuestion tool
with a far richer tabbed modal.
- Chat with Claude Code via streaming JSON input/output
- Resume sessions —
/resumeopens a picker of prior conversations in the current directory - Pick the Claude model —
/modelopens a picker (default / haiku / sonnet / opus / custom) and persists the choice - Configurable UI —
/configtoggles quiet mode, cursor blink, inline diff rendering, and skip-all-permissions; persisted to~/.config/ask/ask.json - Themes — pick a palette from
/config→ Theme (15 flavors:default,dracula,nord,gruvbox,tokyo night, the four Catppuccin variantslatte/frappé/macchiato/mochaplus the green-leaning Mocha siblingmatcha,rose pine,fighter(Monokai Pro),love(crush),hacker(Matrix),amber(CRT)). Backgrounds, foregrounds, borders, and glamour markdown/syntax highlighting all follow the active theme. - Inline markdown rendering with glamour, cached per history entry so typing stays responsive in long chats
- Live turn status — spinner line surfaces the tool Claude is running (
Read: file.go,Bash: <description>,Grep: <pattern>,Task: <subagent>, …) - Live todo panel —
TodoWriteentries render inline as a bordered box with ☐ / ▸ / ✓ markers while the turn is active - Inline diffs —
Edit/Write/NotebookEditstructured patches render as colored unified diffs in history (toggle with/config) - Input history —
↑/↓at the first line of the input walks prior sent messages - Shell mode — type
!on an empty prompt to run a command through your$SHELL; stdout/stderr stream into history (capped at 100 lines),cdpersists,Esc/Ctrl+C/ double-backspace exits,↑/↓walks shell history separately from LLM input history - Image attachments via clipboard paste
Ctrl+Von Wayland reads the clipboard withwl-paste- In Kitty-compatible terminals (Kitty, Ghostty) images render as inline thumbnails using the Kitty graphics protocol with Unicode placeholders
- In any other terminal they fall back to a text chip
- Multiple attachments pasted in a row show side-by-side with a bordered preview
- Draggable scrollbar in the right column (mouse or
PgUp/PgDn); the viewport sticks to the bottom only while you are at the bottom, so scrolling up during a stream no longer yanks you back - Built-in MCP server exposing two tools:
ask_user_question— tabbed modal with three question kinds:pick_one— single select radio listpick_many— multi-select checkboxespick_diagram— radio list with an ASCII-art preview box rendered beside it- All kinds support
allow_custom(appends an Enter-your-own free-text option) and per-question notes (n)
approval_prompt— wired as Claude's--permission-prompt-tool, shows a per-tool allow / deny / always-allow modal (concise one-line summary — no field dump) before the tool runs; "always allow" records a session-scoped rule so repeat calls for the same file or command skip the prompt
- PreToolUse hook injected at launch that blocks Claude's built-in
AskUserQuestionand redirects the model to our MCP tool instead
go install github.com/Cidan/ask@latest
Requires Go 1.26+ and the claude CLI on your PATH.
Optional dependencies:
wl-clipboard— for image paste on Wayland (pacman -S wl-clipboard, etc.)- A terminal speaking the Kitty graphics protocol — for inline thumbnails (Kitty, Ghostty). Without one, images still send to Claude; only the local preview falls back to a text chip.
Launch in any directory:
ask
| Command | What it does |
|---|---|
/resume |
Pick a prior session in this directory |
/new / /clear |
Discard history and start a fresh session |
/model |
Choose the Claude model (default / haiku / sonnet / opus / custom) |
/config |
Open the ask config modal (see Config) |
Claude's own slash commands (the ones surfaced by claude at init) are
merged into the popover alongside these. Typing / filters both lists.
ask intercepts cd and ls as local shell-style builtins before the
input is ever sent to Claude, so you can navigate without dropping out
of the TUI.
| Command | What it does |
|---|---|
cd [path] |
Change the working directory. No arg → home. Tilde (~, ~/foo) expands. Kills the live claude subprocess and clears history, since Claude sessions are bound to a cwd. |
ls [path] |
Colorized listing (dirs first, executables, symlinks) with mode, human size, and "X ago" timestamps. No arg → current dir. Globs (*, ?, […]) and tilde expansion both work; ls path/to/file prints a single-row entry. |
Tab on cd or ls triggers path completion against the current
prefix, same as anywhere else a path is expected.
Type ! as the first character of an empty prompt to enter shell
mode. The ! is consumed and a Shell Mode indicator appears on
the spinner row. Enter sends the input to your $SHELL (falling back
to /bin/sh), and stdout/stderr stream line-by-line into history in
the same slot LLM responses use. Output is capped at 100 lines per
command with a … output truncated at 100 lines marker; the command
still runs to completion, and the pipe stays drained so it can't block
on a full kernel buffer.
cd and anything else that changes $PWD inside the subshell
persists into ask's own process after the command returns, so the
prompt, /resume, and path completion all track the new directory.
Exit shell mode with Esc, Ctrl+C on an empty prompt, or two
consecutive Backspace presses on an empty prompt. While a command is
running, Ctrl+C SIGKILLs the whole process group instead of leaving
the mode. Shell mode keeps its own ↑ / ↓ history independent of
the LLM input history, and the /-slash popover plus cd / ls path
picker are suppressed while active.
Curses / full-screen apps (vim, htop, less, …) are not supported — output goes through pipes, not a PTY, so their altscreen sequences render as raw text in history. Drop to a separate shell for those.
| Key | Action |
|---|---|
Enter |
Send message / confirm |
Shift+Enter, Ctrl+J |
Insert newline in the input |
Ctrl+V |
Paste image from clipboard |
Ctrl+C / Esc |
While a turn is running, open a Stop this turn? confirm box; on confirm it kills the claude subprocess and a new one spawns on the next send. Esc also clears pending attachments when idle. |
Ctrl+C (twice, idle) |
Quit. First press shows a Press ctrl+c again to exit hint; a second Ctrl+C quits. Any other key disarms the hint. |
Ctrl+D |
Quit immediately |
PgUp / PgDn |
Scroll the viewport half a page |
| Mouse wheel | Scroll the viewport |
Mouse click on │ |
Jump to that position on the scrollbar |
↑ / ↓ |
Navigate lists (session picker, slash menu, modal); at the first line of an empty/unmodified input they walk prior sent messages. In shell mode they walk the shell-only history. |
Tab |
Auto-complete a path or slash command |
! (empty prompt) |
Enter shell mode |
| Key | Action |
|---|---|
↑ / ↓ |
Move cursor between options |
Space |
Toggle selection (pick-many) |
Enter |
Commit current tab and advance; submit on the last tab |
← / →, Tab |
Switch between question tabs |
n |
Add a note to the current question |
| Typing on "Enter your own" | Fills the custom answer in place; Shift+Enter for a newline |
Esc |
Cancel the dialog |
/config opens a modal with toggles that persist to
~/.config/ask/ask.json. Typing filters the list; ↑ / ↓ move, Enter
toggles the highlighted entry and writes the file immediately, Esc
closes the modal.
| Toggle | Default | What it does |
|---|---|---|
| Quiet Mode | on | When on, assistant text chunks stream silently and the combined turn is rendered once at the end; when off, each chunk is appended as it arrives. |
| Cursor Blink | on | Blinking input cursor at a 650ms cadence. Off keeps a steady cursor. |
| Render Diffs | on | Render Edit / Write / NotebookEdit structured patches as inline colored diffs. Off suppresses the diff block (the edit still happens). |
| Skip All Permissions | off | Pass --dangerously-skip-permissions to the claude subprocess so every tool call bypasses the approval modal. Toggling kills the running subprocess; the next send respawns with the new flag state. |
| Worktree | off | Pass --worktree to fresh claude invocations so the session runs inside an isolated git worktree. Not passed on --resume (resume handles it internally) and not passed to the one-off init probe that caches slash commands. Silently dropped outside a git checkout (claude refuses to start with --worktree in that case). Toggling kills the running subprocess; the next send respawns with the new flag state. As an opinionated safety check, enabling this (via toggle or by starting with it already on in the config file) also appends .claude/worktrees/ to the repo's .gitignore if no existing rule already covers that path. No-op outside a git checkout. |
Other fields the config file stores automatically:
claude.model— last/modelpick; passed as--modelto the claude subprocess on the next spawn.claude.slashCommands— cache of slash commands reported byclaude's init event, so the popover has completions before the first real call.
The file is created on first launch and rewritten whenever a value
changes; hand-editing it while ask is closed is fine.
When ask launches it listens on 127.0.0.1:<random-port> and exposes
two Streamable-HTTP MCP tools, ask_user_question and
approval_prompt. The spawned claude subprocess is given:
--mcp-configpointing at this endpoint--settingsinstalling aPreToolUsehook that blocks the built-inAskUserQuestiontool and redirects the model tomcp__ask__ask_user_question--permission-prompt-tool mcp__ask__approval_promptso every permission-gated tool call routes through the ask TUI's approval modal
Response:
{
"answers": [
{ "picks": ["…"], "custom": "…optional…", "note": "…optional…" }
],
"cancelled": false
}The tool description pins the rules the model must follow for
pick_diagram previews:
- Monospace box-drawing characters only:
╭╮╰╯─│├┤┬┴┼ - Fill blocks:
░for content areas,▓for interactive/accent areas - No emoji, no tabs, no trailing whitespace
- ≤ 40 columns × ≤ 12 rows (all diagrams in one question are padded to the same bounding box before rendering)
Set ASK_DEBUG=1 to write a trace to /tmp/ask.log (paste/send/claude
stream events, MCP tool dispatch, etc.). Helpful when the TUI feels
stuck — pair it with the in-history stderr surfaced on Claude exit.
See LICENSE.

{ "questions": [ { "kind": "pick_one" | "pick_many" | "pick_diagram", "prompt": "…", "options": [ { "label": "…", "diagram": "…optional; required for pick_diagram…" } ], "allow_custom": false // pick_one and pick_many only } ] }