Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@renxqoo/agent-cli-sdk

English · 中文 · Website

A skill factory for agent-facing CLIs. Install one skill, let your AI agent turn any company API into a CLI and an agent skill — auth, unified output, typed errors and progressive disclosure included. The author writes only "which endpoint to call, how to map fields".

License: MIT Node CI


What it is

agent-cli-sdk lets you expose a company API to humans and AI agents from a single declaration. It is an SDK (you build a CLI with it), and it is a skill factory: the package ships an agent-cli-builder skill, so an AI agent can generate the whole CLI for you from an API description.

One defineCommand produces three synchronized artifacts:

defineCommand(name / description / zod / run)
        │
        ├── CLI          humans + unix pipes (acme orders list | jq)
        ├── SKILL.md     progressive disclosure (generated by `skills gen`)
        └── agent dirs   synced by `skills sync` (~/.claude, ~/.codex, …)

One source of truth. No drift between the command, the docs, and what the agent reads.


How an agent generates your CLI

The fastest path — you don't even write the code:

# 1. Add the SDK to a new business package
npm install @renxqoo/agent-cli-sdk
# 2. Install the bundled skill into your agent (the package ships it, but does not auto-install it)
"Install this skill for me: https://github.com/renxqoo/agent-cli-sdk/tree/main/skills/agent-cli-builder"
# 3. Give the API to your agent
"Use the agent-cli-builder skill to wrap https://api.acme.com's /orders endpoints
 into a CLI named 'acme'. List, get, update orders. OAuth device flow."

The agent creates src/commands/*.ts and src/index.ts, following the SDK contract (Zod schemas, ctx.get, typed errors, errs.*). Then:

# 4. Build, generate the skill, sync it to every agent directory
acme skills gen acme --init   # SKILL.md with an auto-generated command table
acme skills sync              # ~/.agents + detected ~/.claude/.codex/.cursor/...

Done. Teammates run acme orders list; their agents discover acme from the skill description and call it identically. The generated code is buildable and testable, not a sample snippet — the skill enforces the SDK's invariants during generation.


Installation

Requires Node.js >= 20. This package is ESM-only (import / dynamic import(); CommonJS require() is not supported).

npm install @renxqoo/agent-cli-sdk
# or
pnpm add @renxqoo/agent-cli-sdk

Quick start

A complete single-command CLI in under 30 lines (no-auth, public data):

#!/usr/bin/env node
import { defineCli, defineCommand } from "@renxqoo/agent-cli-sdk";
import * as z from "zod";
import { realpathSync } from "node:fs";
import { fileURLToPath } from "node:url";

const app = defineCli({
  name: "myapp",
  description: "My data CLI",
  baseUrl: "https://api.example.com",
  commands: {
    list: defineCommand({
      name: "list",
      description: "Query list",
      args: {
        schema: z.object({
          limit: z.coerce.number().min(1).max(100).default(20),
        }),
      },
      async run(ctx, args) {
        const res = await ctx.get<{ items: Array<{ id: string; title: string }> }>("/items", {
          limit: args.limit,
        });
        return { data: res.data.items, meta: { count: res.data.items.length } };
      },
    }),
  },
});

function isMainEntry(): boolean {
  try {
    return realpathSync(process.argv[1] ?? "") === fileURLToPath(import.meta.url);
  } catch {
    return false;
  }
}
if (isMainEntry()) app.run(process.argv.slice(2));
export default app;

Add OAuth in one line:

import { defineCliApp, defineAuth } from "@renxqoo/agent-cli-sdk";
import { homedir } from "node:os";
import { join } from "node:path";

export default await defineCliApp({
  name: "orders",
  // One app-owned root; the assembler hands it to every stateful plugin via apply(services).
  dir: join(homedir(), ".orders"),
  plugins: [
    defineAuth({
      credentialNamespace: "orders", // → config/orders.json + credentials/orders.json
      baseUrl: "https://auth.example.com",
      scope: "orders.read offline_access", // business-defined, no default
    }),
  ],
  commands: {},
});
// → orders auth login / status / logout / register are auto-injected

Core API

defineCli(options) — assemble a CLI

defineCli({
  name: 'orders',                  // required: namespace
  description: '...',              // required
  plugins: [authPlugin],           // optional: plugins (auth/logging/audit…)
  commands: { list, get },         // required: top-level commands → orders list
  namespaces: { orders: {...} },   // optional: sub-namespaces → orders orders list
  baseUrl: 'https://api.x.com',    // optional: backend address for ctx.get/post/…
  errorOnStatus: { 404: 'not_found', '5xx': 'server_error' },  // optional
  defaultFormat: 'auto',           // optional: 'auto' (default) | 'json' | 'human'
  skillsDir: './skills',           // optional: enable the built-in skills commands
  skillsTargets: [...],            // optional: sync targets (omit = default agent dirs)
})

defineCommand(spec) — declare a command

import * as z from "zod";

defineCommand({
  name: "get",
  description: "Query a single order",
  args: {
    schema: z.object({
      id: z.string().min(1).describe("Order ID"),
      verbose: z.boolean().describe("Verbose output").default(false),
    }),
    pos: ["id"], // `id` is a positional operand, not a same-name flag
  },
  humanFormat: (data) => `Order: ${data.id}`, // optional: custom --no-json text
  async run(ctx, args) {
    const res = await ctx.get(`/orders/${args.id}`); // ctx.get/post/put/patch/delete
    return { data: res.data };
  },
});

args is optional (omit = no business parameters). Its Zod object is the only validation and type source. args.type defaults to "argv"; set it to "json" for a single complete document via --input / --input-file / stdin (see docs/07-structured-input.md).

defineAuth(opts) — OAuth 2.1 factory

const auth = defineAuth({
  credentialNamespace: "crm", // → config/crm.json + credentials/crm.json
  baseUrl: AUTH_BASE_URL, // OAuth middleware
  scope: "company.api offline_access", // one scope for login + registration
  // flow: 'device',                   // default 'device' | 'authorization_code' | 'client_credentials'
  // commandNamespace: 'auth',         // default 'auth' → crm auth login
});

Three OAuth 2.1 flows, one factory: device (RFC 8628, default), authorization_code + PKCE, and client_credentials. It returns a Plugin — drop it into defineCliApp({ plugins: [auth] }) and the login/status/logout/register commands are auto-mounted.

Plugin (hooks + provides)

const myPlugin = {
  name: "audit",
  enforce: "pre", // 'pre' | 'post' (default normal)
  provides: {
    commands: { telemetry: telemetryCmd }, // contribute commands
    namespaces: { admin: { users: userCmd } },
  },
  async beforeRequest(ctx, req) {
    return { ...req, headers: { ...req.headers, "x-client": "my-cli" } };
  },
  async transformOutput(ctx, data) {
    return data;
  },
  async handleUnauthorized(ctx, event) {
    return { action: "decline" };
  },
};

Commands contributed via a plugin's provides are automatically exempted from that plugin's own beforeCommand, but not from other plugins. See docs/02-sdk-guide.md.


Output contract

Success (stdout):

{"ok":true,"source":"orders","data":{"orders":[...]},"meta":{"count":2,"pagination":{"complete":true}}}

Error (stderr):

{
  "ok": false,
  "error": {
    "type": "api",
    "subtype": "not_found",
    "message": "Order not found",
    "hint": "Check the ID"
  }
}

Exit codes (set automatically by error category; agents branch on them):

code category meaning
0 success
1 api server-side business error (404/500/429…)
2 validation invalid parameter
3 authentication / authorization / config login required / missing permission / missing config
4 network DNS / timeout / connection refused
5 internal SDK internal error (should rarely happen)
6 policy risk-control block
10 confirmation high-risk write requires --yes

Nine typed error classes — ValidationError / AuthenticationError / PermissionError / ConfigError / NetworkError / APIError (with NotFoundError) / PolicyError / InternalError / ConfirmationRequiredError. Always throw errs.*; a bare Error is downgraded to internal/unknown.

Output modes: auto (TTY → text, pipe/script → JSON) by default, overridable with --json / --no-json or defaultFormat. Agents and scripts should always pass --json.


Skills & progressive disclosure

  • <bin> skills gen <name> --init — generate a SKILL.md skeleton with an auto-generated command table (AUTO-GEN region).
  • <bin> skills gen <name> — refresh only the auto-generated block; hand-written semantics are preserved.
  • <bin> skills sync — copy skills to installed agent dirs (~/.agents always; ~/.claude/~/.codex/~/.cursor/~/.zcode/~/.openclaw/~/.pi when present).
  • <bin> skills list / <bin> skills read <name> — list / read bundled skills.

Agents load skills lazily: they start with only name + description, expand the full SKILL.md when a task matches, and read references/ only on demand — so unused APIs cost no tokens.


Documentation

Design docs ship in docs/ — English as *.en.md, Chinese as *.md:

Doc Content
00-overview.en.md Architecture, layering, decision checklist
01-cli-usage.en.md Command invocation, pipes, pagination, exit codes
02-sdk-guide.en.md SDK usage, ctx interface, hooks
03-envelopes.en.md Unified output field contract
04-errors.en.md 9 error classes, when to throw
05-credentials.en.md Provider chain, custom credentials
06-skills.en.md Skill system, command doc auto-generation
07-structured-input.md Structured payloads, validation, write policies (English; see 07-structured-input.zh-CN.md for Chinese)

The npm package ships the agent-cli-builder skill — the agent-facing guide to building a CLI with this SDK — plus 11 references (core API, auth patterns, error catalog, plugin patterns, skill generation, testing, …).


Development

pnpm install
pnpm build        # tsup → dist/
pnpm typecheck    # tsc (incl. type-tests)
pnpm test         # vitest
pnpm test:package # npm tarball smoke test

See the contribution guide, security policy, and support policy before opening a pull request or report.

License

MIT © renxqoo

About

Agent-native CLI framework — a command-line framework that lets AI agents consume business data in a structured way (auth, unified output, errors, credentials, pipes, skills)

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages