This skill should be used when working with Bun runtime, bun:sqlite, Bun.serve, bun:test, or when Bun, bun:test, or Bun-specific patterns are mentioned.
Generates Bun-native code for SQLite, HTTP servers, file operations, testing, and shell commands using Bun runtime APIs.
npx claudepluginhub outfitter-dev/outfitterThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/database-crud.mdexamples/file-uploads.mdreferences/server-patterns.mdreferences/sqlite-patterns.mdreferences/testing.mdBun runtime → native APIs → zero-dependency patterns.
<when_to_use>
NOT for: Node.js-only patterns, cross-runtime libraries, non-Bun projects
</when_to_use>
<runtime_basics>
Package management:
bun install # Install deps
bun add zod # Add package
bun remove zod # Remove package
bun update # Update all
Script execution:
bun run dev # Run package.json script
bun run src/index.ts # Execute TypeScript directly
bun --watch index.ts # Watch mode
Testing:
bun test # All tests
bun test src/ # Directory
bun test --watch # Watch mode
bun test --coverage # With coverage
Building:
bun build ./index.ts --outfile dist/bundle.js
bun build ./index.ts --compile --outfile myapp # Standalone executable
</runtime_basics>
<file_operations>
// Read file (lazy, efficient)
const file = Bun.file("./data.json");
if (!(await file.exists())) throw new Error("File not found");
// Read formats
const text = await file.text();
const json = await file.json();
const buffer = await file.arrayBuffer();
const stream = file.stream(); // Large files
// Metadata
console.log(file.size, file.type);
// Write
await Bun.write("./output.txt", "content");
await Bun.write("./data.json", JSON.stringify(data));
await Bun.write("./blob.txt", new Blob(["data"]));
</file_operations>
import { Database } from "bun:sqlite";
const db = new Database("app.db", {
create: true,
readwrite: true,
strict: true,
});
// Create tables
db.run(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)
`);
// Prepared statements (always use these)
const getUser = db.prepare("SELECT * FROM users WHERE id = ?");
const createUser = db.prepare(
"INSERT INTO users (id, email, name) VALUES (?, ?, ?) RETURNING *"
);
// Execution
const user = getUser.get("user-123"); // Single row
const all = db.prepare("SELECT * FROM users").all(); // All rows
db.prepare("DELETE FROM users WHERE id = ?").run("id"); // No return
// Named parameters
const stmt = db.prepare("SELECT * FROM users WHERE email = $email");
stmt.get({ $email: "alice@example.com" });
// Transactions (atomic, auto-rollback on error)
const transfer = db.transaction(
(fromId: string, toId: string, amount: number) => {
db.run("UPDATE accounts SET balance = balance - ? WHERE id = ?", [
amount,
fromId,
]);
db.run("UPDATE accounts SET balance = balance + ? WHERE id = ?", [
amount,
toId,
]);
}
);
transfer("alice", "bob", 100);
db.close(); // When done
See sqlite-patterns.md for migrations, pooling, repository pattern.
</sqlite>// Hash (argon2id recommended)
const hash = await Bun.password.hash("password123", {
algorithm: "argon2id",
memoryCost: 65536, // 64 MB
timeCost: 3,
});
// Or bcrypt
const bcryptHash = await Bun.password.hash("password123", {
algorithm: "bcrypt",
cost: 12,
});
// Verify
const isValid = await Bun.password.verify("password123", hash);
if (!isValid) throw new Error("Invalid password");
Auth flow example:
app.post("/auth/register", zValidator("json", RegisterSchema), async (c) => {
const { email, password } = c.req.valid("json");
const db = c.get("db");
if (db.prepare("SELECT id FROM users WHERE email = ?").get(email)) {
throw new HTTPException(409, { message: "Email already registered" });
}
const hashedPassword = await Bun.password.hash(password, {
algorithm: "argon2id",
});
const user = db
.prepare(
`
INSERT INTO users (id, email, password) VALUES (?, ?, ?) RETURNING id, email
`
)
.get(crypto.randomUUID(), email, hashedPassword);
return c.json({ user }, 201);
});
</password>
<http_server>
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/") return new Response("Hello");
if (url.pathname === "/json") return Response.json({ ok: true });
return new Response("Not found", { status: 404 });
},
error(err) {
return new Response(`Error: ${err.message}`, { status: 500 });
},
});
With Hono (recommended for APIs):
import { Hono } from "hono";
const app = new Hono()
.get("/", (c) => c.text("Hello"))
.get("/json", (c) => c.json({ ok: true }));
Bun.serve({ port: 3000, fetch: app.fetch });
See server-patterns.md for routing, middleware, file serving, streaming.
</http_server>
import type { ServerWebSocket } from "bun";
type WsData = { userId: string };
Bun.serve<WsData>({
port: 3000,
fetch(req, server) {
const url = new URL(req.url);
if (url.pathname === "/ws") {
const userId = url.searchParams.get("userId") || "anon";
return server.upgrade(req, { data: { userId } })
? undefined
: new Response("Upgrade failed", { status: 400 });
}
return new Response("Hello");
},
websocket: {
open(ws: ServerWebSocket<WsData>) {
ws.subscribe("chat");
ws.send(JSON.stringify({ type: "connected" }));
},
message(ws: ServerWebSocket<WsData>, msg: string | Buffer) {
ws.publish("chat", msg);
},
close(ws: ServerWebSocket<WsData>) {
ws.unsubscribe("chat");
},
},
});
See server-patterns.md for client tracking, rooms, reconnection.
</websocket>import { $ } from "bun";
// Run commands
const result = await $`ls -la`;
console.log(result.text());
// Variables (auto-escaped)
const dir = "./src";
await $`find ${dir} -name "*.ts"`;
// Check exit code
const { exitCode } = await $`npm test`.nothrow();
if (exitCode !== 0) console.error("Tests failed");
// Spawn process
const proc = Bun.spawn(["ls", "-la"]);
await proc.exited;
// Capture output
const proc2 = Bun.spawn(["echo", "Hello"], { stdout: "pipe" });
const output = await new Response(proc2.stdout).text();
</shell>
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
describe("feature", () => {
let db: Database;
beforeEach(() => {
db = new Database(":memory:");
});
afterEach(() => {
db.close();
});
test("behavior", () => {
expect(result).toBe(expected);
expect(arr).toContain(item);
expect(fn).toThrow();
expect(obj).toEqual({ foo: "bar" });
});
test("async", async () => {
const result = await asyncFn();
expect(result).toBeDefined();
});
test.todo("pending feature");
test.skip("temporarily disabled");
});
bun test # All tests
bun test src/api.test.ts # Specific file
bun test --watch # Watch mode
bun test --coverage # With coverage
See testing.md for assertions, mocking, snapshots, best practices.
</testing>// Access
console.log(Bun.env.NODE_ENV);
console.log(Bun.env.DATABASE_URL);
// Zod validation
import { z } from "zod";
const EnvSchema = z.object({
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
DATABASE_URL: z.string(),
PORT: z.coerce.number().int().positive().default(3000),
API_KEY: z.string().min(32),
});
export const env = EnvSchema.parse(Bun.env);
Bun auto-loads .env, .env.local, .env.production.
// High-resolution timing
const start = Bun.nanoseconds();
await doWork();
console.log(`Took ${(Bun.nanoseconds() - start) / 1_000_000}ms`);
// Hashing
const hash = Bun.hash(data);
const crc32 = Bun.hash.crc32(data);
const sha256 = Bun.CryptoHasher.hash("sha256", data);
// Sleep
await Bun.sleep(1000);
// Memory
const { rss, heapUsed } = process.memoryUsage();
console.log("RSS:", rss / 1024 / 1024, "MB");
</performance>
# Production bundle
bun build ./index.ts --outfile dist/bundle.js --minify --sourcemap
# External deps
bun build ./index.ts --outfile dist/bundle.js --external hono --external zod
# Standalone executable
bun build ./index.ts --compile --outfile myapp
# Cross-compile
bun build ./index.ts --compile --target=bun-linux-x64 --outfile myapp-linux
bun build ./index.ts --compile --target=bun-darwin-arm64 --outfile myapp-macos
bun build ./index.ts --compile --target=bun-windows-x64 --outfile myapp.exe
</building>
<rules>
ALWAYS:
NEVER:
PREFER:
Examples:
Level up: Load the bun-first skill for migration auditing, dependency elimination, and the decision framework for when to use Bun native APIs over npm packages.
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.