Agent interface and works anywhere Smithers accepts an agent, including <Task>.
The agent spawns the CLI, passes the prompt, captures output, and returns a GenerateTextResult.
For API-billed provider wrappers, see SDK Agents.
Import
Prerequisites
| Agent | CLI Required | Install |
|---|---|---|
ClaudeCodeAgent | claude | Claude Code |
CodexAgent | codex | OpenAI Codex CLI |
GeminiAgent | gemini | Gemini CLI |
PiAgent | pi | PI Coding Agent |
KimiAgent | kimi | Kimi CLI |
ForgeAgent | forge | Forge CLI |
AmpAgent | amp | Amp CLI |
Quick Start
Hijack Support
All built-in CLI agents support native-session hijack viasmithers hijack <runId>.
| Agent | Hijack Mode | Native Relaunch |
|---|---|---|
ClaudeCodeAgent | Native CLI session | claude --resume <session> |
CodexAgent | Native CLI session | codex resume <session> -C <cwd> |
GeminiAgent | Native CLI session | gemini --resume <session> |
PiAgent | Native CLI session | pi --session <session> |
KimiAgent | Native CLI session | kimi --session <session> --work-dir <cwd> |
ForgeAgent | Native CLI session | forge --conversation-id <id> -C <cwd> |
AmpAgent | Native CLI session | amp threads continue <thread> |
- Live run: Smithers waits until the agent is between blocking tool calls before aborting.
- Finished/cancelled run: Smithers reopens the latest persisted native session.
- If the hijacked session exits successfully, the workflow resumes automatically in detached mode.
- Cross-engine hijack is not supported.
smithers hijack <runId> --launch=false to inspect the resumable candidate without opening the session.
Non-Idempotent Tool Resume Warning
When a<Task> retries after a failure, previous attempts may have already executed side-effect tools (e.g., sending messages, creating PRs). Smithers detects non-idempotent tool calls from prior attempts and prepends a warning to the agent’s prompt:
Previous attempts in this task already called non-idempotent side-effect tools. Those side effects may already have happened before the interruption or retry. Do not blindly call them again. Verify external state first or continue from the prior result.The warning includes the specific tool names and attempt numbers. It is automatically injected — no configuration is required.
Base Options
| Option | Default | Description |
|---|---|---|
id | Random UUID | Agent instance identifier |
model | undefined | Model name passed to --model |
systemPrompt | undefined | System instructions prepended to the prompt |
instructions | undefined | Alias for systemPrompt |
cwd | Tool context rootDir or process.cwd() | Working directory for the spawned process |
env | {} | Extra environment variables merged with process.env |
yolo | true | Skip all interactive permission prompts |
timeoutMs | undefined | Hard wall-clock timeout; kills process after this many ms |
idleTimeoutMs | undefined | Inactivity timeout; kills process after this many ms with no output |
maxOutputBytes | undefined | Truncate captured output to this size |
extraArgs | [] | Additional CLI flags |
Timeouts
timeoutMs: hard wall-clock cap.idleTimeoutMs: inactivity cap, resets on any stdout/stderr output.
ClaudeCodeAgent
Wrapsclaude CLI with --print mode.
Claude-Specific Options
| Option | Description |
|---|---|
permissionMode | "bypassPermissions", "acceptEdits", "default", "delegate", "dontAsk", "plan" |
allowedTools | Tool name whitelist |
disallowedTools | Tool name blacklist |
disableSlashCommands | Disable all slash commands |
maxBudgetUsd | Spending cap in USD |
mcpConfig | Model Context Protocol server configuration files |
mcpDebug | Enable MCP debug logging |
addDir | Additional context directories |
file | Files to inject into context |
fromPr | Pull request URL or number to use as additional context |
fallbackModel | Model to use if the primary model is unavailable |
appendSystemPrompt | Text appended to the system prompt |
agents | Multi-agent configuration as a map of agent definitions or JSON string |
betas | Beta feature flags to enable |
pluginDir | Plugin directories for Claude Code skills |
resume / sessionId | Resume a previous session by ID |
settings / settingSources | Override settings file or sources |
jsonSchema | JSON schema string for structured output |
includePartialMessages | Stream partial assistant messages |
inputFormat | "text" or "stream-json" for input |
outputFormat | "text", "json", or "stream-json" (default: "stream-json") |
yolo is true (default), the agent passes --allow-dangerously-skip-permissions, --dangerously-skip-permissions, and --permission-mode bypassPermissions unless permissionMode is explicitly set.
PR Context
ThefromPr option passes --from-pr <value> to the Claude CLI, loading the diff and metadata of the specified pull request into the conversation context. Accepts a PR URL or number:
CodexAgent
Wrapscodex CLI using codex exec with stdin input.
Codex-Specific Options
| Option | Description |
|---|---|
sandbox | "read-only", "workspace-write", or "danger-full-access" |
fullAuto | Full auto mode (no confirmations) |
dangerouslyBypassApprovalsAndSandbox | Skip all approval prompts and sandbox restrictions |
config | Configuration overrides as key-value pairs or raw strings |
oss | Use open-source models |
localProvider | Local model provider URL |
image | Image file paths to include as visual inputs |
outputSchema | Path to JSON schema file for structured output |
outputLastMessage | File path to write the last message (auto-generated if not set) |
yolo is true and fullAuto is not set, passes --dangerously-bypass-approvals-and-sandbox. If fullAuto is true, uses --full-auto instead.
Prompt is passed via stdin using the - argument.
GeminiAgent
Wraps thegemini CLI.
Gemini-Specific Options
| Option | Description |
|---|---|
sandbox | Run in sandbox mode |
approvalMode | "default", "auto_edit", "yolo", or "plan" |
allowedTools | Tool name whitelist |
allowedMcpServerNames | MCP server name whitelist |
extensions | Gemini CLI extensions to load |
resume | Resume a previous session by ID |
listSessions / deleteSession | Session management |
includeDirectories | Additional directories to include |
outputFormat | "text", "json", or "stream-json" (default: "json") |
yolo is true and approvalMode is not set, passes --yolo.
Prompt is passed via --prompt.
gcloud Authentication
When neitherGOOGLE_API_KEY nor GEMINI_API_KEY is set, Gemini CLI uses gcloud application-default credentials. The diagnostics api_key_valid check falls back to running gcloud auth print-access-token to confirm that gcloud auth is configured. No extra options are required — the Gemini CLI picks up the credentials automatically from the environment:
PiAgent
Wraps thepi CLI.
PI-Specific Options
| Option | Description |
|---|---|
provider | PI provider name (--provider) |
model | PI model (--model) |
apiKey | Passed to --api-key (prefer env/config for secrets) |
mode | text, json, or rpc |
print | Force --print in text mode |
continue / resume / session | Session continuation controls |
sessionDir | Custom session directory |
models / listModels | Scoped model patterns and listing |
extension | Extension path(s) |
skill | Skill path(s) |
promptTemplate | Prompt template path(s) |
theme | Theme path(s) |
tools / noTools | Enable specific tools or disable built-ins |
export | Export session HTML |
files | File args passed as @path (text/json modes) |
onExtensionUiRequest | RPC-only handler for extension UI requests |
noSession | Disable session persistence (default true unless session flags set) |
files emit as @path arguments. In rpc mode, the prompt is sent as JSON over stdin. Text mode defaults to --print without --mode; json/rpc set --mode and omit --print.
For workflow hijack, Smithers automatically uses PI’s structured event stream and keeps session persistence enabled regardless of noSession.
KimiAgent
Wrapskimi CLI using --print mode.
Kimi-Specific Options
| Option | Description |
|---|---|
thinking | Enable/disable thinking mode |
outputFormat | "text" or "stream-json" (default: "text") |
finalMessageOnly | Only print the final assistant message |
quiet | Alias for --print --output-format text --final-message-only |
agent | Built-in agent spec: "default" or "okabe" |
agentFile | Path to custom agent specification file |
workDir | Override the working directory for the kimi process |
session / continue | Session resumption and continuation |
skillsDir | Skills directory path |
mcpConfigFile / mcpConfig | MCP config file(s) or inline config |
maxStepsPerTurn | Max steps in one turn |
maxRetriesPerStep | Max retries in one step |
maxRalphIterations | Extra iterations after the first turn in Loop mode |
yolo is true (default), passes --print which implicitly adds --yolo.
Prompt is passed via --prompt.
Isolated Share Directory
Kimi stores per-session metadata in~/.kimi/ (or $KIMI_SHARE_DIR). When running parallel tasks, concurrent writes to this directory can corrupt kimi.json. KimiAgent automatically creates an isolated temporary directory per invocation, copies config.toml, credentials, device_id, and latest_version.txt from the default share dir, and sets KIMI_SHARE_DIR to the temporary copy. The directory is removed via the cleanup hook when the run completes.
To opt out of isolation and use a specific directory, set KIMI_SHARE_DIR in env:
ForgeAgent
Wrapsforge CLI. Supports 300+ models via --prompt.
Forge-Specific Options
| Option | Description |
|---|---|
directory | Working directory (-C); defaults to cwd |
provider | Model provider name |
agent | Agent type |
conversationId | Resume conversation by ID |
sandbox | Sandbox name |
restricted | Enable restricted mode |
workflow | Workflow file path |
event | Event JSON for workflow triggers |
conversation | Conversation file path |
--prompt mode auto-approves tool use; no separate yolo flag.
Prompt is passed via --prompt.
AmpAgent
Wrapsamp CLI using --execute mode.
Amp-Specific Options
| Option | Description |
|---|---|
visibility | Thread visibility: "private", "public", "workspace", "group" |
mcpConfig | MCP configuration file path |
settingsFile | Custom settings file path |
logLevel | "error", "warn", "info", "debug", "audit" |
logFile | Log output file path |
dangerouslyAllowAll | Allow all tool calls without confirmation |
yolo is true (default) or dangerouslyAllowAll is true, passes --dangerously-allow-all.
Prompt is passed via --execute. Automatically passes --no-ide, --no-jetbrains, --no-color, and --archive for headless execution.
Diagnostics
Before each run, Smithers launches a diagnostic probe concurrently with the agent process. If the agent fails, the probe’s findings are attached to the error and printed as a warning.DiagnosticReport contains:
CLI Installed Check
Thecli_installed check runs which <command> to confirm the binary is on PATH.
- pass — binary found;
detail.binaryPathcontains the resolved path. - fail — binary not found; install the CLI listed in Prerequisites.
API Key Check
Theapi_key_valid check verifies the API credential for each provider.
| Agent | Env var checked | Method |
|---|---|---|
ClaudeCodeAgent | ANTHROPIC_API_KEY | Format check (sk-ant-*); absent = subscription mode (pass) |
CodexAgent | OPENAI_API_KEY | GET /v1/models |
GeminiAgent | GOOGLE_API_KEY or GEMINI_API_KEY | GET /v1beta/models; falls back to gcloud auth |
AmpAgent | — | Skipped (Amp manages its own auth) |
Rate Limit Check
Therate_limit_status check probes the provider’s API for current quota headroom.
- Reads standard rate-limit headers (
anthropic-ratelimit-*,x-ratelimit-*). - Status is
skipwhen using gcloud auth or subscription mode. - If the check passed before the run but the error text contains rate-limit patterns (e.g.
429,too many requests,quota exceeded), the check is upgraded tofailpost-hoc and attached to the error.
Capability Registry
Every CLI agent exposes acapabilities property that describes its tool surface. Smithers uses this at runtime to normalize tool names and verify that the agent configuration is self-consistent.
Normalization
normalizeCapabilityRegistry canonicalizes a registry before comparison or hashing: string lists are deduplicated and sorted, tool descriptor fields are trimmed, and empty optional values are removed.
normalizeCapabilityStringList applies the same rules to any standalone string array:
Hashing
hashCapabilityRegistry produces a stable SHA-256 hex fingerprint of the normalized registry. Use it to detect configuration drift between agent invocations or CI runs.
getCliAgentCapabilityReport() as entry.fingerprint.
Capability Doctor
getCliAgentCapabilityDoctorReport() validates every built-in CLI agent’s registry against consistency rules and returns a report with per-agent issues:
Agent Contract
The agent contract describes the Smithers MCP server tool surface that is injected into an agent’s context. It is separate from the capability registry — the registry describes what the agent can do, while the contract describes what Smithers exposes to the agent.Raw vs. Semantic Tool Surface
SmithersToolSurface is "raw" or "semantic". The semantic surface groups and renames tools to reduce noise for general-purpose agents. The raw surface exposes every tool name as-is.
smithers ask builds the MCP launch configuration and probes the live tool surface internally. For direct package use, build a contract from the tools you expose to an agent:
Prompt Guidance
contract.promptGuidance is a compact, instruction-friendly string listing available tools grouped by category. Inject it into an agent’s system prompt:
Docs Guidance
contract.docsGuidance is a Markdown table listing every tool with its category, destructive flag, and description. Suitable for injecting into documentation or longer context windows:
Token and Usage Tracking
Smithers extracts token usage from raw CLI output and populates theusage field of the returned GenerateTextResult. This works across all built-in agents without additional configuration.
Usage Extraction
BaseCliAgent parses raw CLI stdout to find token counts. The extraction strategy is format-specific:
| Agent / Format | Source |
|---|---|
ClaudeCodeAgent stream-json | message_start.message.usage (input) + message_delta.usage (output) |
CodexAgent --json | turn.completed.usage |
GeminiAgent json | stats.models[*].tokens |
| Generic NDJSON | Any line with a usage object containing input_tokens / output_tokens |
cache_read_input_tokens, cached_input_tokens), cache write tokens (cache_creation_input_tokens), and reasoning tokens (reasoning_tokens) are accumulated when present.
BaseCliAgent Internals
Cleanup Hook
CliCommandSpec.cleanup is an optional async () => void returned by buildCommand. It runs after the agent process exits, whether the run succeeds or fails. Use it to remove temporary files:
Effect.ensuring, so it is guaranteed to execute even when the command throws.
Stdout Error Detection
Some CLIs exit with code 0 but print an error message to stdout. ThestdoutErrorPatterns field on CliCommandSpec accepts an array of RegExp patterns. If any pattern matches the cleaned stdout text (after banner stripping), the agent throws AGENT_CLI_ERROR with the matched content as the message:
{ or [ (i.e., JSON output).
Banner Stripping
CLI tools occasionally print version banners, update notices, or telemetry lines to stdout before the model response. ThestdoutBannerPatterns field on CliCommandSpec accepts an array of RegExp patterns that are stripped from stdout before text extraction:
Agent Interface
All CLI agents implement two methods.generate(options)
Runs the CLI synchronously and returns a GenerateTextResult:
- Extracts prompt from
options.prompt(string) oroptions.messages(array). - Builds the CLI command with all configured flags.
- Spawns the process and captures stdout/stderr.
- For
json/stream-jsonoutput, extracts text from the JSON payload. - Returns the result as a
GenerateTextResult.
stream(options)
Calls generate() internally and wraps the result as a StreamTextResult. Not truly streamed.
Message Handling
When called with messages, agents convert them to a text prompt:- System messages are extracted and prepended as a system prompt.
- User/assistant messages are formatted as
ROLE: content, joined with double newlines. - Message system prompt is combined with any
systemPrompton the agent instance.