AI agent in your terminal
curl -sSf https://raw.githubusercontent.com/pwittchen/aictl/master/install.sh | shThe installer downloads a prebuilt binary for your platform from the latest GitHub release and places it in ~/.local/bin/aictl. If aictl is already installed at ~/.cargo/bin/aictl (e.g. from a prior cargo install), the installer updates it in place at that location instead of the default ~/.local/bin/. Set AICTL_INSTALL_DIR to pick a different location explicitly. If no prebuilt binary exists for your platform, the installer falls back to building from source with cargo install.
Prebuilt binaries are published for:
| OS | Architectures |
|---|---|
| Linux | x86_64, aarch64 |
| macOS | x86_64, aarch64 (Apple Silicon) |
Native Windows is not supported — aictl depends on a POSIX shell (sh) and Unix tools (date, pbcopy, etc.) for its built-in tool calls. Windows users can run aictl inside WSL using the Linux binary, which works normally.
Other platforms (FreeBSD, other BSDs, uncommon Linux architectures) can still build from source via the cargo install fallback path, provided a Rust toolchain is available.
Installing a prebuilt binary has no prerequisites beyond curl. Building from source (either via the installer fallback or manually) requires Rust (edition 2024).
git clone git@github.com:pwittchen/aictl.git
cd aictl
cargo install --path .This installs the aictl binary to ~/.cargo/bin/.
cargo build --releaseThe binary will be at target/release/aictl.
The install script places the binary at ~/.local/bin/aictl (or $AICTL_INSTALL_DIR if you set it). Remove it with:
rm ~/.local/bin/aictlCargo tracks its own installs, so the clean way is:
cargo uninstall aictlThis removes ~/.cargo/bin/aictl. If cargo uninstall doesn't find it (e.g. installed under a different crate name), delete the binary directly:
rm ~/.cargo/bin/aictlaictl stores all state under ~/.aictl/ — config file, saved agents, saved sessions. To wipe it completely:
rm -rf ~/.aictlSkip this step if you plan to reinstall and want to keep your API keys, agents, and session history.
aictl [--version] [--update] [--config] [--provider <PROVIDER>] [--model <MODEL>] [--message <MESSAGE>] [--auto] [--quiet] [--unrestricted] [--incognito] [--agent <NAME>] [--list-agents] [--session <ID|NAME>] [--list-sessions] [--clear-sessions]Omit --message to enter interactive REPL mode with persistent conversation history.
The interactive REPL supports slash commands:
| Command | Description |
|---|---|
/agent |
Manage agents (create manually, create with AI, view/load/delete, unload) |
/clear |
Clear conversation context |
/compact |
Summarize conversation into a compact context |
/context |
Show context usage (token and message counts vs limits) |
/copy |
Copy last response to clipboard |
/help |
Show available commands |
/info |
Show setup info (provider, model, behavior, memory, agent, version, OS, binary size) |
/issues |
Fetch and display known issues from the remote ISSUES.md |
/memory |
Switch memory mode: long-term (all messages) or short-term (sliding window) |
/security |
Show current security policy (blocked commands, CWD jail, timeouts, etc.) |
/session |
Manage sessions (show current info, set name, view/load/delete saved, clear all) |
/behavior |
Switch between auto and human-in-the-loop mode during the session |
/model |
Switch model and provider during the session (persists to ~/.aictl/config) |
/tools |
Show available tools |
/update |
Update to the latest version |
/exit |
Exit the REPL |
Press Esc during any LLM call or tool execution to interrupt the operation and return to the prompt. Conversation history is rolled back so the interrupted turn has no effect.
| Flag | Short | Description |
|---|---|---|
--version |
-V |
Print version information |
--update |
-u |
Update to the latest version |
--config |
-C |
Interactive configuration wizard — set provider, model, and API keys step by step |
--provider |
-p |
LLM provider (openai, anthropic, gemini, grok, mistral, deepseek, kimi, zai, or ollama). Falls back to AICTL_PROVIDER in ~/.aictl/config |
--model |
-M |
Model name (e.g. gpt-4o). Falls back to AICTL_MODEL in ~/.aictl/config |
--message |
-m |
Message to send (omit for interactive mode) |
--agent |
-A |
Load a saved agent by name (works in both single-shot and interactive modes) |
--list-agents |
-L |
Print saved agents from ~/.aictl/agents/ and exit |
--auto |
-a |
Run in autonomous mode (skip tool confirmation prompts) |
--quiet |
-q |
Suppress tool calls and reasoning, only print the final answer (requires --auto) |
--unrestricted |
-U |
Disable all security restrictions (use with caution) |
--incognito |
-i |
Start interactive REPL without saving any session (disables /session). Falls back to AICTL_INCOGNITO in ~/.aictl/config |
--session |
-s |
Load a saved session by uuid or name on startup (interactive mode only) |
--list-sessions |
-l |
Print saved sessions from ~/.aictl/sessions/ and exit |
--clear-sessions |
-c |
Remove all saved sessions and exit |
CLI flags take priority over config file values.
In interactive mode, each REPL run is a session. A new uuid is generated at startup and the conversation is persisted to ~/.aictl/sessions/<uuid> as JSON after every agent turn and compaction. Session names (optional, unique) are stored in ~/.aictl/sessions/.names. On exit, the session uuid (and name, if set) is printed.
Use /session to show current session info, assign a readable name, browse saved sessions (load or delete with confirmation), or clear all sessions. Pass --session <uuid|name> to resume an existing session on startup. Incognito mode (--incognito or AICTL_INCOGNITO=true) runs the REPL without creating or saving any session file; /session is disabled and displays a notice.
Agents are reusable system prompt extensions that specialize the LLM for dedicated tasks or behaviors. Agent prompts are stored as plain text files in ~/.aictl/agents/.
Use /agent to open the agent menu:
- Create agent manually — enter a name and type or paste the agent prompt text directly
- Create agent with AI — provide a name and brief description; the LLM generates the full agent prompt
- View all agents — browse saved agents, view their prompt, load an agent, or delete it
- Unload agent — remove the currently loaded agent (only shown when one is loaded)
Agents can also be loaded from the command line with --agent <name> (or -A <name>), which works in both single-shot and interactive modes.
Agent names may contain only letters, numbers, underscores, and dashes. When an agent is loaded, its prompt is appended to the system prompt and the agent name appears in magenta brackets before the input prompt (e.g. [my-agent] ❯).
Configuration is loaded from ~/.aictl/config. This is a single global config file.
Additionally, aictl loads a project prompt file from the current working directory (default: AICTL.md). If present, its contents are appended to the system prompt, allowing per-project instructions for the agent. The filename can be customized via AICTL_PROMPT_FILE in ~/.aictl/config.
The quickest way to get started is the interactive wizard:
aictl --configIt walks you through selecting a provider, model, and entering API keys. You can also edit ~/.aictl/config manually at any time.
You need to configure API key for the provider and model you want to use. AICTL_MEMORY and AICTL_INCOGNITO params are optional.
| Key | Description |
|---|---|
AICTL_PROVIDER |
Default provider (openai, anthropic, gemini, grok, mistral, deepseek, kimi, zai, or ollama) |
AICTL_MODEL |
Default model name |
AICTL_MEMORY |
Memory mode: long-term (all messages, default) or short-term (sliding window) |
AICTL_INCOGNITO |
Start interactive REPL without saving sessions. Accepts true or false (default: false) |
AICTL_PROMPT_FILE |
Filename for the project prompt file loaded from the current directory (default: AICTL.md) |
AICTL_TOOLS_ENABLED |
Enable or disable all tool calls. When false, the LLM can only respond with plain text (default: true) |
AICTL_AUTO_COMPACT_THRESHOLD |
Context usage percentage at which the REPL auto-compacts the conversation. Accepts an integer in 1..=100 (default: 80) |
FIRECRAWL_API_KEY is optional and is needed only if you want to use search_web tool.
Not all API keys are required. You need to provide only those, for which you set AICTL_PROVIDER and AICTL_MODEL.
If you want to use multiple LLM providers, then you need to provide appropriate keys.
| Key | Description |
|---|---|
LLM_OPENAI_API_KEY |
API key for OpenAI |
LLM_ANTHROPIC_API_KEY |
API key for Anthropic |
LLM_GEMINI_API_KEY |
API key for Google Gemini |
LLM_GROK_API_KEY |
API key for xAI Grok |
LLM_MISTRAL_API_KEY |
API key for Mistral |
LLM_DEEPSEEK_API_KEY |
API key for DeepSeek |
LLM_KIMI_API_KEY |
API key for Kimi (Moonshot AI) |
LLM_ZAI_API_KEY |
API key for Z.ai |
LLM_OLLAMA_HOST |
Ollama server URL (default: http://localhost:11434) |
FIRECRAWL_API_KEY |
API key for Firecrawl (search_web tool) |
| Key | Description |
|---|---|
AICTL_SECURITY |
Master security switch (default: true) |
AICTL_SECURITY_INJECTION_GUARD |
Block user prompts that look like prompt-injection attempts (default: true) |
AICTL_SECURITY_CWD_RESTRICT |
Restrict file tools to working directory (default: true) |
AICTL_SECURITY_SHELL_ALLOWED |
Comma-separated whitelist of allowed shell commands (empty = all except blocked) |
AICTL_SECURITY_SHELL_BLOCKED |
Additional blocked shell commands (added to built-in defaults) |
AICTL_SECURITY_BLOCK_SUBSHELL |
Block $(), backticks, and process substitution (default: true) |
AICTL_SECURITY_BLOCKED_PATHS |
Additional blocked file paths (added to built-in defaults) |
AICTL_SECURITY_ALLOWED_PATHS |
Paths allowed outside the working directory |
AICTL_SECURITY_SHELL_TIMEOUT |
Shell command timeout in seconds (default: 30) |
AICTL_SECURITY_MAX_WRITE |
Max file write size in bytes (default: 1048576 = 1 MB) |
AICTL_SECURITY_DISABLED_TOOLS |
Comma-separated tool names to disable (e.g. exec_shell,search_web) |
AICTL_SECURITY_BLOCKED_ENV |
Additional env vars to scrub from shell subprocesses |
Create ~/.aictl/config (see .aictl/config in this repo for the reference):
AICTL_PROVIDER=anthropic
AICTL_MODEL=claude-sonnet-4-20250514
LLM_ANTHROPIC_API_KEY=sk-ant-...
FIRECRAWL_API_KEY=fc-...
The file format supports comments (#), quoted values, and optional export prefixes.
aictl supports nine LLM providers:
Requires LLM_OPENAI_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
gpt-4.1-nano |
$0.10 | $0.40 |
gpt-4.1-mini |
$0.40 | $1.60 |
gpt-4.1 |
$2.00 | $8.00 |
gpt-4o-mini |
$0.15 | $0.60 |
gpt-4o |
$2.50 | $10.00 |
gpt-5-mini |
$0.25 | $2.00 |
gpt-5 |
$1.25 | $10.00 |
gpt-5.2 |
$1.75 | $14.00 |
gpt-5.2-pro |
$30.00 | $180.00 |
gpt-5.4-nano |
$0.20 | $1.25 |
gpt-5.4-mini |
$0.75 | $4.50 |
gpt-5.4 |
$2.50 | $15.00 |
gpt-5.4-pro |
$60.00 | $270.00 |
o4-mini |
$1.10 | $4.40 |
o3 |
$2.00 | $8.00 |
o1 |
$15.00 | $60.00 |
GPT-5.2 and GPT-5.4 use dual-tier pricing that doubles above the 272K context threshold; the table shows the short-context rates. The cost meter in aictl always reports the short-context price.
Requires LLM_ANTHROPIC_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
claude-haiku-* (3.x) |
$0.25 | $1.25 |
claude-haiku-4-* |
$1.00 | $5.00 |
claude-sonnet-* |
$3.00 | $15.00 |
claude-opus-4-5-* / claude-opus-4-6-* |
$5.00 | $25.00 |
claude-opus-4-* (older) |
$15.00 | $75.00 |
Requires LLM_GEMINI_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
gemini-3.1-pro-preview |
$2.00 | $12.00 |
gemini-3.1-flash-lite-preview |
$0.25 | $1.50 |
gemini-2.5-pro |
$1.25 | $10.00 |
gemini-2.5-flash |
$0.15 | $0.60 |
Gemini 3.1 Pro uses dual-tier pricing that doubles above a 200K context threshold; the table shows the short-context rates. gemini-2.0-flash has been removed from the model list because Google is shutting it down on June 1, 2026.
Requires LLM_GROK_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
grok-4 |
$3.00 | $15.00 |
grok-4-fast-reasoning / grok-4-fast-non-reasoning |
$0.20 | $0.50 |
grok-4-1-fast-reasoning / grok-4-1-fast-non-reasoning |
$0.20 | $0.50 |
grok-3 |
$3.00 | $15.00 |
grok-3-mini |
$0.30 | $0.50 |
Grok 4 Fast variants ship with a 2M-token context window, the largest available across frontier models.
Requires LLM_MISTRAL_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
mistral-large-latest |
$2.00 | $6.00 |
mistral-medium-latest |
$0.40 | $2.00 |
mistral-small-latest |
$0.10 | $0.30 |
codestral-latest |
$0.30 | $0.90 |
Requires LLM_DEEPSEEK_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
deepseek-chat |
$0.27 | $1.10 |
deepseek-reasoner |
$0.55 | $2.19 |
Requires LLM_KIMI_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
kimi-k2.5 |
$0.60 | $2.00 |
kimi-k2-0905-preview |
$0.60 | $2.00 |
kimi-k2-0711-preview |
$0.60 | $2.00 |
kimi-k2-turbo-preview |
$0.60 | $2.00 |
kimi-k2-thinking |
$0.60 | $2.00 |
kimi-k2-thinking-turbo |
$0.60 | $2.00 |
moonshot-v1-128k |
$0.60 | $2.00 |
moonshot-v1-32k |
$0.60 | $2.00 |
moonshot-v1-8k |
$0.60 | $2.00 |
Requires LLM_ZAI_API_KEY. Supported models with cost estimates (input/output per 1M tokens):
| Model | Input | Output |
|---|---|---|
glm-5.1 |
$1.40 | $4.40 |
glm-5-turbo |
$1.20 | $4.00 |
glm-5 |
$0.72 | $2.30 |
glm-4.7 |
$0.39 | $1.75 |
glm-4.7-flash |
$0.06 | $0.40 |
Ollama runs models locally — no API key required. Install Ollama from ollama.com, pull a model, and start the server:
ollama pull llama3.2
ollama serveThen configure aictl to use it:
AICTL_PROVIDER=ollama
AICTL_MODEL=llama3.2:latest
Available models are detected automatically from your local Ollama instance via the REST API. The /model command shows only models you have pulled locally. If Ollama is not running, it will not appear in the model menu.
By default, aictl connects to http://localhost:11434. To use a different address, set LLM_OLLAMA_HOST in ~/.aictl/config.
All Ollama models are free (self-hosted), so cost estimation shows $0.00.
Any model string can be passed via --model; cost estimation uses pattern matching on the model name and falls back to zero if unrecognized.
The per-token tables above tell you what each model charges; they don't tell you what a realistic workday actually costs. For that, see LLM_PRICING.md — it models two usage patterns (chat assistant and coding agent) and reports daily and monthly totals for every model in the catalog.
The headline numbers for intensive use (150 chat turns/day or 50 coding tasks/day, 22 working days/month, cached pricing):
| Usage pattern | Cheapest | Flagship cluster | Opus 4.6 |
|---|---|---|---|
| Chat | $2.64/mo (grok-4-fast) | ~$35–$48/mo | $69.74/mo |
| Coding agent | $34.76/mo (grok-4-fast) | ~$460–$525/mo | $874.50/mo |
A few things worth knowing before you budget:
- Intensive coding agent use is roughly 60× more expensive than chat use on any given model, because the agent loop re-sends the growing conversation history each iteration and produces long, code-heavy outputs. Tool call count is not the dominant factor.
- Prompt caching cuts costs roughly in half, but the "cached" column is only reliable for Anthropic — aictl explicitly writes to Anthropic's prompt cache via
cache_controlmarkers. OpenAI, Gemini, Grok, DeepSeek, and Kimi cache automatically server-side, so you'll hit cached rates during sustained sessions but not after idle gaps longer than the provider's TTL (typically 5–10 minutes). Z.ai GLM and Mistral have no cache handling in aictl, so they always bill at the full rate. - The cost meter that aictl prints after every turn reflects actual cached vs. fresh tokens from each provider's response, so it's more accurate than any estimate. If you want to know what your specific workload really costs, run a few typical sessions and watch the per-turn summary.
aictl runs an agent loop: the LLM can invoke tools, see their results, and continue reasoning until it produces a final answer.
By default, every tool call requires confirmation (y/N prompt). Use --auto to skip confirmation and run autonomously.
Available tools:
| Tool | Description |
|---|---|
exec_shell |
Execute a shell command via sh -c |
read_file |
Read the contents of a file |
write_file |
Write content to a file (first line = path, rest = content) |
remove_file |
Remove (delete) a file (regular files only, not directories) |
create_directory |
Create a directory and any missing parent directories |
list_directory |
List files and directories at a path with [FILE]/[DIR]/[LINK] prefixes |
search_files |
Search file contents by pattern (grep regex) with optional directory scope |
edit_file |
Apply a targeted find-and-replace edit to a file (exact unique match required) |
search_web |
Search the web via Firecrawl API (requires FIRECRAWL_API_KEY) |
find_files |
Find files matching a glob pattern (e.g. **/*.rs) with optional base directory |
fetch_url |
Fetch a URL and return readable text content (HTML tags stripped) |
extract_website |
Fetch a URL and extract only the main readable content (strips scripts, styles, nav, boilerplate) |
fetch_datetime |
Get the current date, time, timezone, and day of week |
fetch_geolocation |
Get geolocation data for an IP address (city, country, timezone, coordinates, ISP) via ip-api.com |
The tool-calling mechanism uses a custom XML format in the LLM response text (not provider-native tool APIs):
<tool name="exec_shell">
ls -la /tmp
</tool>The agent loop runs for up to 20 iterations. LLM reasoning is printed to stderr; the final answer goes to stdout. Token usage, estimated cost, and execution time are always displayed after each response.
All tool calls pass through a configurable security policy (src/security.rs) before execution. By default:
- Shell command blocking: dangerous commands are blocked (
rm,sudo,dd,mkfs,nc, etc.). Command substitution ($(...), backticks) is blocked. Compound commands (|,&&,||,;) are split and each segment is validated independently. - CWD jail: file tools (
read_file,write_file,remove_file,edit_file,create_directory,list_directory,search_files,find_files) can only operate within the working directory. Path traversal via..is defeated by canonicalization. - Blocked paths: sensitive paths are always blocked (
~/.ssh,~/.gnupg,~/.aictl,~/.aws,~/.config/gcloud,/etc/shadow,/etc/sudoers). - Environment scrubbing: shell subprocesses receive a clean environment — vars matching
*_KEY,*_SECRET,*_TOKEN,*_PASSWORDare stripped so API keys cannot leak. - Shell timeout: commands are killed after 30 seconds (configurable).
- Write size limit: file writes are capped at 1 MB (configurable).
- Output sanitization: tool results are sanitized to prevent prompt injection via
<tool>tags. - Injection guard: user prompts are scanned before being sent to the LLM. Inputs containing instruction-override phrases ("ignore previous instructions", "disable security", etc.) or forged role/tool tags (
<tool …>,<|system|>,### System:, etc.) are blocked with a clear error. Disable withAICTL_SECURITY_INJECTION_GUARD=false.
Security denials are returned to the LLM as tool results (displayed in red) so it can adapt. Use --unrestricted to disable all security checks. Individual settings are configurable via AICTL_SECURITY_* keys in ~/.aictl/config.
# With defaults configured in ~/.aictl/config, just run:
aictl
# Or send a single message:
aictl -m "What is Rust?"
# Override provider/model from the command line:
aictl -p openai -M gpt-4o -m "What is Rust?"
# Agent with tool calls (interactive confirmation)
aictl -m "List files in the current directory"
# Autonomous mode (no confirmation prompts)
aictl --auto -m "What OS am I running?"
# Quiet mode (only final answer, no tool calls or reasoning)
aictl --auto -q -m "What OS am I running?"cargo testUnit tests cover core logic across six modules: commands (slash command parsing), config (config file parsing), tools (tool-call XML parsing), ui (formatting helpers), llm (cost estimation and model matching), and security (shell validation, path validation, output sanitization). The session module handles persistence of REPL conversations under ~/.aictl/sessions/.
See ARCH.md for detailed ASCII diagrams covering:
- Module structure
- Startup flow
- Agent loop
- Tool execution dispatch
- LLM provider abstraction
- UI layer
- End-to-end data flow
This project includes Claude Code skills for common workflows. Run them as slash commands in a Claude Code session:
| Skill | Description |
|---|---|
/commit |
Commit staged and unstaged changes with a clear commit message |
/update-docs |
Update README.md, CLAUDE.md, and ARCH.md to match the current project state |
/evaluate-rust-quality |
Audit code quality, idiomatic Rust usage, and best practices |
/evaluate-rust-security |
Audit security posture, injection risks, and credential handling |
/evaluate-rust-performance |
Audit performance patterns, allocations, and CLI responsiveness |
Evaluation reports are saved to .claude/reports/ with timestamped filenames.
See ISSUES.md for a list of known issues and planned improvements.
This project is licensed under the PolyForm Noncommercial License 1.0.0. It is free to use for non-commercial purposes, including personal use, research, education, and use by non-profit organizations. For commercial use, please contact piotr@wittchen.io.

