A Unix and Unix-like command-line client for quick AI questions, persistent conversations, and explicitly authorized local automation.
AI-generated notice
The source code, tests, and documentation in this project are 100% AI-generated.
Current version: 0.5.0
- Ask directly from the command line with
ask "...". - Stream responses from OpenAI-compatible, Anthropic, and Gemini APIs.
- Configure providers, models, endpoints, credentials, headers, and context limits in a full-screen TUI.
- Resume persistent conversations through a session picker with message previews.
- Choose automatically, always, or never to continue a first terminal answer in the REPL.
- Resume an automatically exited conversation by running bare
askwithin 10 seconds. - Search history, complete commands, enter multiline prompts, and cancel input or generation with Ctrl-C.
- Let ordinary ask mode inspect workspace files and selected system status without write access.
- Provide native read-only Git tools for status, diffs, history, revisions, branches, and conflicts.
- Let the model request one-time or conversation-scoped do access through an explicit permission screen.
- Enable local tools with
--dofor files, commands, HTTP requests, web pages, and web search. - Run multi-round
model -> tool -> modelagent loops. - Isolate model-generated commands with Bubblewrap on Linux.
- Approve one exact command to run outside the workspace sandbox as the current user.
- Automatically compact active context at 70% of the configured context window while retaining the original transcript.
- Use stdin, stdout redirection, pipelines, and structured JSON output in scripts.
- Run an independent keyboard-first multi-AI Conference with agendas, roles, interruption, visible tool evidence, persisted meeting records, and moderator autopilot.
- 快速上手(中文):从构建、配置到普通对话、Do 模式和 AI Conference 的任务导向指南。
- AI Conference 功能规格:独立多 AI 会议工作流的产品边界、会议角色、TUI、键盘操作、工具授权和数据模型设计。
AI Conference 的主持人自动推进默认关闭。通过会议的 Configure autopilot permissions 或 /autopilot,可选择无限推进或 4/8/12/20 个自动发言轮次,并逐项预授权 write_file、沙箱内 run_command、fetch_http、browse_page 或 web_search。无限模式会持续到主持人明确请求用户决定、完成会议、用户中断或暂停;阶段深度只会触发主持人的总结检查点,不会静默结束会议。未勾选时自动模式仅使用只读工具;自动模式不会提升命令权限,用户可随时暂停、插话或关闭授权。
| Protocol | Configuration value | Non-streaming | Streaming | Tool calls |
|---|---|---|---|---|
| OpenAI Chat Completions | openai or openai_chat |
Yes | Yes | Yes |
| Anthropic Messages | anthropic |
Yes | Yes | Yes |
| Google Gemini generateContent | gemini |
Yes | Yes | Yes |
The OpenAI-compatible adapter works with OpenAI, DeepSeek, OpenRouter, Ollama, and other services that expose a compatible /chat/completions endpoint.
ask requires a C++20 compiler and these system libraries:
- libcurl
- JsonCpp
- SQLite 3
- ncursesw
- libedit
- Bubblewrap, required for sandboxed model commands on Linux
Fedora:
sudo dnf install gcc-c++ cmake ninja-build pkgconf-pkg-config \
libcurl-devel jsoncpp-devel sqlite-devel ncurses-devel libedit-devel bubblewrapDebian or Ubuntu:
sudo apt install build-essential cmake ninja-build pkg-config \
libcurl4-openssl-dev libjsoncpp-dev libsqlite3-dev libncursesw5-dev libedit-dev bubblewrapArch Linux:
sudo pacman -S base-devel cmake ninja pkgconf curl jsoncpp sqlite ncurses libedit bubblewrapConfigure, build, and test:
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failureInstall the executable, README, and license:
pkexec cmake --install buildYou can also run ./build/ask directly.
The chat client can be built on Unix-like systems with the required dependencies. The model-command sandbox currently uses Linux Bubblewrap. If no secure sandbox backend is available, model-generated commands fail closed instead of silently running without isolation.
Open the configuration TUI:
ask --configThe configuration interface supports:
- A General Settings home page for session behavior, the default model, AI calls, and providers
- Breadcrumb headings that preserve the full Settings / Provider / Model hierarchy
- Nested Providers and Models pages for connection-specific configuration
- Arrow-key navigation, Enter to open or select, and Esc to return
- Transactional editing with explicit Save changes and Cancel actions
- Provider creation, field-by-field editing, deletion, enablement, and model discovery
- OpenAI-compatible, Anthropic, and Gemini protocols
- API base URLs
- API keys stored directly or read from environment variables
- Additional HTTP headers
- Manual model lists and model discovery
- Per-provider context windows and timeouts
- A nested AI call page for Thinking strength and budget, Temperature, Top P, maximum output tokens, streaming, and advanced request JSON
- Per-model capability overrides for tool calls, streaming, Thinking, Temperature, Top P, JSON mode, and context window
- A Conversation entry page for Automatic, Always continue, and Always exit behavior, with a separately configurable Judge model
- Provider-default sampling and Thinking values that omit unsupported overrides instead of forcing them on every model
- Maximum tool-loop rounds
- Automatic compaction ratio and system prompt
The ask settings ➔ AI call page uses the same navigation model as the rest of the TUI: Up/Down moves focus, Left/Right adjusts a value, Enter opens a selector or editor, and Esc returns to General. Temperature and Top P accept explicit values from 0.0 to 1.0; choosing Provider default leaves the field out of the request. A Thinking budget of 0 means automatic budgeting from the selected strength.
Thinking settings are translated for each protocol rather than copied verbatim. OpenAI-compatible providers receive reasoning_effort; OpenRouter receives its reasoning object; Anthropic receives thinking.type and budget_tokens; Gemini receives generationConfig.thinkingConfig. Anthropic Thinking removes conflicting Temperature and Top P values and requires its budget to remain below max_tokens. Provider and model support still varies, so Provider default is the safest compatibility setting.
Each model has an effective capability profile. The built-in profile is inferred from the protocol and model name, while entries under a provider's model_capabilities object override it. Unsupported tools, streaming, Thinking, sampling, and JSON parameters are omitted even when advanced request JSON tries to add them. A model without streaming support automatically uses a complete request, and its registered context window controls automatic compaction.
Advanced request JSON must be an object. It is recursively merged after the normal generation controls, so it can override ordinary provider-specific parameters. Request structure remains protected: it cannot replace model, messages or contents, system instructions, tools, tool choice, or the streaming flag. Protocol safety checks still run on the final Anthropic Thinking request.
Environment variables are recommended for API keys. For example, the built-in DeepSeek provider reads DEEPSEEK_API_KEY:
export DEEPSEEK_API_KEY='your-api-key'
ask --provider deepseek --model deepseek-v4-flash "Hello"An environment variable value takes precedence over a key stored in the configuration file.
ask settings ➔ Conversation entry controls what happens after the first answer in an interactive terminal:
Automaticsends the original question and completed answer to the configured Judge model. The Judge returns onlyCONTINUEorEXIT. Its request is non-streaming, has no tools, and is not added to conversation history.Always continuepreserves the traditional behavior and enters the REPL without calling the Judge.Always exitsaves the first exchange and exits without calling the Judge.
If the Judge request fails, times out, or returns an invalid decision, ask safely enters the REPL and reports the failure on stderr. --no-repl, --json, piped/non-TTY use, and explicit -i do not call the Judge; -i always enters the REPL.
After Automatic chooses exit or Always exit applies, running bare ask in the same working directory within 10 seconds restores that conversation, prints resumed, and opens the REPL. The quick-resume record is consumed once. A prompt, resume, --config, or any other explicit entry option starts its requested flow instead. Restored sessions retain their provider, model, message history, working directory, and --do mode.
Usage:
ask [options] [prompt ...]
ask --do [options] [prompt ...]
ask --config
ask resume [session-id] [--provider ID] [--model MODEL]
ask conference [goal] [--provider ID] [--model MODEL]
ask conference resume [conference-id]
Options:
-p, --provider ID Override the provider for this session
-m, --model MODEL Override the model for this session
--do Start with workspace tools enabled
-i, --interactive Enter the REPL even when input was piped
--no-repl Exit after one response
--no-stream Wait for the complete response before printing
--json Emit one JSON object and disable streaming
-q, --quiet Hide tool progress messages
--config Open the settings TUI
-h, --help Show help
--version Show the version
Run ask conference "goal" to open the dedicated multi-AI meeting TUI. It creates a moderator, expert, auditor, and recorder with a visible agenda and meeting rules. Use the arrow keys to move between agenda, discussion, controls, and input; Enter opens or executes the selected action; Space pauses or resumes; ordinary typed text plus Enter is a high-priority user interruption. Conference records are persisted separately from ordinary conversations and can be resumed with ask conference resume.
Meeting members use only read-only workspace tools by default while advancing a speaker. Every request and its result appears as a timeline event. Selecting Run user-approved execution or entering /execute presents a confirmation first; the approved speaker receives full tools for that one meeting turn only. The user may export the current structured summary with /export [relative-path].
Examples:
# Use the configured default provider and model
ask "Explain edge-triggered epoll"
# Override the provider and model for this session
ask --provider deepseek --model deepseek-v4-flash "Review this SQL query"
# Allow the model to use tools inside the current directory
ask --do "Inspect this project and fix its build"
# Resume a saved conversation
ask resume
ask resume 20260719-120000-abcdef
# Run one non-streaming request and exit
ask --no-repl --no-stream "Reply with only yes or no"Responses stream to stdout by default when Stream output is enabled under ask settings ➔ AI call. OpenAI-compatible SSE deltas, Anthropic content blocks, and Gemini streamGenerateContent events are decoded incrementally. Tool names and JSON arguments are assembled internally and are not printed as protocol data. Later model turns in a do-mode tool loop continue streaming normally.
ask "Write a four-line poem"Use --no-stream to wait for the complete response regardless of the saved setting:
ask --no-stream "Summarize this error"Pressing Ctrl-C during generation cancels the current HTTP stream and returns to the REPL without exiting the conversation. Both --no-stream and --json override the saved streaming preference; --json always disables streaming so stdout remains one complete, valid JSON object.
When stdin and stdout are both terminals, the configured Conversation entry policy decides whether ask enters the REPL after the first response. Regular input continues the conversation. The following commands are also available:
| Input | Behavior |
|---|---|
?COMMAND |
Run a user shell command and retain its output, errors, and exit code as conversation context |
!do PROMPT |
Enable full tools for the next turn only |
!ask PROMPT |
Force read-only tools for the next turn; permission upgrades are unavailable |
!model |
Switch the provider and model for the current session |
!config |
Open configuration and then return to the current conversation |
!compact |
Ask the current model to summarize older active context |
!help |
Show REPL commands |
!q |
Save and quit |
Editing behavior:
- End a line with
\to continue the prompt on another line. - Press Ctrl-R to search persistent history.
- Press Tab to complete special commands and use normal filename completion.
- Press Ctrl-C while editing to clear the current input.
- Press Ctrl-D on an empty input to save and exit.
- Prefix
!or?with a backslash to enter it literally, for example\!q.
!do and !ask are one-turn overrides. They do not permanently change the session's base mode.
The REPL labels ordinary mode as [ask/read-only] and an upgraded session as [do].
If stdin or stdout is not a TTY, ask runs once and exits. It does not open a TUI or wait for another prompt.
# Combine an instruction with piped input
git diff | ask "Review this patch"
# Save plain text output
ask --no-repl "Generate a commit message" > commit-message.txt
# Read structured output
result=$(printf '%s' 'Explain this log' | ask --json)
printf '%s\n' "$result" | jq -r '.text'When both an argv prompt and piped stdin are present, ask combines them as prompt + blank line + stdin. Diagnostics and tool progress go to stderr. Plain answers or JSON go to stdout.
JSON output has this shape:
{
"session": "20260719-120000-abcdef",
"provider": "deepseek",
"model": "deepseek-v4-flash",
"text": "...",
"finish_reason": "stop",
"usage": {
"prompt_tokens": 42,
"completion_tokens": 18,
"total_tokens": 60
}
}Ordinary ask mode exposes only read-only tools:
| Tool | Purpose |
|---|---|
read_file |
Read part of a regular workspace file |
list_files |
List workspace files and directories without following symlinks |
search_text |
Search literal text in regular workspace files |
run_readonly_command |
Run a command and argument combination from a strict read-only allowlist |
request_do_mode |
Ask the user for explicit do access; the tool call itself grants nothing |
The command allowlist covers workspace inspection with pwd, ls, rg, stat, file,
wc, head, tail, and read-only git status, diff, log, or show. It also
supports constrained system-status queries through nvidia-smi, uname, lscpu,
free, df, and uptime. Each command has its own option whitelist. Workspace commands
run with a read-only workspace mount, and all commands use a fixed executable with a direct
argument vector rather than model-controlled shell syntax.
When a requested task needs mutation, the model can describe the reason and concrete operation
on ask permission ➔ Do mode. The permission screen starts on Deny; use Up/Down and Enter
to choose Allow once or Allow for conversation, or Esc to deny. Allow once grants the next
model response's complete tool-call batch and is then consumed. Allow for conversation changes
only the current conversation and survives save, explicit resume, and 10-second quick resume. It
does not alter global configuration or new conversations. Without an interactive stdin and stdout,
permission requests are denied automatically.
Every model request also receives an application-generated runtime permission block after the
configured system prompt. Before elevation, it identifies ASK_READ_ONLY, lists the tools available
now, lists the additional tools that full do mode would provide, and explains all three approval
outcomes. The block changes per request to one of ASK_READ_ONLY, FORCED_ASK_READ_ONLY,
DO_FOR_USER_TURN, DO_ONCE_THIS_RESPONSE, or DO_FOR_CONVERSATION. In particular, an
Allow once response tells the model that the complete tool-call batch returned in that response is
authorized, that returning the response consumes the grant even if it contains no tool calls, and
that the following model response is read-only again. The runtime block is included in token
estimation but is never stored as a conversation message or compacted into session history.
Do mode exposes these tools to the model:
| Tool | Purpose |
|---|---|
read_file |
Read part of a regular file inside the workspace |
write_file |
Write or append a file inside the workspace |
list_files |
List workspace files and directories |
search_text |
Search literal text in workspace files |
run_readonly_command |
Run a strictly allowlisted read-only command |
run_command |
Run a shell command in the Bubblewrap sandbox |
fetch_http |
Fetch a public HTTP resource with SSRF, timeout, and size protections |
browse_page |
Fetch a public web page and convert it to readable text |
web_search |
Search the public web |
Model-provided file paths are resolved against the directory where ask started. Absolute external paths, .. traversal, and symbolic links escaping the workspace are rejected.
Sandboxed model commands have these boundaries:
- Host environment variables are cleared to protect API keys and other secrets.
- System runtime files are mounted read-only.
/tmpis private.- The startup directory is the only host-writable mount.
- Commands keep the current user ID and never use root or sudo.
- Failure to start Bubblewrap causes the command to be rejected.
A model can request elevated: true for one command. Here, "elevated" only means running outside the workspace sandbox; the command still runs as the current unprivileged user. The approval prompt shows the reason, exact command, working directory, and environment policy. Approval applies once, is never cached, and is denied automatically without a controlling terminal.
A command entered directly by the user with ?COMMAND is not a model tool call and runs in the user's normal shell environment.
Network tools accept only http and https. They reject loopback, private, link-local, multicast, and .local destinations, pin validated DNS results, and validate every redirect target again.
Sessions, tool calls, permission results, and complete message history are stored in SQLite. ask resume shows timestamps, titles, modes, providers, models, and recent message previews. Resuming never repeats the last request automatically. Conversation-scoped do access is restored; one-time access is never persisted.
Before every request, ask estimates the size of:
- The configured system prompt and current runtime permission block
- Tool schemas
- The current compact summary
- Active messages
- Pending user input
- Reserved output tokens
When the prediction reaches the configured share of the model context window, ask asks the active model to create a compact summary. The default threshold is 70%. The original transcript remains in SQLite; only the active API request view advances to the summary and recent turns.
| Data | Explicit location | Default location |
|---|---|---|
| Configuration | $ASK_CONFIG_HOME/config.json |
$XDG_CONFIG_HOME/ask/config.json or ~/.config/ask/config.json |
| Session database | $ASK_DATA_HOME/sessions.db |
$XDG_DATA_HOME/ask/sessions.db or ~/.local/share/ask/sessions.db |
| REPL history | $ASK_DATA_HOME/history |
$XDG_DATA_HOME/ask/history or ~/.local/share/ask/history |
Directories are created with mode 0700. Configuration, database, and history files use mode 0600.
ctest --test-dir build --output-on-failureThe test suite covers configuration and session persistence, AI call validation and protocol request mapping, CLI parsing, path traversal, Bubblewrap isolation, approval boundaries, private-network rejection, OpenAI/Anthropic/Gemini streaming, fragmented tool calls, JSON and pipeline behavior, Ctrl-C cancellation, and fixed-size settings/model/resume TUI flows in a PTY.
This project is released under the MIT License.
The source code, tests, and documentation in this repository are 100% AI-generated.