mcpField
B
bun-shell

bun-shell

github.com/secondsky開発240398 回インストール5 週間前に更新
リポジトリ

bun-shell について

bun-shellは、JavaScriptおよびTypeScript開発者向けにBunのシェルスクリプトAPIを案内するスキルです。コマンドテンプレート、プロセス起動、出力処理、スクリプトのパターンを扱います。

主な機能

  1. 01Bun.$によるシェルコマンド実行
  2. 02安全な変数埋め込み
  3. 03パイプと出力形式の処理
  4. 04プロセス起動と同期実行
  5. 05ストリーミングとプロセス制御
  6. 06環境変数と作業ディレクトリの設定

活用シーン

  1. 01Bunのシェルスクリプト作成
  2. 02プロジェクトのチェックを並列実行
  3. 03コマンド出力の取得やストリーミング
  4. 04長時間実行される子プロセスの管理

インストール

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

$npx skills add https://github.com/secondsky/claude-skills --skill bun-shell

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

詳細

Bun Shell

Bun は、テンプレートリテラルと spawn API による強力なシェルスクリプト機能を提供します。

Bun.$(シェルテンプレート)

基本的な使い方
import { $ } from "bun";

// Run command
await $`echo "Hello World"`;

// Get output
const result = await $`ls -la`.text();
console.log(result);

// JSON output
const pkg = await $`cat package.json`.json();
console.log(pkg.name);
変数の補間
import { $ } from "bun";

const name = "world";
const dir = "./src";

// Safe interpolation (escaped)
await $`echo "Hello ${name}"`;
await $`ls ${dir}`;

// Array expansion
const files = ["a.txt", "b.txt", "c.txt"];
await $`touch ${files}`;
パイプ処理
import { $ } from "bun";

// Pipe commands
const result = await $`cat file.txt | grep "pattern" | wc -l`.text();

// Chain with JavaScript
const files = await $`ls -la`.text();
const lines = files.split("\n").filter(line => line.includes(".ts"));
エラー処理
import { $ } from "bun";

// Throws on non-zero exit
try {
  await $`exit 1`;
} catch (err) {
  console.log(err.exitCode); // 1
  console.log(err.stderr);
}

// Quiet mode (no throw)
const result = await $`exit 1`.quiet();
console.log(result.exitCode); // 1

// Check exit code
const { exitCode } = await $`grep pattern file.txt`.quiet();
if (exitCode !== 0) {
  console.log("Pattern not found");
}
出力の種類
import { $ } from "bun";

// Text
const text = await $`echo hello`.text();

// JSON
const json = await $`cat data.json`.json();

// Lines
const lines = await $`ls`.lines();

// Blob
const blob = await $`cat image.png`.blob();

// ArrayBuffer
const buffer = await $`cat binary.dat`.arrayBuffer();
環境変数
import { $ } from "bun";

// Set env for command
await $`echo $MY_VAR`.env({ MY_VAR: "value" });

// Access current env
$.env.MY_VAR = "value";
await $`echo $MY_VAR`;

// Clear env
await $`env`.env({});
作業ディレクトリ
import { $ } from "bun";

// Change directory for command
await $`pwd`.cwd("/tmp");

// Or globally
$.cwd("/tmp");
await $`pwd`;

Bun.spawn

基本的な spawn
const proc = Bun.spawn(["echo", "Hello World"]);
const output = await new Response(proc.stdout).text();
console.log(output); // "Hello World\n"
オプションを指定する
const proc = Bun.spawn(["node", "script.js"], {
  cwd: "./project",
  env: {
    NODE_ENV: "production",
    ...process.env,
  },
  stdin: "pipe",
  stdout: "pipe",
  stderr: "pipe",
});

// Write to stdin
proc.stdin.write("input data\n");
proc.stdin.end();

// Read stdout
const output = await new Response(proc.stdout).text();
const errors = await new Response(proc.stderr).text();

// Wait for exit
const exitCode = await proc.exited;
標準入出力のオプション
// Inherit (use parent's stdio)
Bun.spawn(["ls"], { stdio: ["inherit", "inherit", "inherit"] });

// Pipe (capture output)
Bun.spawn(["ls"], { stdin: "pipe", stdout: "pipe", stderr: "pipe" });

// Null (ignore)
Bun.spawn(["ls"], { stdout: null, stderr: null });

// File (redirect to file)
Bun.spawn(["ls"], {
  stdout: Bun.file("output.txt"),
  stderr: Bun.file("errors.txt"),
});
出力のストリーミング
const proc = Bun.spawn(["tail", "-f", "log.txt"], {
  stdout: "pipe",
});

const reader = proc.stdout.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(new TextDecoder().decode(value));
}

Bun.spawnSync

// Synchronous execution
const result = Bun.spawnSync(["ls", "-la"]);

console.log(result.exitCode);
console.log(result.stdout.toString());
console.log(result.stderr.toString());
console.log(result.success); // exitCode === 0

シェルスクリプト

Shebang スクリプト
#!/usr/bin/env bun
import { $ } from "bun";

// Script logic
const branch = await $`git branch --show-current`.text();
console.log(`Current branch: ${branch.trim()}`);

await $`npm test`;
await $`npm run build`;
chmod +x script.ts
./script.ts
複雑なスクリプト
#!/usr/bin/env bun
import { $ } from "bun";

async function deploy() {
  console.log("🚀 Starting deployment...");

  // Check for uncommitted changes
  const status = await $`git status --porcelain`.text();
  if (status.trim()) {
    console.error("❌ Uncommitted changes found!");
    process.exit(1);
  }

  // Run tests
  console.log("🧪 Running tests...");
  await $`bun test`;

  // Build
  console.log("🏗️ Building...");
  await $`bun run build`;

  // Deploy
  console.log("📦 Deploying...");
  await $`rsync -avz ./dist/ server:/app/`;

  console.log("✅ Deployment complete!");
}

deploy().catch((err) => {
  console.error("❌ Deployment failed:", err);
  process.exit(1);
});
並列コマンド
import { $ } from "bun";

// Run in parallel
await Promise.all([
  $`npm run lint`,
  $`npm run typecheck`,
  $`npm run test`,
]);

// Or with spawn
const procs = [
  Bun.spawn(["npm", "run", "lint"]),
  Bun.spawn(["npm", "run", "typecheck"]),
  Bun.spawn(["npm", "run", "test"]),
];

await Promise.all(procs.map(p => p.exited));

対話型コマンド

import { $ } from "bun";

// Pass through stdin
const proc = Bun.spawn(["node"], {
  stdin: "inherit",
  stdout: "inherit",
  stderr: "inherit",
});

await proc.exited;

プロセス管理

const proc = Bun.spawn(["long-running-process"]);

// Kill process
proc.kill(); // SIGTERM
proc.kill("SIGKILL"); // Force kill

// Check if running
console.log(proc.killed);

// Get PID
console.log(proc.pid);

// Wait with timeout
const timeout = setTimeout(() => proc.kill(), 5000);
await proc.exited;
clearTimeout(timeout);

よくあるパターン

npm/bun スクリプトの実行
import { $ } from "bun";

await $`bun run build`;
await $`bun test`;
await $`bunx tsc --noEmit`;
Git 操作
import { $ } from "bun";

const branch = await $`git branch --show-current`.text();
const commit = await $`git rev-parse HEAD`.text();
const status = await $`git status --short`.text();

if (status) {
  await $`git add -A`;
  await $`git commit -m "Auto commit"`;
}
ファイル操作
import { $ } from "bun";

// Find files
const files = await $`find . -name "*.ts"`.lines();

// Search content
const matches = await $`grep -r "TODO" src/`.text();

// Archive
await $`tar -czf backup.tar.gz ./data`;

よくあるエラー

エラー原因解決方法
Command not foundPATH に含まれていない絶対パスを使用する
Permission denied実行可能ではないchmod +x
Exit code 1コマンドが失敗したstderr を確認する
EPIPEパイプが破損したプロセスの終了を処理する

参照資料を読み込むタイミング

以下の場合は references/advanced-scripting.md を読み込みます:

  • 複雑なパイプライン
  • プロセスグループ
  • シグナル処理

以下の場合は references/cross-platform.md を読み込みます:

  • Windows との互換性
  • パスの処理
  • シェルの違い

コメント