12 KiB
ai-cli-dispatch
Dispatch AI CLI coding tasks to available clients (Codex, Claude Code, OpenCode) with automatic discovery, version checking, and execution.
Scope
- discover installed AI CLI clients on the host
- report client versions and availability
- dispatch a prompt to a specific client by name
- auto-resolve the best client from prompt keywords
- forward arguments natively to each client
- run tasks asynchronously as background jobs with lifecycle management
- run tasks synchronously when blocking until completion is desired
The tool supports both async (default) and sync execution modes. Async jobs run as detached background processes and are tracked on disk. For ACP-based harnesses, see docs/openclaw-acp-orchestration.md.
Setup
From the repo or installed skill directory:
cd tools/ai-cli-dispatch
npm install
The dispatcher itself requires only Node.js 20+ and npm. The actual AI CLI clients (codex, claude, opencode) are discovered from the host PATH; they are not bundled.
Commands
ai-cli-dispatch list [--json|--text]
ai-cli-dispatch run --client <client> --prompt <prompt> [--sync] [--timeout <ms>] [--debug] [--json|--text]
ai-cli-dispatch dispatch <prompt> [--client <client>] [--sync] [--timeout <ms>] [--debug] [--json|--text]
ai-cli-dispatch start --client <client> --prompt <prompt> [--timeout <ms>] [--debug] [--json|--text]
ai-cli-dispatch status <job-id> [--json|--text]
ai-cli-dispatch results <job-id> [--json|--text]
ai-cli-dispatch cancel <job-id> [--json|--text]
ai-cli-dispatch list-jobs [--status running|completed|failed] [--json|--text]
ai-cli-dispatch cleanup-jobs [--max-age <number>[h|m|s|d]] [--json|--text]
ai-cli-dispatch --help
list
Discover and report all supported clients.
ai-cli-dispatch list --json
Example JSON output:
[
{
"name": "codex",
"path": "/usr/local/bin/codex",
"version": "1.2.3",
"found": true
},
{
"name": "claude",
"found": false
},
{
"name": "opencode",
"path": "/opt/homebrew/bin/opencode",
"version": "0.5.1",
"found": true
}
]
Use --text for human-readable checkmarks:
ai-cli-dispatch list --text
run
Execute a prompt directly through a named client. By default, this starts a background job and returns immediately.
# Async (default) — returns a job ID
ai-cli-dispatch run --client codex --prompt "refactor this function"
# Sync — blocks until the client finishes
ai-cli-dispatch run --client claude --prompt "add tests for auth middleware" --sync
# With custom timeout and debug diagnostics
ai-cli-dispatch run --client opencode --prompt "migrate to ESM" --timeout 600000 --debug
The prompt is forwarded with each client’s native argument shape:
| Client | Arguments passed |
|---|---|
codex |
exec "<prompt>" |
claude |
-p "<prompt>" |
opencode |
"<prompt>" |
dispatch
Auto-resolve the client from prompt keywords, then execute. Defaults to async; use --sync to block.
ai-cli-dispatch dispatch "use claude to write tests"
ai-cli-dispatch dispatch "codex refactor auth module"
ai-cli-dispatch dispatch "opencode migrate to ESM"
Keyword matching is case-insensitive and ordered:
--clientflag (highest precedence)"open code"(spaced variant) →opencode"claude"→claude"codex"→codex"opencode"→opencodedefaultClientfrom config (lowest precedence)
Override auto-resolution explicitly:
ai-cli-dispatch dispatch "fix the bug" --client claude
start
Explicitly start a background job (same as run without --sync). Useful when you want the async behavior unambiguously.
ai-cli-dispatch start --client codex --prompt "refactor this function"
status
Check the status of a background job.
ai-cli-dispatch status <job-id>
JSON output:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client": "codex",
"prompt": "refactor this function",
"status": "running",
"startedAt": "2026-05-19T12:34:56.789Z",
"pid": 12345
}
Statuses: running, completed, failed, timed_out, cancelled.
results
Retrieve the execution result of a completed job.
ai-cli-dispatch results <job-id>
JSON output:
{
"stdout": "...",
"stderr": "...",
"exitCode": 0,
"client": "codex",
"durationMs": 45231
}
Requires status completed. For failed or timed_out jobs, use status to see the captured error.
cancel
Cancel a running job.
ai-cli-dispatch cancel <job-id>
list-jobs
List all tracked jobs, newest first.
ai-cli-dispatch list-jobs --json
ai-cli-dispatch list-jobs --status running --json
cleanup-jobs
Remove job files older than a threshold. Default unit is hours.
ai-cli-dispatch cleanup-jobs --max-age 24h
ai-cli-dispatch cleanup-jobs --max-age 30m
Async vs Sync Mode
By default, run and dispatch are async: they start a detached background process, persist a job record to disk, and return a job ID immediately. This is ideal for:
- Fire-and-forget tasks that may run for minutes
- Long-running codegen or migration tasks
- Scenarios where the caller should not block
Use --sync when you need:
- The complete output before the next step
- Synchronous composition in shell pipelines or scripts
- Immediate error propagation to the calling process
| Aspect | Async (default) | Sync (--sync) |
|---|---|---|
| Return value | Job ID + status | Full stdout/stderr + exit code |
| Process model | Detached child, parent exits immediately | Attached child, parent waits |
| Persistence | Job file written to disk | No job file |
| Timeout | Enforced via child.kill() after --timeout |
Enforced via child.kill() after --timeout |
Client Discovery
Discovery searches PATH in this order for each client name:
codex— OpenAI Codex CLIclaude— Anthropic Claude Codeopencode— OpenCode CLI
The search uses which (or where on Windows) first, then falls back to a manual PATH directory scan. If a binary is found, --version is invoked to extract a semver string.
Configuration
Optional config file:
~/.openclaw/ai-cli-dispatch.json
Example:
{
"paths": {
"codex": "/usr/local/bin/codex",
"claude": "/opt/homebrew/bin/claude"
},
"defaultClient": "claude",
"timeout": 300000
}
Resolution priority for paths and default client (highest to lowest):
- CLI flag (
--client,--codex-path, etc.) - Environment variable (
AI_CLI_CODEX_PATH,AI_CLI_DEFAULT_CLIENT, etc.) - Config file (
paths,defaultClient) which/wherediscovery
Supported env vars:
| Variable | Purpose |
|---|---|
AI_CLI_CODEX_PATH |
Override codex binary path |
AI_CLI_CLAUDE_PATH |
Override claude binary path |
AI_CLI_OPENCODE_PATH |
Override opencode binary path |
AI_CLI_DEFAULT_CLIENT |
Override default client (codex, claude, or opencode) |
Output Model
Default output is JSON. Use --text to stream raw stdout/stderr directly.
Sync JSON success shape (run --sync, dispatch --sync)
{
"stdout": "...",
"stderr": "...",
"exitCode": 0,
"client": "codex",
"durationMs": 45231
}
Async JSON success shape (run, dispatch, start)
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client": "codex",
"status": "running"
}
Job status shape (status)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client": "codex",
"prompt": "refactor this function",
"status": "running",
"startedAt": "2026-05-19T12:34:56.789Z",
"pid": 12345
}
Job result shape (results)
{
"stdout": "...",
"stderr": "...",
"exitCode": 0,
"client": "codex",
"durationMs": 45231
}
JSON error shape
{
"error": "..."
}
Exit codes:
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Missing/unknown command, missing argument, unknown client, resolution failure, execution error, or job lifecycle error |
Error Handling Guidance
Client "<name>" not found or not installed
Meaning: the requested client binary is not on PATH and not overridden by config.
Actions:
- Confirm the client is installed (
codex --version,claude --version, etc.) - Check that its directory is on
PATH - Or override the path in
~/.openclaw/ai-cli-dispatch.json
Prompt cannot be empty
Meaning: the prompt string was empty or whitespace-only.
Action: supply a non-empty --prompt or positional prompt argument.
Execution timed out after 600000ms
Meaning: the client subprocess did not finish within the timeout.
Action: the client may be waiting for interactive input or the task is too large. Break the prompt into smaller pieces, increase --timeout, or run the client directly to diagnose. Async jobs that time out are recorded with status timed_out.
Could not resolve client from prompt
Meaning: dispatch found no matching keyword and no defaultClient is configured.
Action: include a client name in the prompt (e.g., "use claude to ...") or set defaultClient in config.
Job "<job-id>" not found
Meaning: the requested job ID does not exist in the job store.
Action: verify the job ID. Job files are stored under ~/.openclaw/ai-cli-dispatch/jobs/. If the directory was cleaned or the host restarted, old jobs may have been removed.
Job "<job-id>" result is not available (status: <status>)
Meaning: results was called on a job that has not finished (running) or finished unsuccessfully (failed, timed_out, cancelled).
Action: poll status until the job reaches completed, or inspect status output for the error field.
Job Lifecycle Workflows
Fire-and-forget
JOB=$(ai-cli-dispatch run --client codex --prompt "refactor auth" --json | jq -r '.jobId')
# caller continues immediately
Poll until completion
JOB=$(ai-cli-dispatch start --client claude --prompt "write tests" --json | jq -r '.jobId')
while [ "$(ai-cli-dispatch status "$JOB" --json | jq -r '.status')" = "running" ]; do
sleep 5
done
ai-cli-dispatch results "$JOB" --json
Sync one-shot
ai-cli-dispatch run --client opencode --prompt "fix lint" --sync --text
Batch cleanup
ai-cli-dispatch cleanup-jobs --max-age 24h
Common Flows
Check what is installed
ai-cli-dispatch list --json
Run a quick task through a specific client (async)
ai-cli-dispatch run --client codex --prompt "fix lint errors in src/app.ts"
Run a quick task synchronously
ai-cli-dispatch run --client codex --prompt "fix lint errors in src/app.ts" --sync
Let the tool pick the client from the prompt
ai-cli-dispatch dispatch "claude: add unit tests for utils.ts"
Force a client when the prompt is ambiguous
ai-cli-dispatch dispatch "review this PR" --client claude
Coexistence with ACP
ai-cli-dispatch is a direct subprocess dispatcher. It is not an ACP agent and does not participate in ACP orchestration.
- Use
ai-cli-dispatchwhen you need a quick, local, one-shot CLI execution or a background job. - Use ACP (
docs/openclaw-acp-orchestration.md) when you need session-bound coding harnesses with thread context, multi-turn review, or orchestrator-managed verification gates.
Implementation Notes
- The dispatcher is TypeScript/Node.js with a single external dependency (
minimist). - Client arguments are hardcoded per tool to match each client’s stable CLI contract.
- The default timeout is 10 minutes (
600_000ms); override with--timeoutor config. - On Windows, discovery uses
whereinstead ofwhichand.exeextensions are assumed. - Async jobs run as detached processes with
stdio: ["ignore", "pipe", "pipe"]so the dispatcher can exit without waiting. - Job files are written atomically to
~/.openclaw/ai-cli-dispatch/jobs/<jobId>.json.