incur
incurはAIエージェントと人間の両方に対応したCLIを構築するためのTypeScriptフレームワークです。新しいCLIを作成する際に利用してください。
description の原文を見る
incur is a TypeScript framework for building CLIs that work for both AI agents and humans. Use when creating new CLIs.
SKILL.md 本文
incur
エージェントと人間の両方で利用できる CLI を構築するための TypeScript フレームワークです。引数とオプションの厳密型スキーマ、構造化された出力エンベロープ、自動生成されるスキルファイル、Skills、MCP、および --llms を介したエージェント検出機能を提供します。
インストール
npm i incur
pnpm i incur
bun i incur
クイックスタート
import { Cli, z } from 'incur'
const cli = Cli.create('greet', {
description: 'A greeting CLI',
args: z.object({
name: z.string().describe('Name to greet'),
}),
run({ args }) {
return { message: `hello ${args.name}` }
},
})
cli.serve()
greet world
# → message: hello world
CLI の作成
Cli.create() がエントリーポイントです。2 つのモードがあります。
シングルコマンド CLI
run を渡してサブコマンドのない CLI を作成します。
const cli = Cli.create('tool', {
description: 'Does one thing',
args: z.object({ file: z.string() }),
run({ args, options }) {
return { processed: args.file }
},
})
ルーター CLI (サブコマンド)
run を省略して、.command() でサブコマンドを登録する CLI を作成します。
const cli = Cli.create('gh', {
version: '1.0.0',
description: 'GitHub CLI',
})
cli.command('status', {
description: 'Show repo status',
run() {
return { clean: true }
},
})
cli.serve()
コマンド
コマンドの登録
cli.command('install', {
description: 'Install a package',
args: z.object({
package: z.string().optional().describe('Package name'),
}),
options: z.object({
saveDev: z.boolean().optional().describe('Save as dev dependency'),
global: z.boolean().optional().describe('Install globally'),
}),
alias: { saveDev: 'D', global: 'g' },
output: z.object({
added: z.number(),
packages: z.number(),
}),
examples: [
{ args: { package: 'express' }, description: 'Install a package' },
{
args: { package: 'vitest' },
options: { saveDev: true },
description: 'Install as dev dependency',
},
],
run({ args, options }) {
return { added: 1, packages: 451 }
},
})
.command() はチェーン可能です。CLI インスタンスを返します。
cli
.command('ping', { run: () => ({ pong: true }) })
.command('version', { run: () => ({ version: '1.0.0' }) })
サブコマンドグループ
サブ CLI を作成し、コマンドグループとしてマウントします。
const cli = Cli.create('gh', { description: 'GitHub CLI' })
const pr = Cli.create('pr', { description: 'Pull request commands' })
pr.command('list', {
description: 'List pull requests',
options: z.object({
state: z.enum(['open', 'closed', 'all']).default('open'),
}),
run({ options }) {
return { prs: [], state: options.state }
},
})
pr.command('view', {
description: 'View a pull request',
args: z.object({ number: z.number() }),
run({ args }) {
return { number: args.number, title: 'Fix bug' }
},
})
// 親 CLI にマウント
cli.command(pr)
cli.serve()
gh pr list --state closed
gh pr view 42
グループは任意にネストできます。
const cli = Cli.create('gh', { description: 'GitHub CLI' })
const pr = Cli.create('pr', { description: 'Pull requests' })
const review = Cli.create('review', { description: 'Review commands' })
review.command('approve', { run: () => ({ approved: true }) })
pr.command(review)
cli.command(pr)
// → gh pr review approve
Fetch API
.command('name', { fetch }) で HTTP サーバーをコマンドとしてマウントします。Argv は curl スタイルのフラグを使って HTTP リクエストに変換されます。
import { Cli } from 'incur'
import { Hono } from 'hono'
const app = new Hono()
app.get('/users', (c) => c.json({ users: [{ id: 1, name: 'Alice' }] }))
app.post('/users', async (c) => c.json({ created: true, ...(await c.req.json()) }, 201))
Cli.create('my-cli', { description: 'My CLI' }).command('api', { fetch: app.fetch }).serve()
my-cli api users # GET /users
my-cli api users -X POST -d '{"name":"Bob"}' # POST /users
my-cli api users --limit 5 # GET /users?limit=5
Fetch API + OpenAPI
fetch とともに OpenAPI スペックを渡して、スペックから型付きサブコマンドを生成します。引数、オプション、説明が含まれます。
Cli.create('my-cli', { description: 'My CLI' })
.command('api', { fetch: app.fetch, openapi: spec })
.serve()
my-cli api listUsers --limit 5 # GET /users?limit=5
my-cli api getUser 42 # GET /users/42
my-cli api createUser --name Bob # POST /users with body
my-cli api --help # shows typed subcommands
あらゆる (Request) => Response ハンドラー (Hono、Elysia など) で動作します。@hono/zod-openapi からのスペックは直接サポートされています。
CLI を Fetch API として提供
cli.fetch で CLI を標準 Fetch API ハンドラーとして公開します。Bun、Cloudflare Workers、Deno、Hono、および (req: Request) => Response を受け入れるあらゆるもので動作します。
import { Cli, z } from 'incur'
const cli = Cli.create('my-cli', { version: '1.0.0' }).command('users', {
args: z.object({ id: z.coerce.number().optional() }),
options: z.object({ limit: z.coerce.number().default(10) }),
run(c) {
if (c.args.id) return { id: c.args.id, name: 'Alice' }
return { users: [{ id: 1, name: 'Alice' }], limit: c.options.limit }
},
})
Bun.serve(cli) // Bun
Deno.serve(cli.fetch) // Deno
export default cli // Cloudflare Workers
app.all('*', (c) => cli.fetch(c.request)) // Elysia
app.use((c) => cli.fetch(c.req.raw)) // Hono
export const GET = cli.fetch // Next.js
export const POST = cli.fetch
リクエストマッピング:
| HTTP | CLI 相当 |
|---|---|
GET /users?limit=5 | my-cli users --limit 5 |
GET /users/42 | my-cli users 42 (位置引数) |
POST /users with JSON body | my-cli users --name Bob |
GET / | ルートコマンド (または 404) |
レスポンスは JSON エンベロープです: { "ok": true, "data": { ... }, "meta": { "command": "users", "duration": "3ms" } }。
エラーステータスコード: 400 は検証エラー、404 は未知のコマンド、500 はスローされたエラーです。
非同期ジェネレーターコマンドは NDJSON (application/x-ndjson) としてストリーミングされます。ミドルウェアは serve() と同じように実行されます。
Fetch ゲートウェイ
解決されたコマンドが Fetch ゲートウェイ (.command('api', { fetch })) の場合、リクエストはネストされたハンドラーに転送されます。
HTTP 経由の MCP
Fetch ハンドラーは /mcp に MCP エンドポイントを公開します。エージェントは HTTP 経由で MCP ツールとしてコマンドを検出して呼び出すことができます。
POST /mcp { "jsonrpc": "2.0", "method": "initialize", ... }
POST /mcp { "jsonrpc": "2.0", "method": "tools/list", ... }
POST /mcp { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "users", ... } }
MCP サーバーは最初の /mcp リクエストで遅延初期化されます。/mcp 以外のパスは通常どおりコマンド API にルーティングされます。
引数とオプション
すべてのスキーマは Zod を使用します。引数は位置指定 (スキーマキーの順序で割り当てられます)。オプションは名前付きフラグです。
引数
args: z.object({
repo: z.string().describe('Repository in owner/repo format'),
branch: z.string().optional().describe('Branch name'),
})
tool clone owner/repo main
# ^^^^^^^^^^ ^^^^
# repo branch
オプション
options: z.object({
state: z.enum(['open', 'closed']).default('open').describe('Filter by state'),
limit: z.number().default(30).describe('Max results'),
label: z.array(z.string()).optional().describe('Filter by labels'),
verbose: z.boolean().optional().describe('Show details'),
})
サポートされているパース処理:
--flag valueおよび--flag=value-f valueショートエイリアス (aliasプロパティ経由)--verboseブール型フラグ (true)、--no-verbose(false)--label bug --label feature配列オプション- 自動型強制 (文字列 → 数値、文字列 → ブール型)
.default()からのデフォルト、.optional()からのオプション性
エイリアス
alias: { state: 's', limit: 'l' }
tool list -s closed -l 10
非推奨オプション
.meta({ deprecated: true }) でオプションを非推奨としてマークします。--help に [deprecated] を表示し、スキルドキュメントに **Deprecated.** を表示し、JSON スキーマに deprecated: true を表示し、TTY モードで stderr 警告を発行します。
options: z.object({
zone: z.string().optional().describe('Availability zone').meta({ deprecated: true }),
region: z.string().optional().describe('Target region'),
})
環境変数
env: z.object({
NPM_TOKEN: z.string().optional().describe('Auth token'),
NPM_REGISTRY: z.string().default('https://registry.npmjs.org').describe('Registry URL'),
})
環境変数は process.env から解析され、Zod スキーマに対して検証されます。
使用パターン
--help で自動生成される概要の代わりに表示する代替使用パターンを定義します。
Cli.create('curl.md', {
args: z.object({ url: z.string() }),
options: z.object({ objective: z.string().optional() }),
usage: [
{ args: { url: true } },
{ args: { url: true }, options: { objective: true } },
{ prefix: 'cat file.txt |', suffix: '| head' },
],
run({ args }) {
return { content: '...' }
},
})
ヘルプで以下のようにレンダリングされます:
Usage: curl.md <url>
curl.md <url> --objective <objective>
cat file.txt | curl.md | head
各使用パターンエントリはサポートします:
| プロパティ | 型 | 説明 |
|---|---|---|
args | Partial<Record<key, true>> | <key> プレースホルダーとして含める引数キー |
options | Partial<Record<key, true>> | --key <key> フラグとして含めるオプションキー |
prefix | string | コマンド前に付加されるテキスト (例: パイピング) |
suffix | string | コマンド後に付加されるテキスト |
args と options は両方とも Zod スキーマから厳密に型付けされます。有効なキーのみが許可されます。
使用パターンは .command() 経由でサブコマンドでも動作します。
出力
すべてのコマンドはデータを返します。incur はそれを構造化エンベロープでラップし、リクエストされた形式にシリアル化します。
出力スキーマ
output を定義して戻り値の形を宣言します。
cli.command('info', {
output: z.object({
name: z.string(),
version: z.string(),
}),
run() {
return { name: 'express', version: '4.21.2' }
},
})
output が提供される場合、TypeScript は run() が正しい形を返すことを強制します。
フォーマット
--format <fmt> または --json で制御します。
| フラグ | フォーマット | 説明 |
|---|---|---|
| (デフォルト) | TOON | トークン効率的、JSON より約 40% 少ないトークン |
--format json | JSON | JSON.parse() セーフ |
--format yaml | YAML | 人間が読みやすい |
--format md | Markdown | ドキュメント/イシュー用のテーブル |
エンベロープ
--full-output を使用して、完全なエンベロープを発行します。
tool info express --full-output
ok: true
data:
name: express
version: 4.21.2
meta:
command: info
duration: 12ms
--full-output がない場合、data のみが発行されます。エラー時には、error ブロックのみが発行されます。
出力のフィルタリング
--filter-output を使用してコマンド出力を特定のキーに絞り込みます。ネストされたアクセス用のドット表記、[start,end] を使用した配列スライス、カンマ区切りパスをサポートします。
cli.command('users', {
description: 'List users',
run() {
return {
users: [
{ name: 'Alice', email: 'alice@example.com', role: 'admin' },
{ name: 'Bob', email: 'bob@example.com', role: 'user' },
{ name: 'Carol', email: 'carol@example.com', role: 'user' },
],
}
},
})
tool users --filter-output users.name
# → [3]: Alice,Bob,Carol
tool users --filter-output users[0,2].name
# → users[2]{name}:
# → Alice
# → Bob
トークンページネーション
--token-count、--token-limit、--token-offset を使用して大規模な出力を管理します。トークンは LLM トークン化ルール (~96% の精度) を使用して推定されます。
# トークン数を確認
tool users --token-count
# → 42
# 最初の 20 トークンに制限
tool users --token-limit 20
# オフセットでページネーション
tool users --token-offset 20 --token-limit 20
--full-output では、切り詰められた出力にプログラマティックページネーション用に meta.nextOffset が含まれます。
コマンドスキーマ
--schema を使用して、コマンドの引数、環境変数、オプション、出力の JSON スキーマを出力します。
cli.command('install', {
description: 'Install a package',
args: z.object({
package: z.string().describe('Package name'),
}),
options: z.object({
saveDev: z.boolean().optional().describe('Save as dev dependency'),
}),
run({ args }) {
return { added: 1 }
},
})
tool install --schema
# → args:
# → type: object
# → properties:
# → package:
# → type: string
# → options:
# → type: object
# → properties:
# → saveDev:
# → type: boolean
マシン読み取り可能な出力には --schema --format json を使用します。Fetch コマンドではサポートされていません。
TTY 検出
incur は stdout が TTY かどうかに基づいて出力を適応させます。
| シナリオ | TTY (人間) | 非 TTY (エージェント/パイプ) |
|---|---|---|
| コマンド出力 | フォーマットされたデータのみ | TOON エンベロープ |
| エラー | 人間が読みやすいメッセージ | エラーエンベロープ |
--help | きれいなヘルプテキスト | 同じ |
--json / --format | 構造化にオーバーライド | 同じ |
実行コンテキスト
agent ブール型
run コンテキストは agent を含みます。stdout が TTY ではない (パイプまたはエージェントで使用) 場合は true、ターミナルで実行されている場合は false。
cli.command('deploy', {
run(c) {
if (!c.agent) console.log('Deploying...')
return { status: 'ok' }
},
})
ok() および error() ヘルパー
コンテキストヘルパーを使用して明示的な結果制御を行います。
run(c) {
const item = await db.find(c.args.id)
if (!item)
return error({
code: 'NOT_FOUND',
message: `Item ${c.args.id} not found`,
retryable: false,
})
return c.ok(item)
}
CTA (行動喚起)
成功時に次のコマンドを提案してエージェントをガイドします。
run(c) {
const result = { id: 42, name: c.args.name }
return c.ok(result, {
cta: {
description: 'Suggested commands:',
commands: [
{ command: 'get', args: { id: 42 }, description: 'View the item' },
'list',
],
},
})
}
またはエラー時にエージェントが自己修正するのを助けるために。
run(c) {
const token = process.env.GH_TOKEN
if (!token)
return c.error({
code: 'NOT_AUTHENTICATED',
message: 'GitHub token not found',
retryable: true,
cta: {
description: 'To authenticate:',
commands: [
{ command: 'auth login', description: 'Log in to GitHub' },
{ command: 'config set', options: { token: true }, description: 'Set token manually' },
],
},
})
// ...
}
エージェント検出
MCP サーバー
すべての incur CLI には組み込みモデルコンテキストプロトコル (MCP) サポートがあります。エージェントが直接呼び出すことができる MCP ツールとしてコマンドを公開します。
mcp add 組み込みコマンド
CLI をエージェント用の MCP サーバーとして登録します。
my-cli mcp add
これにより、エージェントの MCP 設定に CLI が登録されます。Claude Code、Cursor、Amp その他と組み込みで動作します。
オプション:
| フラグ | 説明 |
|---|---|
-c, --command | エージェントがサーバーを開始するために実行するコマンドをオーバーライド |
--agent <agent> | 特定のエージェントをターゲット (例: claude-code、cursor) |
--no-global | グローバルではなくプロジェクトにインストール |
--mcp フラグ
CLI を MCP stdio サーバーとして開始します。
my-cli --mcp
これにより、すべてのコマンドが stdin/stdout 経由で MCP ツールとして公開されます。コマンドグループはアンダースコアでフラット化されます (例: pr_list、pr_view)。引数とオプションは単一のフラット入力スキーマにマージされます。
Skills
すべての incur ベースの CLI は skills add でエージェントスキルファイルを自動生成してインストールできます。
my-cli skills add
これはコマンド定義から Markdown スキルファイルを生成し、エージェントが自動的に CLI を検出できるようにインストールします。
設定
skills add を設定することも可能です。
const cli = Cli.create('my-cli', {
sync: {
depth: 1,
include: ['_root'],
suggestions: ['install react as a dependency', 'check for outdated packages'],
},
})
| オプション | 型 | 説明 |
|---|---|---|
depth | number | スキルファイルのグループ化深度。0 = 単一ファイル、1 = 最上位コマンドごと。デフォルト: 1 |
include | string[] | 含める追加 SKILL.md ファイルの Glob パターン。プロジェクトレベルの SKILL.md には '_root' を使用 |
suggestions | string[] | 同期後に表示されてユーザーが開始するのを支援するサンプルプロンプト |
--llms フラグ
すべての incur CLI には、すべてのコマンドのマシン読み取り可能マニフェストを出力する組み込み --llms フラグがあります。
tool --llms
デフォルトで Markdown スキルドキュメントを出力します。
# tool install
Install a package
## Arguments
| Name | Type | Required | Description |
| --------- | -------- | -------- | ----------------------- |
| `package` | `string` | no | Package name to install |
## Options
| Flag | Type | Default | Description |
| ----------- | --------- | ------- | ---------------------- |
| `--saveDev` | `boolean` | | Save as dev dependency |
| `--global` | `boolean` | | Install globally |
JSON スキーママニフェストには --llms --format json を使用します。
{
"version": "incur.v1",
"commands": [
{
"name": "install",
"description": "Install a package",
"schema": {
"args": { "type": "object", "properties": { "package": { "type": "string" } } },
"options": { "type": "object", "properties": { "saveDev": { "type": "boolean" } } },
"output": { "type": "object", "properties": { "added": { "type": "number" } } }
}
}
]
}
組み込みフラグ
| フラグ | 説明 |
|---|---|
--help, -h | CLI または特定のコマンドのヘルプを表示 |
--version | CLI バージョンを出力 |
--llms | エージェント読み取り可能なコマンドマニフェスト出力 |
--mcp | MCP stdio サーバーとして開始 |
--json | --format json の短縮形 |
--format <fmt> | 出力フォーマット: toon、json、yaml、md |
--full-output | 完全なエンベロープを含める (ok、data、meta) |
例
コマンドの型付き例
cli.command('deploy', {
args: z.object({ env: z.enum(['staging', 'production']) }),
options: z.object({ force: z.boolean().optional() }),
examples: [
{ args: { env: 'staging' }, description: 'Deploy to staging' },
{ args: { env: 'production' }, options: { force: true }, description: 'Force deploy to prod' },
],
run({ args }) {
return { deployed: args.env }
},
})
例は --help 出力と生成されたスキルファイルに表示されます。
ヒント
cli.command('publish', {
hint: 'Requires NPM_TOKEN to be set in your environment.',
// ...
})
ヒントはヘルプ出力の例の後に表示され、スキルファイルに含まれます。
出力ポリシー
出力データが人間に表示されるかどうかを制御します。'all' (デフォルト) はすべてにデータを表示します。'agent-only' は人間/TTY モードでのデータ表示を抑制しながら、--json、--format、または --full-output 経由で返します。
cli.command('deploy', {
outputPolicy: 'agent-only',
run() {
return { id: 'deploy-123', url: 'https://staging.example.com' }
},
})
グループまたはルート CLI に設定して、子全体で継承します。子はオーバーライドできます。
const sub = Cli.create('internal', { outputPolicy: 'agent-only' })
sub.command('sync', { run: () => ({ synced: true }) }) // agent-only を継承
sub.command('status', { outputPolicy: 'all', run: () => ({}) }) // オーバーライド
ミドルウェア
cli.use() で構成可能な before/after フックを登録します。ミドルウェアは登録順に、オニオンスタイルで実行されます。各呼び出しは await next() で続行します。
const cli = Cli.create('deploy-cli
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- wevm
- リポジトリ
- wevm/incur
- ライセンス
- MIT
- 最終更新
- 2026/5/1
Source: https://github.com/wevm/incur / ライセンス: MIT