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

Opencode Builder

skill-timmy6942025-opencode-builder-skill-opencode-builder · by Timmy6942025

|

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

Install

$ agentstack add skill-timmy6942025-opencode-builder-skill-opencode-builder

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

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

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 →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-timmy6942025-opencode-builder-skill-opencode-builder)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo 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 Opencode Builder? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

OpenCode Builder

> 📚 Official Docs: For the latest information, always refer to the official documentation: > For the latest SDK docs: https://opencode.ai/docs/sdk/. For plugins: https://opencode.ai/docs/plugins/.

A comprehensive skill for building on top of OpenCode — an AI-powered coding assistant. This skill covers both the JavaScript/TypeScript SDK for programmatic control and the plugin architecture for extending OpenCode's behavior.


Table of Contents

  1. [Quick Start: Choose Your Path](#quick-start-choose-your-path)
  2. [SDK Path: Programmatic Control](#sdk-path-programmatic-control)
  3. [Plugin Path: Extending OpenCode](#plugin-path-extending-opencode)
  4. [Configuration Deep-Dive](#configuration-deep-dive)
  5. [Common Patterns](#common-patterns)
  6. [Advanced Orchestration](#advanced-orchestration)
  7. [Agent Skills](#agent-skills)
  8. [Custom Commands](#custom-commands)
  9. [Debugging & Troubleshooting](#debugging--troubleshooting)
  10. [Best Practices](#best-practices)

Quick Start: Choose Your Path

OpenCode offers two primary extension surfaces:

| Path | Use When | Key Packages | |------|----------|-------------| | SDK (@opencode-ai/sdk) | You want to control OpenCode from an external app, script, or service. Build integrations, automate workflows, or create UIs that interact with OpenCode. | @opencode-ai/sdk | | Plugin (@opencode-ai/plugin) | You want to run code inside OpenCode, hook into its lifecycle, add custom AI tools, or modify behavior. | @opencode-ai/plugin |

You can use both in the same project. A plugin can internally use the SDK client to call back into OpenCode.

Decision Table

| Scenario | Recommended Path | |----------|-----------------| | Build a CI/CD integration | SDK | | Add a custom tool the AI can call | Plugin | | Create a webhook listener that triggers sessions | SDK | | Intercept and modify tool calls | Plugin | | Build a dashboard showing session status | SDK | | Protect sensitive files from being read | Plugin | | Automate multi-agent workflows | SDK + Plugin | | Add notifications on session completion | Plugin | | Create an external UI for OpenCode | SDK | | Inject environment variables into all shell commands | Plugin |


SDK Path: Programmatic Control

Installation

npm install @opencode-ai/sdk
# or
bun add @opencode-ai/sdk

Two Client Modes

Mode 1: Full Lifecycle — starts a local server + client

import { createOpencode } from "@opencode-ai/sdk"

const { client, server } = await createOpencode({
  hostname: "127.0.0.1",
  port: 4096,
  timeout: 5000,          // ms to wait for server start
  config: {               // overrides / merges with opencode.json
    model: "anthropic/claude-sonnet-4-5",
  },
})

// Use client...
await server.close()

Mode 2: Client Only — connects to an already-running OpenCode server

import { createOpencodeClient } from "@opencode-ai/sdk"

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  throwOnError: false,     // return error objects instead of throwing
  responseStyle: "fields", // or "data"
})

Response Styles

  • "fields" (default) — Each field is a separate result property
  • "data" — Nested data object

Error Handling

// With throwOnError: false (default)
const result = await client.session.create({ body: {} })
if (result.error) {
  console.error("Failed:", result.error)
}

// With throwOnError: true
const session = await client.session.create({
  body: {},
  throwOnError: true, // throws on error
})

Core Workflows

1. Session Management

Sessions are the primary unit of interaction in OpenCode.

// Create a session
const session = await client.session.create({
  body: { title: "My automation task" },
})

// Initialize (analyzes project, creates AGENTS.md context)
await client.session.init({ path: { id: session.id } })

// Send a prompt and get AI response
const result = await client.session.prompt({
  path: { id: session.id },
  body: {
    parts: [{ type: "text", text: "Refactor the auth module" }],
  },
})
// result.data.info — assistant message metadata
// result.data.parts — message parts (text, tool calls, etc.)

// Inject context WITHOUT triggering a response
await client.session.prompt({
  path: { id: session.id },
  body: {
    noReply: true,
    parts: [{ type: "text", text: "You are working in a Next.js project." }],
  },
})

// Run a shell command through the session
const shellResult = await client.session.shell({
  path: { id: session.id },
  body: { command: "npm test" },
})

// List all messages
const messages = await client.session.messages({ path: { id: session.id } })

// Get session children (subagent sessions)
const children = await client.session.children({ path: { id: session.id } })

// Get todo list
const todos = await client.session.todo({ path: { id: session.id } })

// Abort a running session
await client.session.abort({ path: { id: session.id } })

// Fork a session
const forked = await client.session.fork({
  path: { id: session.id },
  body: { messageID: "optional-msg-id" },
})

// Share / unshare
await client.session.share({ path: { id: session.id } })
await client.session.unshare({ path: { id: session.id } })

// Get diff
const diff = await client.session.diff({ path: { id: session.id } })

// Summarize session
await client.session.summarize({
  path: { id: session.id },
  body: { providerID: "anthropic", modelID: "claude-sonnet-4-5" },
})

// Revert a message
await client.session.revert({
  path: { id: session.id },
  body: { messageID: "msg-123" },
})

// Delete session
await client.session.delete({ path: { id: session.id } })
2. Structured Output

Request validated JSON from the model using JSON Schema:

const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Analyze this codebase" }],
    format: {
      type: "json_schema",
      schema: {
        type: "object",
        properties: {
          summary: { type: "string" },
          techStack: { type: "array", items: { type: "string" } },
          entryPoints: { type: "array", items: { type: "string" } },
        },
        required: ["summary", "techStack"],
      },
      retryCount: 2,
    },
  },
})

console.log(result.data.info.structured_output)
// { summary: "...", techStack: ["React", "TypeScript"], entryPoints: [...] }

if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("Failed after", result.data.info.error.retries, "attempts")
}

Best practices:

  • Provide clear description fields on schema properties
  • Keep schemas focused; deeply nested schemas are harder to fill
  • Increase retryCount for complex schemas
  • Always handle StructuredOutputError gracefully
3. File Operations
// Read a file
const file = await client.file.read({ query: { path: "src/index.ts" } })

// Search for text across files
const matches = await client.find.text({
  query: { pattern: "function.*handler" },
})

// Find files by name
const files = await client.find.files({
  query: { query: "*.ts", type: "file", limit: 50 },
})

// Find directories
const dirs = await client.find.files({
  query: { query: "components", type: "directory" },
})

// Find workspace symbols
const symbols = await client.find.symbols({
  query: { query: "UserService" },
})

// Get git status for tracked files
const status = await client.file.status()
4. TUI Control
await client.tui.appendPrompt({ body: { text: "npm install lodash" } })
await client.tui.submitPrompt()
await client.tui.clearPrompt()
await client.tui.showToast({
  body: { message: "Done!", variant: "success" },
})
await client.tui.openHelp()
await client.tui.openSessions()
await client.tui.openModels()
await client.tui.openThemes()
await client.tui.executeCommand({ body: { command: "/clear" } })
5. Event Streaming
const events = await client.event.subscribe()
for await (const event of events.stream) {
  console.log(event.type, event.properties)
  // Types: "session.updated", "message.part.updated", "tool.execute.after", etc.
}
6. Authentication
await client.auth.set({
  path: { id: "anthropic" },
  body: { type: "api", key: "sk-ant-api03-..." },
})
7. Logging
await client.app.log({
  body: {
    service: "my-integration",
    level: "info",
    message: "Deployment started",
    extra: { env: "production", version: "1.2.3" },
  },
})

SDK Type Safety

import type { Session, Message, Part, Config, Project } from "@opencode-ai/sdk"

Plugin Path: Extending OpenCode

Installation

npm install @opencode-ai/plugin
# or
bun add @opencode-ai/plugin

Plugin Basics

A plugin is a JS/TS module that exports a function receiving a context object and returning hooks:

import type { Plugin } from "@opencode-ai/plugin"

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  console.log("Plugin loaded for project:", project?.name)

  return {
    // Hook implementations go here
  }
}

Context object properties:

| Property | Type | Description | |----------|------|-------------| | project | Project \| null | Current project metadata | | directory | string | Current working directory | | worktree | string | Git worktree path | | client | OpencodeClient | SDK client for calling back into OpenCode | | $ | BunShell | Bun's shell API for executing commands |

Loading Plugins

1. Local files (auto-loaded at startup)

  • .opencode/plugins/ — project-level
  • ~/.config/opencode/plugins/ — global

2. npm packages (installed automatically via Bun)

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-helicone-session", "@my-org/custom-plugin"]
}

Load order: Global config → Project config → Global plugin dir → Project plugin dir

Dependencies for local plugins: Add a package.json to .opencode/:

{
  "dependencies": {
    "shescape": "^2.1.0"
  }
}

All Plugin Hooks

Tool Hooks
return {
  // BEFORE any tool executes — modify args or block actions
  "tool.execute.before": async (input, output) => {
    if (input.tool === "read" && output.args.filePath.includes(".env")) {
      throw new Error("Blocked: do not read .env files")
    }
    if (input.tool === "bash") {
      const { escape } = await import("shescape")
      output.args.command = escape(output.args.command)
    }
  },

  // AFTER tool execution — inspect or modify results
  "tool.execute.after": async (input, output) => {
    console.log(`Tool ${input.tool} finished:`, output.result)
  },
}
Shell Hooks
return {
  "shell.env": async (input, output) => {
    output.env.MY_API_KEY = process.env.MY_API_KEY
    output.env.PROJECT_ROOT = input.cwd
  },
}
Session Hooks
return {
  "session.created": async ({ event }) => {
    console.log("New session:", event.properties.sessionId)
  },
  "session.updated": async ({ event }) => {},
  "session.idle": async ({ event }) => {
    // AI finished responding — great for notifications
  },
  "session.error": async ({ event }) => {
    console.error("Session error:", event.properties.error)
  },
  "session.compacted": async ({ event }) => {},
  "session.deleted": async ({ event }) => {},
  "session.diff": async ({ event }) => {},
  "session.status": async ({ event }) => {},
}
Message Hooks
return {
  "message.updated": async ({ event }) => {},
  "message.removed": async ({ event }) => {},
  "message.part.updated": async ({ event }) => {},
  "message.part.removed": async ({ event }) => {},
}
TUI Hooks
return {
  "tui.prompt.append": async ({ event }) => {},
  "tui.command.execute": async ({ event }) => {},
  "tui.toast.show": async ({ event }) => {},
}
File Hooks
return {
  "file.edited": async ({ event }) => {},
  "file.watcher.updated": async ({ event }) => {},
}
Permission Hooks
return {
  "permission.asked": async ({ event }) => {},
  "permission.replied": async ({ event }) => {},
}
LSP Hooks
return {
  "lsp.client.diagnostics": async ({ event }) => {},
  "lsp.updated": async ({ event }) => {},
}
Other Hooks
return {
  "command.executed": async ({ event }) => {},
  "installation.updated": async ({ event }) => {},
  "todo.updated": async ({ event }) => {},
  "server.connected": async ({ event }) => {},
}
Compaction Hooks (Experimental)
return {
  "experimental.session.compacting": async (input, output) => {
    // Inject additional context
    output.context.push(`
## Project State
- Currently working on: auth refactor
- Important decisions: using JWT, not sessions
- Active files: src/auth.ts, src/middleware.ts
`)

    // OR replace the entire compaction prompt:
    output.prompt = `
You are generating a continuation prompt for a multi-agent session.
Summarize:
1. Current task and status
2. Files being modified
3. Blockers or dependencies
4. Next steps
`
  },
}

Custom Tools via Plugins

import { type Plugin, tool } from "@opencode-ai/plugin"

export const CustomToolsPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      deploy_to_vercel: tool({
        description: "Deploy the current project to Vercel",
        args: {
          preview: tool.schema.boolean().optional(),
          environment: tool.schema.enum(["production", "staging"]).optional(),
        },
        async execute(args, context) {
          const { directory, worktree } = context
          const { $ } = ctx
          const result = await $`cd ${directory} && vercel ${args.preview ? "--preview" : ""}`
          return `Deployed! Output: ${result.stdout}`
        },
      }),
    },
  }
}

Tool schema helpers:

  • tool.schema.string(), .number(), .boolean(), .enum([...]), .array(itemSchema)
  • .optional() — make any field optional
  • .describe("help text") — add field descriptions

Tool precedence: Plugin tools override built-in tools with the same name.

Standalone Custom Tools (.opencode/tools/)

You can also define custom tools as standalone TypeScript/JS files in .opencode/tools/. These are loaded automatically without needing a plugin wrapper:

.opencode/tools/
  my-tool.ts
  another-tool.js
// .opencode/tools/my-tool.ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "A standalone custom tool",
  args: {
    query: tool.schema.string().describe("Input query"),
  },
  async execute(args, context) {
    return `Result for: ${args.query}`
  },
})

Standalone tools are simpler for single-function tools where you don't need lifecycle hooks or plugin context. For more complex tools that need access to the plugin context ($, client, directory), use plugin-defined tools instead.

Event Hook Pattern

Generic event listener for any event type:

return {
  event: async ({ event }) => {
    if (event.type === "session.idle") {
      await ctx.client.app.log({
        body: { service: "my-plugin", level: "info", message: "Session completed" },
      })
    }
  },
}

Logging from Plugins

Always use client.app.log() instead of console.log:

await ctx.client.app.log({
  body: {
    service: "my-plugin",
    level: "info",
    message: "Plugin initialized",
    extra: { version: "1.0.0" },
  },
})

Levels: debug, info, warn, error


Configuration Deep-Dive

opencode.json Schema

{
  "$schema": "https://opencode.a

…

## Source & license

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

- **Author:** [Timmy6942025](https://github.com/Timmy6942025)
- **Source:** [Timmy6942025/opencode-builder-skill](https://github.com/Timmy6942025/opencode-builder-skill)
- **License:** MIT

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.