Dropstone Docs

Plugins

Escreva seus próprios plugins para estender o Dropstone.

Plugins permitem que você estenda o Dropstone conectando-se a vários eventos e personalizando o comportamento. Você pode criar plugins para adicionar novos recursos, integrar com serviços externos ou modificar o comportamento padrão do Dropstone.


Use um plugin

Existem duas maneiras de carregar plugins.


De arquivos locais

Coloque arquivos JavaScript ou TypeScript no diretório de plugins.

  • .dropstone/plugins/ - Plugins no nível do projeto
  • ~/.config/dropstone/plugins/ - Plugins globais

Os arquivos nesses diretórios são carregados automaticamente na inicialização.


Do npm

Especifique pacotes npm no seu arquivo de configuração.

{
  "$schema": "https://dropstone.io/schema/config.json",
  "plugin": ["@my-org/internal-plugin", "dropstone-notify-on-idle"]
}

Pacotes npm regulares e com escopo são suportados.


Como os plugins são instalados

Plugins npm são instalados automaticamente usando Bun na inicialização. Pacotes e suas dependências são armazenados em cache em ~/.cache/dropstone/node_modules/.

Plugins locais são carregados diretamente do diretório de plugins. Para usar pacotes externos, você deve criar um package.json dentro do seu diretório de configuração (veja Dependências), ou publicar o plugin no npm e adicioná-lo à sua configuração.


Ordem de carregamento

Plugins são carregados de todas as fontes e todos os hooks são executados em sequência. A ordem de carregamento é:

  1. Configuração global (~/.config/dropstone/dropstone.json)
  2. Configuração do projeto (dropstone.json)
  3. Diretório de plugins global (~/.config/dropstone/plugins/)
  4. Diretório de plugins do projeto (.dropstone/plugins/)

Pacotes npm duplicados com o mesmo nome e versão são carregados uma vez. No entanto, um plugin local e um plugin npm com nomes semelhantes são ambos carregados separadamente.


Crie um plugin

Um plugin é um módulo JavaScript/TypeScript que exporta uma ou mais funções de plugin. Cada função recebe um objeto de contexto e retorna um objeto de hooks.


Dependências

Plugins locais e ferramentas personalizadas podem usar pacotes npm externos. Adicione um package.json ao seu diretório de configuração com as dependências que você precisa.

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

O Dropstone executa bun install na inicialização para instalar essas dependências. Seus plugins e ferramentas podem então importá-las.

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)
      }
    },
  }
}

Estrutura básica

export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
  console.log("Plugin initialized!")

  return {
    // Hook implementations go here
  }
}

A função do plugin recebe:

  • project: As informações do projeto atual.
  • directory: O diretório de trabalho atual.
  • worktree: O caminho da worktree do git.
  • client: Um cliente SDK do Dropstone para interagir com o agente.
  • $: A shell API do Bun para executar comandos.

Suporte a TypeScript

Para plugins TypeScript, você pode importar tipos do pacote de plugin:

import type { Plugin } from "@blankline/dropstone-plugin"

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    // Type-safe hook implementations
  }
}

Eventos

Plugins podem se inscrever em eventos como visto abaixo na seção Exemplos. Aqui está uma lista dos diferentes eventos disponíveis.

Eventos de Comando

  • command.executed

Eventos de Arquivo

  • file.edited
  • file.watcher.updated

Eventos de Instalação

  • installation.updated

Eventos de LSP

  • lsp.client.diagnostics
  • lsp.updated

Eventos de Mensagem

  • message.part.removed
  • message.part.updated
  • message.removed
  • message.updated

Eventos de Permissão

  • permission.asked
  • permission.replied

Eventos de Servidor

  • server.connected

Eventos de Sessão

  • session.created
  • session.compacted
  • session.deleted
  • session.diff
  • session.error
  • session.idle
  • session.status
  • session.updated

Eventos de Todo

  • todo.updated

Eventos de Shell

  • shell.env

Eventos de Ferramenta

  • tool.execute.after
  • tool.execute.before

Exemplos

Aqui estão alguns exemplos de plugins que você pode usar para estender o dropstone.


Enviar notificações

Envie notificações quando certos eventos ocorrem:

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"'`
      }
    },
  }
}

Estamos usando osascript para executar AppleScript no macOS. Aqui estamos usando para enviar notificações.


Proteção de .env

Impeça que o dropstone leia arquivos .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")
      }
    },
  }
}

Injetar variáveis de ambiente

Injete variáveis de ambiente em todas as execuções de shell (ferramentas de IA e terminais do usuário):

export const InjectEnvPlugin = async () => {
  return {
    "shell.env": async (input, output) => {
      output.env.MY_API_KEY = "secret"
      output.env.PROJECT_ROOT = input.cwd
    },
  }
}

Ferramentas personalizadas

Plugins também podem adicionar ferramentas personalizadas ao 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})`
        },
      }),
    },
  }
}

O auxiliar tool cria uma ferramenta personalizada que o dropstone pode chamar. Ele recebe uma função de esquema Zod e retorna uma definição de ferramenta com:

  • description: O que a ferramenta faz
  • args: Esquema Zod para os argumentos da ferramenta
  • execute: Função que é executada quando a ferramenta é chamada

Suas ferramentas personalizadas estarão disponíveis para o dropstone junto com as ferramentas integradas.

Note:

Se uma ferramenta de plugin usar o mesmo nome de uma ferramenta integrada, a ferramenta de plugin tem precedência.


Logging

Use client.app.log() em vez de console.log para logging estruturado:

export const MyPlugin = async ({ client }) => {
  await client.app.log({
    body: {
      service: "my-plugin",
      level: "info",
      message: "Plugin initialized",
      extra: { foo: "bar" },
    },
  })
}

Níveis: debug, info, warn, error. Veja a referência do SDK para detalhes.


Hooks de compactação

Personalize o contexto incluído quando uma sessão é compactada:

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
`)
    },
  }
}

O hook experimental.session.compacting é acionado antes do LLM gerar um resumo de continuação. Use-o para injetar contexto específico do domínio que o prompt de compactação padrão poderia perder.

Você também pode substituir completamente o prompt de compactação definindo 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.
`
    },
  }
}

Quando output.prompt é definido, ele substitui completamente o prompt de compactação padrão. O array output.context é ignorado neste caso.

Ctrl+I