Agent skill

bun-dev

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.

Stars 26
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/outfitter-dev/agents/tree/main/plugins/outfitter/skills/bun-dev

Metadata

Additional technical details for this skill

version
1.0.0

SKILL.md

Bun Development

Bun runtime → native APIs → zero-dependency patterns.

<when_to_use>

  • Bun runtime development
  • SQLite database with bun:sqlite
  • HTTP server with Bun.serve
  • Testing with bun:test
  • File operations with Bun.file/Bun.write
  • Shell operations with $ template
  • Password hashing with Bun.password
  • Environment variable handling
  • Building and bundling

NOT for: Node.js-only patterns, cross-runtime libraries, non-Bun projects

</when_to_use>

<runtime_basics>

Package management:

bash
bun install          # Install deps
bun add zod          # Add package
bun remove zod       # Remove package
bun update           # Update all

Script execution:

bash
bun run dev          # Run package.json script
bun run src/index.ts # Execute TypeScript directly
bun --watch index.ts # Watch mode

Testing:

bash
bun test             # All tests
bun test src/        # Directory
bun test --watch     # Watch mode
bun test --coverage  # With coverage

Building:

bash
bun build ./index.ts --outfile dist/bundle.js
bun build ./index.ts --compile --outfile myapp  # Standalone executable

</runtime_basics>

File Operations

<file_operations>

typescript
// 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>

SQLite (bun:sqlite)

typescript
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.

Password Hashing

typescript
// 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:

typescript
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);
});

HTTP Server

<http_server>

typescript
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):

typescript
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>

WebSocket

typescript
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.

Shell Operations

typescript
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();

Testing (bun:test)

typescript
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');
});
bash
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.

Environment Variables

typescript
// 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.

Performance Utilities

typescript
// 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');

Building & Bundling

bash
# 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

ALWAYS:

  • Use Bun APIs when available (faster, native)
  • Prepared statements for database queries
  • Transactions for multi-statement operations
  • argon2id for password hashing
  • Validate environment variables at startup
  • Close database connections when done

NEVER:

  • String interpolation in SQL (use parameters)
  • Plaintext passwords
  • Ignore async disposal cleanup
  • Deprecated Node.js APIs when Bun native exists

PREFER:

  • Bun.file over fs.readFile
  • Bun.write over fs.writeFile
  • bun:sqlite over external SQLite libraries
  • Bun.password over bcrypt/argon2 packages
  • $ shell template over child_process
  • sqlite-patterns.md — migrations, pooling, repository, FTS
  • server-patterns.md — HTTP, WebSocket, streaming, compression
  • testing.md — assertions, mocking, snapshots, best practices

Examples:

  • database-crud.md — SQLite CRUD patterns
  • file-uploads.md — streaming file handling

Expand your agent's capabilities with these related and highly-rated skills.

outfitter-dev/agents

stack-feedback

Creates GitHub issues for problems discovered while using @outfitter/* packages. Use when finding bugs, missing features, unclear documentation, or improvement opportunities.

26 0
Explore
outfitter-dev/agents

stack-architecture

Design stack-based systems using @outfitter/* packages. Use when planning new projects, choosing packages, designing handler architecture, or when "architecture", "design", "structure", "plan handlers", or "error taxonomy" are mentioned.

26 0
Explore
outfitter-dev/agents

stack-templates

Templates for creating handlers, CLI commands, MCP tools, and daemon services following Outfitter Stack conventions. Use when scaffolding new components, creating handlers, adding commands, or when "create handler", "new command", "add tool", "scaffold", "template", or "daemon service" are mentioned.

26 0
Explore
outfitter-dev/agents

stack-audit

Scan codebase for Outfitter Stack adoption candidates. Identifies throw statements, console usage, hardcoded paths, and custom errors. Use when assessing adoption scope or checking readiness.

26 0
Explore
outfitter-dev/agents

stack-review

Audits code for Outfitter Stack compliance including Result types, error handling, logging patterns, and path safety. Use for pre-commit reviews, code quality checks, migration validation, or when "audit", "check compliance", "review stack", or "stack patterns" are mentioned.

26 0
Explore
outfitter-dev/agents

stack-patterns

Reference for Outfitter Stack patterns including Result types, Handler contract, Error taxonomy, and @outfitter/* package conventions. Use when learning the stack, looking up patterns, understanding packages, or when "Result", "Handler", "error taxonomy", "OutfitterError", "CLI output", "pagination", "MCP server", "MCP tool", "structured logging", "redaction", "test handler", "daemon", "IPC", or "@outfitter/*" are mentioned.

26 0
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results