npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agentslist

v0.3.1

Published

Stable abstraction layer over CLI coding agents (Claude Code, Codex, Gemini CLI, OpenCode)

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-db

Requires 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 summary

Returns: 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);    // number

Parser

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 stdout

Three 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 string

Protocol 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.