Dropstone Docs

插件

编写自己的插件来扩展 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 并将其添加到配置


加载顺序

插件从所有源加载,所有钩子按顺序运行。加载顺序为:

  1. 全局配置(~/.config/dropstone/dropstone.json
  2. 项目配置(dropstone.json
  3. 全局插件目录(~/.config/dropstone/plugins/
  4. 项目插件目录(.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.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 事件

  • shell.env

工具事件

  • tool.execute.after
  • tool.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 schema
  • execute:调用工具时运行的函数

你的自定义工具将与内置工具一起可用于 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" },
    },
  })
}

级别:debuginfowarnerror。详见 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 数组被忽略。

Ctrl+I