Package Exports
- agentslist
- agentslist/model-helpers
- agentslist/resolver-helpers
- agentslist/types
Readme
agentslist
Stable abstraction layer over CLI coding agents (Claude Code, Codex CLI, Gemini CLI, OpenCode). Detects installed agents, resolves capabilities, executes prompts, and normalizes output — all through a single, agent-agnostic API.
Install
npm install agentslist agentslist-dbRequires Node.js >= 18. The agentslist-db package provides the compatibility database consumed at runtime.
Quick Start
import { run } from "agentslist";
const result = await run({
prompt: "Refactor this function to use async/await",
working_dir: "/path/to/project",
output: { format: "json" },
});
console.log(result.status); // "success"
console.log(result.output.text); // Agent's text response
console.log(result.output.json); // Parsed JSON (when format is "json")API
detect(options?): Promise<DetectedAgent[]>
Find all installed CLI agents by running detection commands from the database.
import { detect } from "agentslist";
const agents = await detect();
// [
// {
// id: "claude-code",
// binary: "claude",
// version: "2.1.7",
// path: "/usr/local/bin/claude",
// env_authenticated: true
// },
// { id: "gemini-cli", binary: "gemini", version: "0.27.2", ... }
// ]Options:
| Field | Type | Description |
|---|---|---|
db_path |
string |
Custom path to the DB directory. Defaults to bundled database. |
Returns: DetectedAgent[] — sorted by id. Empty array if no agents are found.
plan(request, options?): Promise<ExecutionPlan>
Resolve an execution request into a concrete execution plan without running it. Useful for previewing what CLI command would be generated.
import { plan } from "agentslist";
const p = await plan({
prompt: "Add tests for the auth module",
model: "claude-sonnet-4-5-20250929",
output: { format: "json" },
max_turns: 5,
});
console.log(p.binary); // "claude"
console.log(p.args); // ["--print", "--output-format", "json", "--model", ...]
console.log(p.env); // { ... }
console.log(p.compatibility_report.overall); // "full"Options:
| Field | Type | Description |
|---|---|---|
agent |
string |
Force a specific agent by id ("claude-code", "codex", "gemini-cli", "opencode"). Auto-selects first detected agent if omitted. |
db_path |
string |
Custom database path. |
Returns: ExecutionPlan
interface ExecutionPlan {
agent_id: string;
binary: string;
args: string[];
env: Record<string, string>;
stdin?: string;
output_parser: string; // "plain_text" | "json_object" | "ndjson_stream"
compatibility_report: CompatibilityReport;
model_resolution?: ModelResolution; // Present when request.model is specified
protocol?: {
type: ProtocolType;
features: string[];
};
}When request.model is specified, the resolver validates the token against the agent's model bindings, resolves aliases (e.g. "opus" to "claude-opus-4-6"), and applies fallback chains for blocked models. The result is captured in model_resolution.
run(request, options?): Promise<ExecutionResult>
Execute the full pipeline end-to-end: detect agent, resolve plan, spawn process, parse output.
import { run } from "agentslist";
const result = await run({
prompt: "Write a hello world in Rust",
working_dir: process.cwd(),
timeout_seconds: 60,
});
if (result.status === "success") {
console.log(result.output.text);
}Options:
| Field | Type | Description |
|---|---|---|
agent |
string |
Force a specific agent. |
db_path |
string |
Custom database path. |
timeout_ms |
number |
Override timeout in milliseconds. Overrides request.timeout_seconds. |
Returns: ExecutionResult
interface ExecutionResult {
agent: { id: string; version: string };
status: "success" | "error" | "timeout" | "budget_exceeded" | "turn_limit";
exit_code: number;
output: {
text?: string; // Extracted text (via json_response_path or plain stdout)
json?: unknown; // Parsed JSON object (when output format is json)
raw: string; // Raw stdout
};
compatibility: CompatibilityReport;
duration_ms: number;
resolved_command: {
binary: string;
args: string[];
env: Record<string, string>;
};
}check(request, options?): Promise<CompatibilityReport>
Check whether an agent supports the requested capabilities without executing anything.
import { check, summarize } from "agentslist";
const report = await check({
prompt: "...",
output: { format: "json", schema: { type: "object" } },
session: { resume_id: "abc-123" },
});
console.log(report.overall); // "full" | "degraded" | "unsupported" | "unknown"
console.log(summarize(report)); // Human-readable summaryReturns: CompatibilityReport
interface CompatibilityReport {
overall: "full" | "degraded" | "unsupported" | "unknown";
details: CapabilityDetail[];
}
interface CapabilityDetail {
capability: CapabilityId; // e.g. "output.json", "session.resume"
status: "supported" | "degraded" | "unsupported" | "unknown";
message?: string; // Explanation for non-supported statuses
fallback_used?: boolean;
}spawn(request, options?): Promise<AgentSession>
Start an interactive session with streaming events and follow-up messaging.
import { spawn } from "agentslist";
const session = await spawn(
{ prompt: "Review this codebase for security issues" },
{ agent: "claude-code", cwd: "/path/to/project" },
);
for await (const event of session.events) {
switch (event.type) {
case "message":
process.stdout.write(event.content);
break;
case "tool_call":
console.log(`Tool: ${event.name}`, event.input);
break;
case "approval_request":
await session.respond(event.id, { behavior: "allow" });
break;
case "cost_update":
console.log(`Cost: $${event.cost_usd.toFixed(4)}`);
break;
case "error":
console.error(event.message);
break;
}
}
const result = await session.wait();SpawnOptions:
| Field | Type | Description |
|---|---|---|
agent |
string |
Force a specific agent. |
db_path |
string |
Custom database path. |
cwd |
string |
Working directory for the spawned process. |
env |
Record<string, string> |
Additional environment variables. |
cancel_grace_ms |
number |
Grace period before SIGKILL after cancel. Default: 5000. |
AgentSession interface:
| Member | Type | Description |
|---|---|---|
events |
AsyncIterable<AgentEvent> |
Normalized event stream. |
send(message) |
Promise<void> |
Send a follow-up message. |
respond(requestId, response) |
Promise<void> |
Respond to an approval request. |
cancel() |
Promise<void> |
Gracefully cancel (SIGTERM, then SIGKILL after grace period). |
wait() |
Promise<ExecutionResult> |
Wait for process exit. |
sessionId |
string | null |
Protocol-provided session ID (readonly). |
pid |
number | undefined |
Child process PID (readonly). |
AgentEvent types:
| Type | Key Fields | Description |
|---|---|---|
message |
content, partial |
Text output from the agent. |
thought |
content |
Internal reasoning (when exposed). |
tool_call |
id, name, input |
Agent invoked a tool. |
tool_result |
id, output |
Tool returned a result. |
approval_request |
id, tool_name, input |
Agent requests permission. Call session.respond() to allow/deny. |
session_start |
session_id |
Session ID assigned by the agent. |
file_change |
path, diff? |
A file was modified. |
plan |
steps |
Agent's execution plan. |
context_usage |
tokens_used, tokens_limit? |
Token usage update. |
cost_update |
cost_usd, tokens_in, tokens_out |
Cost tracking data. |
done |
result? |
Agent finished. |
error |
message |
An error occurred. |
raw |
data |
Unparsed protocol data. |
listModels(options?): Promise<ModelListResult>
List all models available for a specific agent and version, grouped by status.
import { listModels } from "agentslist";
const models = await listModels({ agent: "claude-code" });
console.log(models.agent_id); // "claude-code"
console.log(models.default_model); // "claude-sonnet-4-5-20250929"
console.log(models.selection_mode); // "allowlist" | "passthrough"
console.log(models.supported); // ModelBindingWithCatalog[]
console.log(models.deprecated); // ModelBindingWithCatalog[]Options:
| Field | Type | Description |
|---|---|---|
agent |
string |
Agent id. Auto-selects first detected agent if omitted. |
version |
string |
Override agent version. Defaults to detected version. |
db_path |
string |
Custom database path. |
Returns: ModelListResult
interface ModelListResult {
agent_id: string;
version: string;
default_model?: string;
selection_mode: "allowlist" | "passthrough";
unknown_model_behavior: "passthrough" | "error" | "unknown";
supported: ModelBindingWithCatalog[];
preview: ModelBindingWithCatalog[];
deprecated: ModelBindingWithCatalog[];
blocked: ModelBindingWithCatalog[];
}
interface ModelBindingWithCatalog {
binding: ModelBinding;
catalog?: ModelCatalogEntry; // Catalog metadata (provider, family, lifecycle)
}checkModel(token, options?): Promise<ModelCheckResult>
Check if a model token is compatible with a specific agent and version. Resolves aliases and reports status.
import { checkModel } from "agentslist";
const result = await checkModel("opus", { agent: "claude-code" });
console.log(result.resolution.status); // "matched"
console.log(result.resolution.resolved_model_id); // "claude-opus-4-6"
console.log(result.summary); // 'Model "claude-opus-4-6" is supported'
// Deprecated model
const dep = await checkModel("claude-3-5-haiku-20241022", { agent: "claude-code" });
console.log(dep.resolution.status); // "matched_deprecated"
console.log(dep.resolution.warnings); // [{ type: "deprecated", message: "..." }]
console.log(dep.resolution.suggestions); // ["claude-sonnet-4-5-20250929", ...]Options:
| Field | Type | Description |
|---|---|---|
agent |
string |
Agent id. Auto-selects first detected agent if omitted. |
version |
string |
Override agent version. |
db_path |
string |
Custom database path. |
Returns: ModelCheckResult
interface ModelCheckResult {
resolution: ModelResolution;
summary: string; // Human-readable description
}
interface ModelResolution {
status: "matched" | "matched_deprecated" | "matched_preview"
| "fallback" | "passthrough" | "blocked" | "no_model";
requested_token?: string;
resolved_model_id?: string;
effective_token?: string; // The token passed to --model flag
default_model?: string; // Agent's default model for reference
warnings: ModelWarning[];
suggestions: string[]; // Alternative model IDs
}
interface ModelWarning {
type: "deprecated" | "preview" | "blocked" | "unknown_token" | "fallback" | "lifecycle";
message: string;
}findAgentsForModel(modelId, options?): Promise<CrossAgentModelInfo[]>
Find which agents support a given canonical model ID.
import { findAgentsForModel } from "agentslist";
const agents = await findAgentsForModel("claude-opus-4-6");
// [
// {
// agent_id: "claude-code",
// agent_name: "Claude Code",
// status: "supported",
// accepted_tokens: ["opus", "claude-opus-4-6"],
// is_default: false
// }
// ]Options:
| Field | Type | Description |
|---|---|---|
db_path |
string |
Custom database path. |
Returns: CrossAgentModelInfo[]
interface CrossAgentModelInfo {
agent_id: string;
agent_name: string;
status: "supported" | "deprecated" | "blocked" | "preview";
accepted_tokens: string[];
is_default: boolean;
}ExecutionRequest
The full request shape accepted by run(), plan(), check(), and spawn():
interface ExecutionRequest {
// --- Prompt ---
prompt: string; // The main prompt text
prompt_file?: string; // Read prompt from file instead
piped_input?: string; // Data piped to stdin
system_prompt?: {
strategy?: "replace" | "append"; // Replace or append to default system prompt
content?: string; // Inline system prompt
file?: string; // System prompt from file
append_file?: string; // Additional system prompt file (appended)
};
// --- Media ---
images?: string[]; // Image file paths
file_attach?: string[]; // Files to attach
// --- Context ---
context?: {
include_files?: string[]; // Specific files to include
include_globs?: string[]; // Glob patterns to include
exclude_globs?: string[]; // Glob patterns to exclude
};
// --- Output ---
output?: {
format?: "text" | "json" | "stream_json";
schema?: Record<string, unknown>; // JSON Schema for structured output
file?: string; // Write output to file
diff?: boolean; // Show diffs instead of full files
markdown_render?: boolean; // Render markdown in output
};
// --- Model ---
model?: string; // Model identifier
fallback_model?: string; // Fallback if primary unavailable
reasoning_effort?: "off" | "low" | "medium" | "high" | "max";
// --- Execution Control ---
sandbox?: "none" | "read_only" | "workspace_write" | "full_access";
approval_bypass?: boolean; // Skip all approval prompts
approval_mode?: "default" | "auto_edit" | "plan" | "full_bypass";
max_turns?: number;
max_budget_usd?: number;
timeout_seconds?: number;
working_dir?: string;
additional_dirs?: string[];
debug?: boolean;
quiet?: boolean;
dry_run?: boolean;
plan_mode?: boolean;
config_file?: string;
// --- Environment ---
env?: Record<string, string>; // Extra environment variables
provider?: {
api_base?: string;
api_key?: string; // Passed via env, never in CLI args
name?: string;
};
// --- Session ---
session?: {
resume_id?: string; // Resume a previous session
continue_last?: boolean; // Continue the most recent session
session_id?: string; // Explicit session ID
fork?: boolean; // Fork from an existing session
list?: boolean; // List available sessions
export_path?: string; // Export session to file
import_path?: string; // Import session from file
};
// --- Tools ---
tools?: {
allowed?: string[]; // Restrict to these tools
disallowed?: string[]; // Block these tools
};
mcp?: {
config_path?: string; // MCP server config file
strict?: boolean;
};
}Lower-Level API
The high-level functions (detect, plan, run, check, spawn) compose these building blocks. You can use them directly for advanced scenarios.
Detector
import { detectAll, detectAgent, isAvailable } from "agentslist";
const agents = await detectAll(db); // All installed agents
const claude = await detectAgent("claude-code", db); // Specific agent or null
const hasClaude = await isAvailable("claude-code", db);Resolver
Pure function — no I/O. Maps ExecutionRequest + DetectedAgent + AgentDB to ExecutionPlan.
import { resolve } from "agentslist";
import type { ResolveOptions } from "agentslist";
const plan = resolve(request, agent, db);
// Protocol mode: use the agent's streaming protocol flags instead of oneshot flags
const plan = resolve(request, agent, db, { mode: "protocol" });Executor
Spawns the CLI process and collects raw output.
import { execute } from "agentslist";
const raw = await execute(plan, {
cwd: "/path/to/project",
timeout_ms: 30_000,
});
console.log(raw.stdout);
console.log(raw.stderr);
console.log(raw.exit_code); // number
console.log(raw.timed_out); // boolean
console.log(raw.duration_ms); // numberParser
Normalizes raw CLI output into ParsedOutput.
import { parse } from "agentslist";
const output = parse(rawOutput, plan, versionRange?.output_parsing);
console.log(output.text); // Extracted text
console.log(output.json); // Parsed JSON (if applicable)
console.log(output.raw); // Raw stdoutThree parsing strategies are selected automatically based on the execution plan:
| Strategy | When | Behavior |
|---|---|---|
plain_text |
Default / text output | Trims stdout |
json_object |
output.format: "json" |
Parses JSON, extracts text via json_response_path |
ndjson_stream |
output.format: "stream_json" |
Parses newline-delimited JSON events, finds the final event |
Reporter
import { preflightReport, isFullySupported, summarize } from "agentslist";
const report = preflightReport(request, agent, db);
const ok = isFullySupported(request, agent, db); // boolean
const text = summarize(report); // Human-readable stringProtocol Parsers
Normalize agent-specific streaming protocols into AgentEvent:
import {
getProtocolParser,
createClaudeSdkParser,
createCodexJsonrpcParser,
createAcpParser,
createPlainTextParser,
} from "agentslist";| Protocol | Agent | Description |
|---|---|---|
claude_sdk |
Claude Code | Structured JSON events |
codex_app_server |
Codex CLI | JSON-RPC 2.0 over stdio |
codex_jsonrpc |
Codex CLI | Legacy JSON-RPC |
acp |
— | Agent Communication Protocol |
plain_text |
Fallback | Raw line-by-line stdout |
Resolver Helpers (Browser-Safe)
Exported as raw .ts — no Node.js dependencies. Safe for browser/playground use.
import {
resolveCapability,
computeOverallCompatibility,
capabilityIdToDbKey,
dbKeyToCapabilityId,
pushFlagValue,
pushBooleanFlag,
pushMultiValues,
} from "agentslist/resolver-helpers";Model Helpers (Browser-Safe)
Pure functions for model resolution. Exported as raw .ts — no Node.js dependencies.
import {
resolveModel,
resolveModelToken,
applyModelFallback,
findBindingByToken,
findCatalogEntry,
generateModelWarnings,
listModelBindings,
findAgentsForModel,
summarizeModelResolution,
} from "agentslist/model-helpers";| Function | Description |
|---|---|
resolveModel(token, fallback, models, catalog) |
Full resolution pipeline with fallback chain. |
resolveModelToken(token, models, catalog) |
Resolve a single token against bindings. |
applyModelFallback(resolution, fallback, models, catalog) |
Apply fallback when primary is blocked. |
findBindingByToken(token, bindings) |
Find a binding that accepts a given token. |
findCatalogEntry(modelId, catalog) |
Look up a model in the catalog by ID. |
generateModelWarnings(binding, catalog, token, models) |
Generate warnings for a binding. |
listModelBindings(models, catalog) |
Group bindings by status with catalog info. |
findAgentsForModel(modelId, agentModelsMap) |
Cross-agent lookup (sync, takes a Map). |
summarizeModelResolution(resolution) |
Human-readable summary string. |
Errors
All errors extend AgentslistError:
| Error | When |
|---|---|
AgentslistError |
Base class for all library errors. |
AgentNotFoundError |
Agent id not in the database. Properties: agentId. |
AgentNotInstalledError |
Agent binary not found on the system. Properties: agentId, binary. |
VersionNotMatchedError |
Detected version has no matching range in the DB. Properties: agentId, version. |
DBLoadError |
Failed to load the compatibility database. Properties: path. |
ExecutionError |
Process spawn or execution failure. Properties: exitCode?, stderr?. |
ParseError |
Output parsing failed. Properties: rawOutput?. |
import { run, AgentNotInstalledError, ExecutionError } from "agentslist";
try {
await run({ prompt: "...", working_dir: "." }, { agent: "codex" });
} catch (err) {
if (err instanceof AgentNotInstalledError) {
console.error(`${err.binary} is not installed`);
} else if (err instanceof ExecutionError) {
console.error(`Execution failed (exit ${err.exitCode}): ${err.stderr}`);
}
}Export Paths
// Main API — all functions, classes, types
import { detect, plan, run, check, spawn, AgentDB } from "agentslist";
// Model API
import { listModels, checkModel, findAgentsForModel } from "agentslist";
// Browser-safe helpers — no Node.js dependencies
import { resolveCapability, computeOverallCompatibility } from "agentslist/resolver-helpers";
import { resolveModel, resolveModelToken, listModelBindings } from "agentslist/model-helpers";
// Types only
import type {
ExecutionRequest, ExecutionPlan, ExecutionResult, AgentEvent,
ModelResolution, ModelCatalogEntry, ModelBinding, ModelCompatRange,
} from "agentslist/types";License
Apache License 2.0. See the LICENSE file for details.