AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL unreviewed MIT Self-run

Bun

skill-nklisch-skilltap-bun · by nklisch

Reference for Bun runtime APIs used in this project. Use this skill whenever writing code that runs shell commands (Bun.$ or Bun.spawn), reading/writing files (Bun.file/Bun.write), running tests (bun test), or configuring the monorepo (workspaces, bunfig.toml). Prefer Bun APIs over Node.js equivalents — use Bun.$ instead of child_process, Bun.file() instead of fs.readFile, bun:test instead of jes…

No reviews yet
0 installs
27 views
0.0% view→install

Install

$ agentstack add skill-nklisch-skilltap-bun

Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Destructive filesystem operation.

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Reliability & compatibility

Not yet reviewed
0 installs to date
no reviews yet
4mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Bun? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Bun Runtime Reference

This project uses Bun as its runtime. Prefer Bun-native APIs over Node.js equivalents.

Bun.$ — Shell API

The primary way to run external commands. Cross-platform, auto-escapes interpolated values, throws on non-zero exit by default.

import { $ } from "bun"

Running commands

// Simple command — prints to stdout by default
await $`git clone --depth 1 ${url} ${dest}`

// Capture output as string (.text() auto-quiets)
const sha = await $`git rev-parse HEAD`.cwd(dir).text()
// sha === "\n"

// Capture output as trimmed string
const sha = (await $`git rev-parse HEAD`.cwd(dir).text()).trim()

// Capture as JSON
const result = await $`echo '{"ok": true}'`.json()

// Read output line-by-line
for await (const line of $`git log --oneline -5`.cwd(dir).lines()) {
  console.log(line)
}

// Capture stdout and stderr as Buffers
const { stdout, stderr } = await $`git status`.cwd(dir).quiet()

Error handling

Non-zero exit codes throw ShellError by default:

try {
  await $`git clone ${url} ${dest}`
} catch (err) {
  console.error(`Exit code: ${err.exitCode}`)
  console.error(err.stderr.toString())
}

Use .nothrow() to handle exit codes manually:

const { exitCode, stdout, stderr } = await $`git diff --quiet`.cwd(dir).nothrow().quiet()
if (exitCode !== 0) {
  // There are unstaged changes
}

Setting cwd and env

// Per-command
await $`git status`.cwd("/path/to/repo")
await $`echo $MY_VAR`.env({ ...process.env, MY_VAR: "value" })

// Global defaults
$.cwd("/default/dir")
$.env({ ...process.env, GIT_TERMINAL_PROMPT: "0" })

Piping and redirection

// Pipe between commands
const count = await $`git log --oneline | wc -l`.cwd(dir).text()

// Redirect to file
await $`git diff > ${Bun.file("changes.patch")}`

// Redirect stderr to stdout
const output = await $`git clone ${url} 2>&1`.text()

Interpolation safety

All interpolated values are auto-escaped — no shell injection:

const userInput = "foo; rm -rf /"
await $`echo ${userInput}`  // SAFE: treated as single argument

To pass raw (unescaped) strings:

await $`echo ${{ raw: "$(date)" }}`  // Executes command substitution

Pattern: skilltap git.ts module

import { $ } from "bun"
import type { Result } from "./types"

export async function clone(
  url: string,
  dest: string,
  opts?: { depth?: number; branch?: string }
): Promise> {
  try {
    const args = ["git", "clone"]
    if (opts?.depth) args.push("--depth", String(opts.depth))
    if (opts?.branch) args.push("--branch", opts.branch)
    args.push(url, dest)

    await $`${args}`.quiet()
    return { ok: true, value: undefined }
  } catch (err) {
    return {
      ok: false,
      error: new GitError(err.stderr?.toString() ?? err.message),
    }
  }
}

export async function revParse(dir: string): Promise> {
  try {
    const sha = (await $`git rev-parse HEAD`.cwd(dir).text()).trim()
    return { ok: true, value: sha }
  } catch (err) {
    return { ok: false, error: new GitError(err.message) }
  }
}

Bun.spawn — Subprocess API

Lower-level than $. Use when you need fine-grained control over stdin/stdout streams, IPC, or synchronous execution.

// Async
const proc = Bun.spawn(["git", "clone", url, dest], {
  cwd: "/tmp",
  env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
  stdout: "pipe",   // capture stdout as ReadableStream
  stderr: "pipe",   // capture stderr as ReadableStream
})

const exitCode = await proc.exited
const stdout = await proc.stdout.text()

// Sync (blocking — good for CLI tools)
const result = Bun.spawnSync(["git", "rev-parse", "HEAD"], { cwd: dir })
if (result.success) {
  console.log(result.stdout.toString().trim())
}

When to use Bun.$ vs Bun.spawn

| Use case | API | |----------|-----| | Running git commands, shell scripts | Bun.$ | | Piping between commands | Bun.$ | | Simple command + capture output | Bun.$ | | Streaming stdin/stdout | Bun.spawn | | IPC with child process | Bun.spawn | | Sync execution (CLI tools) | Bun.spawnSync | | Invoking agent CLIs for semantic scan | Bun.spawn (need stdout stream) |

Bun.file / Bun.write — File I/O

Faster than Node's fs module. Returns a BunFile (lazy, doesn't read until consumed).

// Read file as string
const content = await Bun.file("config.toml").text()

// Read as JSON
const data = await Bun.file("installed.json").json()

// Read as ArrayBuffer
const buf = await Bun.file("binary.dat").arrayBuffer()

// Check if file exists
const exists = await Bun.file("config.toml").exists()

// Get file size
const size = Bun.file("config.toml").size  // bytes

// Write string to file
await Bun.write("config.toml", tomlString)

// Write from another BunFile (copy)
await Bun.write("dest.txt", Bun.file("src.txt"))

// Write JSON
await Bun.write("data.json", JSON.stringify(data, null, 2))

vs Node.js fs

| Node.js | Bun | |---------|-----| | fs.readFileSync(path, "utf-8") | await Bun.file(path).text() | | fs.writeFileSync(path, data) | await Bun.write(path, data) | | fs.existsSync(path) | await Bun.file(path).exists() | | fs.statSync(path).size | Bun.file(path).size |

Node's fs still works in Bun — use it when you need fs.mkdirSync, fs.readdirSync, symlink operations, etc. that Bun.file/Bun.write don't cover.

bun test — Test Runner

Jest-compatible test runner. Import from "bun:test".

import { describe, test, expect, beforeAll, afterAll, mock, spyOn } from "bun:test"

See references/testing.md for the full test API reference.

Quick patterns

import { describe, test, expect, beforeAll, afterAll } from "bun:test"

describe("scanner", () => {
  let tmpDir: string

  beforeAll(async () => {
    tmpDir = await createFixtureRepo()
  })

  afterAll(async () => {
    await fs.rm(tmpDir, { recursive: true })
  })

  test("finds root SKILL.md", async () => {
    const skills = await scanForSkills(tmpDir)
    expect(skills).toHaveLength(1)
    expect(skills[0].name).toBe("test-skill")
  })

  test("validates frontmatter", async () => {
    const skills = await scanForSkills(tmpDir)
    expect(skills[0].valid).toBe(true)
    expect(skills[0].description).toMatch(/test/)
  })

  test.todo("handles deep scan with confirmation")
})

Running tests

bun test                          # Run all tests
bun test scanner                  # Filter by filename
bun test -t "validates"           # Filter by test name
bun test --watch                  # Watch mode
bun test --timeout 10000          # 10s per-test timeout
bun test --bail                   # Stop on first failure

Workspaces

Bun uses the standard package.json workspaces field. Same as npm/yarn.

// Root package.json
{
  "name": "skilltap-monorepo",
  "private": true,
  "workspaces": ["packages/*"]
}

Reference workspace packages with workspace:*:

// packages/cli/package.json
{
  "name": "skilltap",
  "dependencies": {
    "@skilltap/core": "workspace:*"
  },
  "devDependencies": {
    "@skilltap/test-utils": "workspace:*"
  }
}

Import workspace packages normally:

import { installSkill } from "@skilltap/core"

bunfig.toml

Bun-specific configuration. Lives at project root.

[install]
# Use exact versions by default
exact = true

[test]
# Preload files before tests
preload = ["./packages/test-utils/src/setup.ts"]
# Default timeout
timeout = 10000

bun build --compile

Compile to a standalone binary with no runtime dependency:

bun build --compile packages/cli/src/index.ts --outfile skilltap

The binary includes the Bun runtime — runs on machines without Bun installed.

Cross-compile targets

bun build --compile --target=bun-linux-x64 packages/cli/src/index.ts --outfile skilltap-linux
bun build --compile --target=bun-darwin-arm64 packages/cli/src/index.ts --outfile skilltap-macos

Other Bun APIs

Hashing

const hash = Bun.hash("some string")              // fast non-crypto hash
const sha = new Bun.CryptoHasher("sha256")
  .update("content")
  .digest("hex")

Temporary files

const tmpDir = `${import.meta.dir}/../.tmp/${crypto.randomUUID()}`
await fs.mkdir(tmpDir, { recursive: true })
// ... use tmpDir ...
await fs.rm(tmpDir, { recursive: true })

Path utilities

import { join, resolve, dirname, basename } from "node:path"
// Node path utilities work perfectly in Bun

Environment

Bun.env.HOME           // same as process.env.HOME
Bun.env.XDG_CONFIG_HOME // standard config path

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.