opencode / packages /web /src /content /docs /ko /plugins.mdx
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
0dbc9de verified
Raw
History Blame Contribute Delete
10.5 kB
---
title: ํ”Œ๋Ÿฌ๊ทธ์ธ
description: OpenCode๋ฅผ ํ™•์žฅํ•˜๊ธฐ ์œ„ํ•ด ์ž์‹ ๋งŒ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ž‘์„ฑํ•˜์„ธ์š”.
---
ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ์™€ ์‚ฌ์šฉ์ž ์ •์˜ ํ–‰๋™์œผ๋กœ ํ›„ํ‚นํ•˜์—ฌ opencode๋ฅผ ํ™•์žฅ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์™ธ๋ถ€ ์„œ๋น„์Šค์™€ ํ†ตํ•ฉํ•˜๊ฑฐ๋‚˜, opencode์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์˜ํ•ด ์ƒ์„ฑ ๋œ [plugins](/docs/ecosystem#plugins)๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.
---
## ํ”Œ๋Ÿฌ๊ทธ์ธ ์‚ฌ์šฉ
ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋กœ๋“œํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.
---
### ๋กœ์ปฌ ํŒŒ์ผ์—์„œ
ํ”Œ๋Ÿฌ๊ทธ์ธ ๋””๋ ‰ํ† ๋ฆฌ์— JavaScript ๋˜๋Š” TypeScript ํŒŒ์ผ์„ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
- `.opencode/plugins/` - ํ”„๋กœ์ ํŠธ ๋ ˆ๋ฒจ ํ”Œ๋Ÿฌ๊ทธ์ธ
- `~/.config/opencode/plugins/` - ๊ธ€๋กœ๋ฒŒ ํ”Œ๋Ÿฌ๊ทธ์ธ
์ด ๋””๋ ‰ํ† ๋ฆฌ์˜ ํŒŒ์ผ์€ ์‹œ์ž‘ ์‹œ ์ž๋™์œผ๋กœ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.
---
### npm์—์„œ
config ํŒŒ์ผ์— npm ํŒจํ‚ค์ง€๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
}
```
์ผ๋ฐ˜ ํŒจํ‚ค์ง€ ๋ฐ ์Šค์ฝ”ํ”„ npm ํŒจํ‚ค์ง€ ๋ชจ๋‘ ์ง€์›๋ฉ๋‹ˆ๋‹ค.
[ecosystem](/docs/ecosystem#plugins)์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ฐพ์•„๋ณด์„ธ์š”.
---
### ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์„ค์น˜๋˜๋Š” ๋ฐฉ๋ฒ•
**npm ํ”Œ๋Ÿฌ๊ทธ์ธ**์€ ์‹œ์ž‘์‹œ Bun์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค. ํŒจํ‚ค์ง€์™€ ๊ทธ ์˜์กด์„ฑ์€ `~/.cache/opencode/node_modules/`์— ์บ์‹œ๋ฉ๋‹ˆ๋‹ค.
**๋กœ์ปฌ ํ”Œ๋Ÿฌ๊ทธ์ธ**์€ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์ง์ ‘ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ตฌ์„ฑ ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด์— `package.json`์„ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜([์˜์กด์„ฑ](#์˜์กด์„ฑ) ์ฐธ์กฐ), ํ”Œ๋Ÿฌ๊ทธ์ธ์„ npm์— ๊ฒŒ์‹œํ•˜๊ณ  [config์— ์ถ”๊ฐ€](/docs/config#plugins)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
---
### ๋กœ๋“œ ์ˆœ์„œ
ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋ชจ๋“  ์†Œ์Šค์—์„œ ๋กœ๋“œ๋˜๋ฉฐ ๋ชจ๋“  ํ›„ํฌ๋Š” ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋กœ๋“œ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:
1. ๊ธ€๋กœ๋ฒŒ ๊ตฌ์„ฑ (`~/.config/opencode/opencode.json`)
2. ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ (`opencode.json`)
3. ๊ธ€๋กœ๋ฒŒ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋””๋ ‰ํ† ๋ฆฌ (`~/.config/opencode/plugins/`)
4. ํ”„๋กœ์ ํŠธ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋””๋ ‰ํ† ๋ฆฌ (`.opencode/plugins/`)
์ค‘๋ณต๋œ ์ด๋ฆ„๊ณผ ๋ฒ„์ „์˜ npm ํŒจํ‚ค์ง€๋Š” ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋กœ์ปฌ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ npm ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ด๋ฆ„์ด ๋น„์Šทํ•˜๋”๋ผ๋„ ๋‘˜ ๋‹ค ๋ณ„๋„๋กœ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.
---
## ํ”Œ๋Ÿฌ๊ทธ์ธ ๋งŒ๋“ค๊ธฐ
ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํ•˜๋‚˜ ์ด์ƒ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•จ์ˆ˜๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” **JavaScript/TypeScript ๋ชจ๋“ˆ**์ž…๋‹ˆ๋‹ค. ๊ฐ ํ•จ์ˆ˜๋Š” context ๊ฐ์ฒด๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ  hooks ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
---
### ์˜์กด์„ฑ
๋กœ์ปฌ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฐ ์‚ฌ์šฉ์ž ์ •์˜ ๋„๊ตฌ๋Š” ์™ธ๋ถ€ npm ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. config ๋””๋ ‰ํ† ๋ฆฌ์— `package.json`์„ ์ถ”๊ฐ€ํ•˜๊ณ  ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ๋ช…์‹œํ•˜์‹ญ์‹œ์˜ค.
```json title=".opencode/package.json"
{
"dependencies": {
"shescape": "^2.1.0"
}
}
```
opencode๋Š” ์‹œ์ž‘ ์‹œ `bun install`์„ ์‹คํ–‰ํ•˜์—ฌ ์ด๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฐ ๋„๊ตฌ์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
```ts title=".opencode/plugins/my-plugin.ts"
import { escape } from "shescape"
export const MyPlugin = async (ctx) => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool === "bash") {
output.args.command = escape(output.args.command)
}
},
}
}
```
---
### ๊ธฐ๋ณธ ๊ตฌ์กฐ
```js title=".opencode/plugins/example.js"
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
console.log("Plugin initialized!")
return {
// Hook implementations go here
}
}
```
ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•จ์ˆ˜๊ฐ€ ๋ฐ›๋Š” ์ธ์ž:
- `project`: ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ์ •๋ณด.
- `directory`: ํ˜„์žฌ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ.
- `worktree`: git worktree ๊ฒฝ๋กœ.
- `client`: AI์™€ ์ƒํ˜ธ ์ž‘์šฉ์„ ์œ„ํ•œ opencode SDK ํด๋ผ์ด์–ธํŠธ.
- `$`: ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•œ Bun์˜ [shell API](https://bun.com/docs/runtime/shell).
---
### TypeScript ์ง€์›
TypeScript ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๊ฒฝ์šฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ํŒจํ‚ค์ง€์—์„œ ํƒ€์ž…์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
```ts title="my-plugin.ts" {1}
import type { Plugin } from "@opencode-ai/plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
// Type-safe hook implementations
}
}
```
---
### ์ด๋ฒคํŠธ
ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์•„๋ž˜ ์˜ˆ์ œ ์„น์…˜์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ด๋ฒคํŠธ ๋ชฉ๋ก์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
#### ๋ช…๋ น์–ด ์ด๋ฒคํŠธ
- `command.executed`
#### ํŒŒ์ผ ์ด๋ฒคํŠธ
- `file.edited`
- `file.watcher.updated`
#### ์„ค์น˜ ์ด๋ฒคํŠธ
- `installation.updated`
#### LSP ์ด๋ฒคํŠธ
- `lsp.client.diagnostics`
- `lsp.updated`
#### ๋ฉ”์‹œ์ง€ ์ด๋ฒคํŠธ
- `message.part.removed`
- `message.part.updated`
- `message.removed`
- `message.updated`
#### ๊ถŒํ•œ ์ด๋ฒคํŠธ
- `permission.asked`
- `permission.replied`
#### ์„œ๋ฒ„ ์ด๋ฒคํŠธ
- `server.connected`
#### ์„ธ์…˜ ์ด๋ฒคํŠธ
- `session.created`
- `session.compacted`
- `session.deleted`
- `session.diff`
- `session.error`
- `session.idle`
- `session.status`
- `session.updated`
#### Todo ์ด๋ฒคํŠธ
- `todo.updated`
#### ์…ธ ์ด๋ฒคํŠธ
- `shell.env`
#### ๋„๊ตฌ ์ด๋ฒคํŠธ
- `tool.execute.after`
- `tool.execute.before`
#### TUI ์ด๋ฒคํŠธ
- `tui.prompt.append`
- `tui.command.execute`
- `tui.toast.show`
---
## ์˜ˆ์ œ
opencode๋ฅผ ํ™•์žฅํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.
---
### ์•Œ๋ฆผ ๋ณด๋‚ด๊ธฐ
ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์•Œ๋ฆผ์„ ์ „์†ก:
```js title=".opencode/plugins/notification.js"
export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => {
return {
event: async ({ event }) => {
// Send notification on session completion
if (event.type === "session.idle") {
await $`osascript -e 'display notification "Session completed!" with title "opencode"'`
}
},
}
}
```
macOS์—์„œ AppleScript๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด `osascript`๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๋ฐ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
:::note
opencode ๋ฐ์Šคํฌํ†ฑ ์•ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์‘๋‹ต์ด ์ค€๋น„๋˜์–ด ์žˆ๊ฑฐ๋‚˜ ์„ธ์…˜ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์„ ๋•Œ ์‹œ์Šคํ…œ ์•Œ๋ฆผ์„ ์ž๋™์œผ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
:::
---
### .env ๋ณดํ˜ธ
opencode๊ฐ€ `.env` ํŒŒ์ผ์„ ์ฝ์ง€ ๋ชปํ•˜๋„๋ก ๋ฐฉ์ง€:
```javascript title=".opencode/plugins/env-protection.js"
export const EnvProtection = async ({ project, client, $, directory, worktree }) => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool === "read" && output.args.filePath.includes(".env")) {
throw new Error("Do not read .env files")
}
},
}
}
```
---
### Inject ํ™˜๊ฒฝ ๋ณ€์ˆ˜
๋ชจ๋“  shell ์‹คํ–‰(AI ๋„๊ตฌ ๋ฐ ์‚ฌ์šฉ์ž terminal)์— ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ฃผ์ž…:
```javascript title=".opencode/plugins/inject-env.js"
export const InjectEnvPlugin = async () => {
return {
"shell.env": async (input, output) => {
output.env.MY_API_KEY = "secret"
output.env.PROJECT_ROOT = input.cwd
},
}
}
```
---
### ์‚ฌ์šฉ์ž ์ •์˜ ๋„๊ตฌ
ํ”Œ๋Ÿฌ๊ทธ์ธ์€ opencode์— ์‚ฌ์šฉ์ž ์ •์˜ ๋„๊ตฌ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
```ts title=".opencode/plugins/custom-tools.ts"
import { type Plugin, tool } from "@opencode-ai/plugin"
export const CustomToolsPlugin: Plugin = async (ctx) => {
return {
tool: {
mytool: tool({
description: "This is a custom tool",
args: {
foo: tool.schema.string(),
},
async execute(args, context) {
const { directory, worktree } = context
return `Hello ${args.foo} from ${directory} (worktree: ${worktree})`
},
}),
},
}
}
```
`tool` helper๋Š” opencode๊ฐ€ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. Zod ์Šคํ‚ค๋งˆ ํ•จ์ˆ˜๋ฅผ ๋ฐ›๊ณ  ๋‹ค์Œ์„ ํฌํ•จํ•˜๋Š” ๋„๊ตฌ ์ •์˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:
- `description`: ๋„๊ตฌ๊ฐ€ ํ•˜๋Š” ์ผ
- `args`: ๋„๊ตฌ์˜ ์ธ์ˆ˜์— ๋Œ€ํ•œ Zod ์Šคํ‚ค๋งˆ
- `execute`: ๋„๊ตฌ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
์‚ฌ์šฉ์ž ์ •์˜ ๋„๊ตฌ๋Š” ๋‚ด์žฅ ๋„๊ตฌ์™€ ํ•จ๊ป˜ opencode์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
:::note
ํ”Œ๋Ÿฌ๊ทธ์ธ ๋„๊ตฌ๊ฐ€ ๋‚ด์žฅ ๋„๊ตฌ์™€ ๊ฐ™์€ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ ๋„๊ตฌ๊ฐ€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค.
:::
---
### ๋กœ๊น…
๊ตฌ์กฐํ™”๋œ ๋กœ๊น…์„ ์œ„ํ•ด `console.log` ๋Œ€์‹  `client.app.log()`๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค:
```ts title=".opencode/plugins/my-plugin.ts"
export const MyPlugin = async ({ client }) => {
await client.app.log({
body: {
service: "my-plugin",
level: "info",
message: "Plugin initialized",
extra: { foo: "bar" },
},
})
}
```
๋ ˆ๋ฒจ: `debug`, `info`, `warn`, `error`. [SDK ๋ฌธ์„œ](https://opencode.ai/docs/sdk)๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.
---
### Compaction ํ›…
์„ธ์…˜์ด ์••์ถ•๋  ๋•Œ ํฌํ•จ๋˜๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉ์ž ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
```ts title=".opencode/plugins/compaction.ts"
import type { Plugin } from "@opencode-ai/plugin"
export const CompactionPlugin: Plugin = async (ctx) => {
return {
"experimental.session.compacting": async (input, output) => {
// Inject additional context into the compaction prompt
output.context.push(`
## Custom Context
Include any state that should persist across compaction:
- Current task status
- Important decisions made
- Files being actively worked on
`)
},
}
}
```
`experimental.session.compacting` ํ›…์€ LLM์ด ์—ฐ์† ์š”์•ฝ์„ ์ƒ์„ฑํ•˜๊ธฐ ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์••์ถ• ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋†“์น  ์ˆ˜ ์žˆ๋Š” ๋„๋ฉ”์ธ๋ณ„ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜์„ธ์š”.
๋˜ํ•œ `output.prompt`๋ฅผ ์„ค์ •ํ•˜์—ฌ ์••์ถ• ํ”„๋กฌํ”„ํŠธ๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:
```ts title=".opencode/plugins/custom-compaction.ts"
import type { Plugin } from "@opencode-ai/plugin"
export const CustomCompactionPlugin: Plugin = async (ctx) => {
return {
"experimental.session.compacting": async (input, output) => {
// Replace the entire compaction prompt
output.prompt = `
You are generating a continuation prompt for a multi-agent swarm session.
Summarize:
1. The current task and its status
2. Which files are being modified and by whom
3. Any blockers or dependencies between agents
4. The next steps to complete the work
Format as a structured prompt that a new agent can use to resume work.
`
},
}
}
```
`output.prompt`๊ฐ€ ์„ค์ •๋˜๋ฉด ๊ธฐ๋ณธ ์••์ถ• ํ”„๋กฌํ”„ํŠธ๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ `output.context` ๋ฐฐ์—ด์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.