Skip to main content
Smithers ships CLI-backed agent classes that wrap external AI command-line tools. Each agent implements the AI SDK Agent interface, so they work as drop-in replacements anywhere you would use an AI SDK agent — including in <Task> components. The agents spawn the corresponding CLI process, pass the prompt via arguments or stdin, capture the output, and return it in the standard GenerateTextResult format.

Import

import {
  ClaudeCodeAgent,
  CodexAgent,
  GeminiAgent,
  PiAgent,
  type PiAgentOptions,
  type PiExtensionUiRequest,
  type PiExtensionUiResponse,
} from "smithers-orchestrator/agents/cli";

Prerequisites

Each agent requires its corresponding CLI tool installed and available in the system PATH:
AgentCLI RequiredInstall
ClaudeCodeAgentclaudeClaude Code CLI
CodexAgentcodexOpenAI Codex CLI
GeminiAgentgeminiGemini CLI
PiAgentpiPI Coding Agent

Quick Start

import { ClaudeCodeAgent, CodexAgent, GeminiAgent, PiAgent } from "smithers-orchestrator/agents/cli";

const claude = new ClaudeCodeAgent({ model: "claude-sonnet-4-20250514" });
const codex = new CodexAgent({ model: "gpt-4.1" });
const gemini = new GeminiAgent({ model: "gemini-2.5-pro" });
const pi = new PiAgent({ provider: "openai", model: "gpt-5.2-codex" });
Use them in workflows like any other agent:
<Task id="analysis" output="analysis" agent={claude}>
  {`Analyze the codebase and identify potential improvements.`}
</Task>

Base Options

All CLI agent classes share a common set of base options:
type BaseCliAgentOptions = {
  id?: string;               // Agent ID (default: random UUID)
  model?: string;            // Model name to pass to the CLI
  systemPrompt?: string;     // System prompt prepended to the user prompt
  instructions?: string;     // Alias for systemPrompt
  cwd?: string;              // Working directory for the CLI process
  env?: Record<string, string>;  // Additional environment variables
  yolo?: boolean;            // Skip permission prompts (default: true)
  timeoutMs?: number;        // Process timeout in milliseconds
  maxOutputBytes?: number;   // Max output capture size
  extraArgs?: string[];      // Additional CLI arguments appended to the command
};
OptionDefaultDescription
idRandom UUIDUnique identifier for the agent instance
modelundefinedModel name passed to the CLI’s --model flag
systemPromptundefinedSystem-level instructions prepended to the prompt
instructionsundefinedAlias for systemPrompt
cwdTool context rootDir or process.cwd()Working directory for the spawned process
env{}Extra environment variables merged with process.env
yolotrueWhen true, configures the CLI to skip all interactive permission prompts
timeoutMsundefinedKill the process after this many milliseconds
maxOutputBytesundefinedTruncate captured output to this size
extraArgs[]Arbitrary additional CLI flags

ClaudeCodeAgent

Wraps the claude CLI with --print mode.
const claude = new ClaudeCodeAgent({
  model: "claude-sonnet-4-20250514",
  systemPrompt: "You are a careful code reviewer.",
  timeoutMs: 300_000,
});

Claude-Specific Options

type ClaudeCodeAgentOptions = BaseCliAgentOptions & {
  addDir?: string[];
  agent?: string;
  agents?: Record<string, { description?: string; prompt?: string }> | string;
  allowDangerouslySkipPermissions?: boolean;
  allowedTools?: string[];
  appendSystemPrompt?: string;
  betas?: string[];
  chrome?: boolean;
  continue?: boolean;
  dangerouslySkipPermissions?: boolean;
  debug?: boolean | string;
  debugFile?: string;
  disableSlashCommands?: boolean;
  disallowedTools?: string[];
  fallbackModel?: string;
  file?: string[];
  forkSession?: boolean;
  fromPr?: string;
  ide?: boolean;
  includePartialMessages?: boolean;
  inputFormat?: "text" | "stream-json";
  jsonSchema?: string;
  maxBudgetUsd?: number;
  mcpConfig?: string[];
  mcpDebug?: boolean;
  noChrome?: boolean;
  noSessionPersistence?: boolean;
  outputFormat?: "text" | "json" | "stream-json";
  permissionMode?: "acceptEdits" | "bypassPermissions" | "default" | "delegate" | "dontAsk" | "plan";
  pluginDir?: string[];
  replayUserMessages?: boolean;
  resume?: string;
  sessionId?: string;
  settingSources?: string;
  settings?: string;
  strictMcpConfig?: boolean;
  tools?: string[] | "default" | "";
  verbose?: boolean;
};
Key options:
OptionDescription
outputFormatOutput format from the CLI: "text", "json", or "stream-json" (default: "text")
permissionModePermission handling: "bypassPermissions", "acceptEdits", "default", "delegate", "dontAsk", "plan"
allowedToolsWhitelist of tool names the CLI may use
disallowedToolsBlacklist of tool names
maxBudgetUsdSpending cap in USD
mcpConfigMCP server configuration files
addDirAdditional directories to include in the context
Yolo mode behavior: When yolo is true (the default), the agent automatically passes --allow-dangerously-skip-permissions, --dangerously-skip-permissions, and --permission-mode bypassPermissions unless permissionMode is explicitly set.

CodexAgent

Wraps the codex CLI using codex exec with stdin input.
const codex = new CodexAgent({
  model: "gpt-4.1",
  sandbox: "workspace-write",
  fullAuto: true,
});

Codex-Specific Options

type CodexAgentOptions = BaseCliAgentOptions & {
  config?: Record<string, string | number | boolean | object | null> | string[];
  enable?: string[];
  disable?: string[];
  image?: string[];
  oss?: boolean;
  localProvider?: string;
  sandbox?: "read-only" | "workspace-write" | "danger-full-access";
  profile?: string;
  fullAuto?: boolean;
  dangerouslyBypassApprovalsAndSandbox?: boolean;
  cd?: string;
  skipGitRepoCheck?: boolean;
  addDir?: string[];
  outputSchema?: string;
  color?: "always" | "never" | "auto";
  json?: boolean;
  outputLastMessage?: string;
};
Key options:
OptionDescription
sandboxSandbox level: "read-only", "workspace-write", or "danger-full-access"
fullAutoEnable full auto mode (no confirmations)
configConfiguration overrides as key-value pairs or raw strings
ossUse open-source models
localProviderLocal model provider URL
outputLastMessageFile path to write the last message (auto-generated temp file if not set)
Yolo mode behavior: When yolo is true and fullAuto is not set, the agent passes --dangerously-bypass-approvals-and-sandbox. If fullAuto is true, it uses --full-auto instead. Input handling: The prompt (with system prompt prepended) is passed via stdin using the - argument.

GeminiAgent

Wraps the gemini CLI.
const gemini = new GeminiAgent({
  model: "gemini-2.5-pro",
  sandbox: true,
  allowedTools: ["read_file", "write_file"],
});

Gemini-Specific Options

type GeminiAgentOptions = BaseCliAgentOptions & {
  debug?: boolean;
  sandbox?: boolean;
  approvalMode?: "default" | "auto_edit" | "yolo" | "plan";
  experimentalAcp?: boolean;
  allowedMcpServerNames?: string[];
  allowedTools?: string[];
  extensions?: string[];
  listExtensions?: boolean;
  resume?: string;
  listSessions?: boolean;
  deleteSession?: string;
  includeDirectories?: string[];
  screenReader?: boolean;
  outputFormat?: "text" | "json" | "stream-json";
};
Key options:
OptionDescription
sandboxRun in sandbox mode
approvalModeApproval handling: "default", "auto_edit", "yolo", or "plan"
allowedToolsWhitelist of tool names
extensionsGemini CLI extensions to load
includeDirectoriesAdditional directories to include
outputFormatOutput format: "text", "json", or "stream-json" (default: "text")
Yolo mode behavior: When yolo is true and approvalMode is not set, the agent passes --yolo. Input handling: The prompt (with system prompt prepended) is passed via --prompt.

PiAgent

Wraps the pi CLI.
const pi = new PiAgent({
  provider: "openai",
  model: "gpt-5.2-codex",
  mode: "text",
  noSession: true,
});

PI-Specific Options

type PiAgentOptions = BaseCliAgentOptions & {
  provider?: string;
  model?: string;
  apiKey?: string;
  systemPrompt?: string;
  appendSystemPrompt?: string;
  mode?: "text" | "json" | "rpc";
  print?: boolean;
  continue?: boolean;
  resume?: boolean;
  session?: string;
  sessionDir?: string;
  noSession?: boolean;
  models?: string | string[];
  listModels?: boolean | string;
  tools?: string[];
  noTools?: boolean;
  extension?: string[];
  noExtensions?: boolean;
  skill?: string[];
  noSkills?: boolean;
  promptTemplate?: string[];
  noPromptTemplates?: boolean;
  theme?: string[];
  noThemes?: boolean;
  thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
  export?: string;
  files?: string[];
  verbose?: boolean;
  onExtensionUiRequest?: (request: PiExtensionUiRequest) =>
    | Promise<PiExtensionUiResponse | null>
    | PiExtensionUiResponse
    | null;
};
Key options:
OptionDescription
providerPI provider name passed to --provider
modelPI model passed to --model
apiKeyPassed to --api-key (visible in process listings; prefer PI env/config when possible)
modePI mode: text, json, or rpc
printForce print mode (--print) in text mode only
continue / resume / sessionSession continuation controls (--continue, --resume, --session)
sessionDirCustom session directory (--session-dir)
models / listModelsScoped model patterns and model listing (--models, --list-models)
extensionLoad extension path(s)
skillLoad skill path(s)
promptTemplateLoad prompt template path(s)
themeLoad theme path(s)
tools / noToolsEnable specific tools or disable built-ins
exportExport session HTML (--export)
filesFile args passed as @path (text/json modes only)
onExtensionUiRequestRPC-only handler for extension UI requests
noSessionDisable session persistence (defaults to true unless session flags are set)
Input handling: In text/json modes, the prompt is passed as a positional PI message argument and files are emitted as @path arguments. In rpc mode, the prompt is sent as a JSON prompt command over stdin (file args are not supported). Text mode defaults to --print and omits --mode, while json/rpc modes set --mode and do not pass --print.

Agent Interface

All CLI agents implement the AI SDK Agent interface with two methods:

generate(options)

Runs the CLI synchronously and returns a GenerateTextResult:
const result = await claude.generate({
  prompt: "Explain the architecture of this codebase.",
});
console.log(result.text);
The method:
  1. Extracts the prompt from options.prompt (string) or options.messages (array).
  2. Builds the CLI command with all configured flags.
  3. Spawns the process and captures stdout/stderr.
  4. If the output format is json or stream-json, extracts text from the JSON payload.
  5. Returns the result wrapped in a GenerateTextResult.

stream(options)

Calls generate() internally and wraps the result as a StreamTextResult. This is a convenience adapter — the actual CLI execution is not truly streamed.
const stream = await claude.stream({
  prompt: "Review this code.",
});
for await (const chunk of stream.textStream) {
  process.stdout.write(chunk);
}

Message Handling

When called with messages (as Smithers does internally), the 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 and joined with double newlines.
  • The system prompt from messages is combined with any systemPrompt configured on the agent instance.

Example: Multi-Agent Workflow

import { ClaudeCodeAgent, CodexAgent } from "smithers-orchestrator/agents/cli";

const reviewer = new ClaudeCodeAgent({
  model: "claude-sonnet-4-20250514",
  systemPrompt: "You are a thorough code reviewer.",
  timeoutMs: 120_000,
});

const fixer = new CodexAgent({
  model: "gpt-4.1",
  fullAuto: true,
  timeoutMs: 180_000,
});

export default smithers((ctx) => (
  <Workflow name="review-and-fix">
    <Task id="review" output="review" agent={reviewer}>
      {`Review the changes in this PR and identify issues.`}
    </Task>
    <Task id="fix" output="fix" agent={fixer}>
      {`Fix these issues: ${ctx.output("review", { nodeId: "review" }).summary}`}
    </Task>
  </Workflow>
));