插件
编写自己的插件来扩展 Dropstone。
插件允许你通过挂接各种事件和自定义行为来扩展 Dropstone。你可以创建插件来添加新功能、与外部服务集成或修改 Dropstone 的默认行为。
使用插件
有两种方式加载插件。
从本地文件
将 JavaScript 或 TypeScript 文件放在插件目录中。
.dropstone/plugins/- 项目级插件~/.config/dropstone/plugins/- 全局插件
这些目录中的文件会在启动时自动加载。
从 npm
在配置文件中指定 npm 包。
{
"$schema": "https://dropstone.io/schema/config.json",
"plugin": ["@my-org/internal-plugin", "dropstone-notify-on-idle"]
}
支持常规和作用域 npm 包。
插件如何安装
npm 插件在启动时使用 Bun 自动安装。包及其依赖项缓存在 ~/.cache/dropstone/node_modules/ 中。
本地插件直接从插件目录加载。要使用外部包,你必须在配置目录中创建 package.json(参见依赖项),或将插件发布到 npm 并将其添加到配置。
加载顺序
插件从所有源加载,所有钩子按顺序运行。加载顺序为:
- 全局配置(
~/.config/dropstone/dropstone.json) - 项目配置(
dropstone.json) - 全局插件目录(
~/.config/dropstone/plugins/) - 项目插件目录(
.dropstone/plugins/)
具有相同名称和版本的重复 npm 包只加载一次。但是,本地插件和名称相似的 npm 插件会分别加载。
创建插件
插件是一个JavaScript/TypeScript 模块,导出一个或多个插件函数。每个函数接收一个上下文对象并返回一个钩子对象。
依赖项
本地插件和自定义工具可以使用外部 npm 包。将 package.json 添加到配置目录中,包含所需的依赖项。
{
"dependencies": {
"shescape": "^2.1.0"
}
}
Dropstone 在启动时运行 bun install 来安装这些包。你的插件和工具可以导入它们。
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)
}
},
}
}
基本结构
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
console.log("Plugin initialized!")
return {
// Hook implementations go here
}
}
插件函数接收:
project:当前项目信息。directory:当前工作目录。worktree:git worktree 路径。client:Dropstone SDK 客户端,用于与代理交互。$:Bun 的shell API,用于执行命令。
TypeScript 支持
对于 TypeScript 插件,你可以从插件包导入类型:
import type { Plugin } from "@blankline/dropstone-plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
// Type-safe hook implementations
}
}
事件
插件可以订阅事件,如下面的示例部分所示。以下是可用的不同事件列表。
命令事件
command.executed
文件事件
file.editedfile.watcher.updated
安装事件
installation.updated
LSP 事件
lsp.client.diagnosticslsp.updated
消息事件
message.part.removedmessage.part.updatedmessage.removedmessage.updated
权限事件
permission.askedpermission.replied
服务器事件
server.connected
会话事件
session.createdsession.compactedsession.deletedsession.diffsession.errorsession.idlesession.statussession.updated
Todo 事件
todo.updated
Shell 事件
shell.env
工具事件
tool.execute.aftertool.execute.before
示例
以下是一些可用于扩展 dropstone 的插件示例。
发送通知
在某些事件发生时发送通知:
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 "dropstone"'`
}
},
}
}
我们使用 osascript 在 macOS 上运行 AppleScript。这里我们用它来发送通知。
.env 保护
防止 dropstone 读取 .env 文件:
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")
}
},
}
}
注入环境变量
将环境变量注入到所有 shell 执行中(AI 工具和用户终端):
export const InjectEnvPlugin = async () => {
return {
"shell.env": async (input, output) => {
output.env.MY_API_KEY = "secret"
output.env.PROJECT_ROOT = input.cwd
},
}
}
自定义工具
插件也可以向 dropstone 添加自定义工具:
import { type Plugin, tool } from "@blankline/dropstone-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 辅助函数创建一个 dropstone 可以调用的自定义工具。它接收一个 Zod schema 函数并返回一个工具定义,包含:
description:工具的功能args:工具参数的 Zod schemaexecute:调用工具时运行的函数
你的自定义工具将与内置工具一起可用于 dropstone。
Note:
如果插件工具与内置工具同名,插件工具优先。
日志记录
使用 client.app.log() 而不是 console.log 进行结构化日志记录:
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 参考。
压缩钩子
自定义会话压缩时包含的上下文:
import type { Plugin } from "@blankline/dropstone-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 来完全替换压缩提示:
import type { Plugin } from "@blankline/dropstone-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 long-running coding session.
Summarize:
1. The current task and its status
2. Which files are being modified
3. Any blockers or open questions
4. The next steps to complete the work
Format as a structured prompt the next session can use to resume work.
`
},
}
}
当设置 output.prompt 时,它完全替换默认压缩提示。在这种情况下,output.context 数组被忽略。