mcpField
N
npx-cli

npx-cli

github.com/jwynia開発179304 回インストール35 週間前に更新
リポジトリ

npx-cli について

単一パッケージのリポジトリで、npx から実行できるコマンドラインツールを構築・公開するためのスキルです。開発には Bun を使い、Node.js で動作するバイナリを作成します。

主な機能

  1. 01Bun を中心としたツールチェーン
  2. 02CLI プロジェクトの雛形作成
  3. 03引数とサブコマンドの解析
  4. 04CLI とロジックの分離
  5. 05テストと公開手順の設定

活用シーン

  1. 01npx CLI の新規作成
  2. 02既存ライブラリへの CLI 追加
  3. 03引数解析とターミナル操作の設定
  4. 04CLI パッケージの npm 公開

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jwynia/agent-skills --skill npx-cli

インストール後、Claude Code で/npx-cliを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

reference/
cli-patterns.md
esm-cjs-guide.md
publishing-workflow.md
strict-typescript.md
scripts/
scaffold.ts
templates/
biome.json
bunup.config.ts
eslint.config.ts
gitignore
package.json.hbs
tsconfig.json
vitest.config.ts
templates/scripts/
postbuild.ts
templates/src/
cli.test.ts.hbs
cli.ts.hbs
index.test.ts.hbs
index.ts.hbs

詳細

npx CLI ツール開発(Bun 優先)

Bun を主なランタイムおよびツールチェーンとして使用し、すべての npm/npx ユーザー(Node.js ランタイム)で動作するバイナリを生成して、npx で実行可能なコマンドラインツールをビルドして公開します。

このスキルを使用する場合

以下の場合に使用します:

  • CLI ツールをゼロから作成する
  • npx で実行可能なバイナリをビルドする
  • CLI の引数解析、サブコマンド、またはターミナル UX を設定する
  • CLI ツールを npm に公開する
  • 既存のライブラリパッケージに CLI を追加する

以下の場合は使用しないでください:

  • CLI のないライブラリをビルドする(npm-package スキルを使用してください)
  • アプリケーションをビルドする(公開するパッケージではないもの)
  • モノレポで作業する(このスキルは単一パッケージのリポジトリを対象としています)

ツールチェーン

項目ツール理由
ランタイム/パッケージマネージャーBunインストール、実行、トランスパイルが高速
バンドラーBunupBun ネイティブ、デュアルエントリー(ライブラリ+CLI)、.d.ts
引数解析citty約 3KB、TypeScript ネイティブ、自動ヘルプ、runMain()
ターミナルの色付けpicocolors約 7KB、CJS+ESM、自動検出
TypeScriptmodule: "nodenext"、strict: true +追加設定最大限の正確性
フォーマット+基本的なリンティングBiome v2高速で、ツールが 1 つで済む
型を考慮したリンティングESLint + typescript-eslint高度な型安全性
テストVitest分離、モック、カバレッジ
バージョン管理Changesetsファイルベース、明示的
公開npm publish --provenanceTrusted Publishing/OIDC

新しい CLI のスキャフォールディング

スキャフォールドスクリプトを実行します:

bun run <skill-path>/scripts/scaffold.ts ./my-cli \
  --name my-cli \
  --bin my-cli \
  --description "What this CLI does" \
  --author "Your Name" \
  --license MIT

オプション:

  • --bin <name> — npx 用のバイナリ名(デフォルトはスコープを除いたパッケージ名)
  • --cli-only — ライブラリのエクスポートなし、CLI バイナリのみ
  • --no-eslint — ESLint をスキップし、Biome のみを使用

次に依存関係をインストールします:

cd my-cli
bun install
bun add -d bunup typescript vitest @vitest/coverage-v8 @biomejs/biome @changesets/cli
bun add citty picocolors
bun add -d eslint typescript-eslint  # unless --no-eslint

プロジェクト構成

デュアル構成(ライブラリ+CLI)— デフォルト
my-cli/
├── src/
│   ├── index.ts            # Library exports (programmatic API)
│   ├── index.test.ts       # Unit tests for library
│   ├── cli.ts              # CLI entry point (imports from index.ts)
│   └── cli.test.ts         # CLI integration tests
├── dist/
│   ├── index.js            # Library bundle
│   ├── index.d.ts          # Type declarations
│   └── cli.js              # CLI binary (with shebang)
├── .changeset/
│   └── config.json
├── package.json
├── tsconfig.json
├── bunup.config.ts
├── biome.json
├── eslint.config.ts
├── vitest.config.ts
├── .gitignore
├── README.md
└── LICENSE
CLI 専用(ライブラリのエクスポートなし)

src/index.ts と src/index.test.ts を除いた同じ構成です。package.json に exports フィールドはなく、bin のみです。

アーキテクチャパターン

ロジックと CLI の接続処理を分離します。 CLI エントリー(cli.ts)は、次の処理を行う薄いラッパーです:

  1. citty で引数を解析する
  2. ライブラリ/コアモジュールを呼び出す
  3. ターミナル向けに出力を整形する

すべてのビジネスロジックは、インポート可能なモジュール(index.ts または内部モジュール)に置きます。これにより、プロセスを起動せずにロジックを単体テストできます。

cli.ts → imports from → index.ts / core modules
                              ↑
                         unit tests

重要なルール(必須)

npm-package スキルのすべてのルールがここでも適用されます。以下は CLI パッケージ固有の追加ルールです:

バイナリの設定
  1. 公開する bin ファイルでは必ず #!/usr/bin/env node を使用してください。 #!/usr/bin/env bun は使用しないでください。npx ユーザーの大多数は Bun をインストールしていません。

  2. bin は dist/ 内のコンパイル済み JavaScript を指すようにしてください。 TypeScript ソースは指定しないでください。npx の利用者はビルドツールチェーンを持っていません。

  3. bin ファイルに実行権限があることを確認してください。 ビルドスクリプトでは、コンパイル後に chmod +x dist/cli.js を実行します。

  4. Node.js をターゲットとしてビルドしてください。 Bunup の出力は Node.js 上で実行できる必要があり、Bun ランタイムの機能に依存してはなりません。

パッケージの設定
  1. package.json では必ず "type": "module" を使用してください。

  2. すべての exports ブロックで、types を最初の条件にしてください。

  3. files: ["dist"] を使用してください。 ホワイトリストのみを指定します。

  4. デュアルパッケージ(ライブラリ+CLI)の場合: exports フィールドはライブラリ API を公開します。bin フィールドは CLI を公開します。これらは独立しており、bin は exports の一部ではありません。

コード品質
  1. any は禁止です。 unknown を使い、型を絞り込んでください。

  2. 型のみのインポートには import type を使用してください。

  3. エラーを適切に処理してください。 CLI のユーザーに生のスタックトレースを見せてはいけません。これを自動で処理する citty の runMain() と、クリーンアップ用の process.on('SIGINT', ...) を使用してください。

  4. 適切な終了コードを返してください。 成功時は 0、エラー時は 1、不正な引数の場合は 2、SIGINT の場合は 130 です。

リファレンスドキュメント

設定を変更する前に、以下をお読みください。

citty による引数解析

単一コマンド
import { defineCommand, runMain } from 'citty';

const main = defineCommand({
  meta: { name: 'my-cli', version: '1.0.0', description: '...' },
  args: {
    input: { type: 'positional', description: 'Input file', required: true },
    output: { alias: 'o', type: 'string', description: 'Output path', default: './out' },
    verbose: { alias: 'v', type: 'boolean', description: 'Verbose output', default: false },
  },
  run({ args }) {
    // args is fully typed
  },
});

void runMain(main);
サブコマンド
import { defineCommand, runMain } from 'citty';

const init = defineCommand({ meta: { name: 'init' }, /* ... */ });
const build = defineCommand({ meta: { name: 'build' }, /* ... */ });

const main = defineCommand({
  meta: { name: 'my-cli', version: '1.0.0' },
  subCommands: { init, build },
});

void runMain(main);

エラー処理、色、スピナーを含む完全な例については、reference/cli-patterns.md をご覧ください。

テスト戦略

単体テスト — ロジックをテストする
// src/index.test.ts
import { describe, it, expect } from 'vitest';
import { processInput } from './index.js';

describe('processInput', () => {
  it('handles valid input', () => {
    expect(processInput('test')).toBe('expected');
  });
});
統合テスト — バイナリをテストする

まずビルドして(bun run build)、コンパイル済みバイナリを起動します。

// src/cli.test.ts
import { describe, it, expect } from 'vitest';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const exec = promisify(execFile);

describe('CLI', () => {
  it('prints help', async () => {
    const { stdout } = await exec('node', ['./dist/cli.js', '--help']);
    expect(stdout).toContain('my-cli');
  });
});

開発ワークフロー

# Write code and tests
bun run test:watch    # Vitest watch mode

# Check everything
bun run lint          # Biome + ESLint
bun run typecheck     # tsc --noEmit
bun run test          # Vitest

# Build and try the CLI locally
bun run build
node ./dist/cli.js --help
node ./dist/cli.js some-input

# Prepare release
bunx changeset
bunx changeset version

# Publish
bun run release       # Build + npm publish --provenance

後からサブコマンドを追加する

  1. サブコマンドごとに新しいファイルを作成します。src/commands/init.ts、src/commands/build.ts
  2. それぞれが defineCommand() の結果をエクスポートします
  3. メインコマンドにインポートし、subCommands に組み込みます
  4. ロジックはテスト可能なモジュールに置き、コマンドは薄いラッパーに保ちます

CLI 専用パッケージをデュアル(ライブラリ + CLI)パッケージに変換する

  1. 公開 API を含む src/index.ts を作成します
  2. bunup.config.ts を更新して、両方のエントリポイントを含めます
  3. 既存の bin と並べて、package.json に exports フィールドを追加します
  4. .d.ts の生成を追加します。dts: { entry: ['src/index.ts'] }

Bun 固有の注意点

  • bun build は .d.ts ファイルを生成しません。 Bunup または tsc --emitDeclarationOnly を使用してください。
  • bun build は構文をダウンレベル化しません。 ES2022 以降の構文はそのまま出力されます。
  • bun publish は --provenance をサポートしません。 npm publish を使用してください。
  • bun publish が使用するのは NODE_AUTH_TOKEN ではなく NPM_CONFIG_TOKEN です。
  • 公開パッケージでは #!/usr/bin/env bun を使用しないでください。 ユーザーは Bun をインストールしていません。
  • Bunup の banner はライブラリのエントリを含む、すべての出力ファイルに shebang を追加します。 問題になる場合は、ビルド後スクリプトを使って dist/cli.js にだけ shebang を追加してください。

コメント