Dropstone Docs

Plugins

Schreiben Sie Ihre eigenen Plugins, um Dropstone zu erweitern.

Plugins ermöglichen es Ihnen, Dropstone zu erweitern, indem Sie sich in verschiedene Ereignisse einklinken und das Verhalten anpassen. Sie können Plugins erstellen, um neue Funktionen hinzuzufügen, externe Dienste zu integrieren oder das Standardverhalten von Dropstone zu ändern.


Plugin verwenden

Es gibt zwei Möglichkeiten, Plugins zu laden.


Aus lokalen Dateien

Platzieren Sie JavaScript- oder TypeScript-Dateien im Plugin-Verzeichnis.

  • .dropstone/plugins/ - Plugins auf Projektebene
  • ~/.config/dropstone/plugins/ - Globale Plugins

Dateien in diesen Verzeichnissen werden beim Start automatisch geladen.


Aus npm

Geben Sie npm-Pakete in Ihrer Konfigurationsdatei an.

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

Sowohl reguläre als auch scoped npm-Pakete werden unterstützt.


Wie Plugins installiert werden

npm-Plugins werden beim Start automatisch mit Bun installiert. Pakete und ihre Abhängigkeiten werden in ~/.cache/dropstone/node_modules/ zwischengespeichert.

Lokale Plugins werden direkt aus dem Plugin-Verzeichnis geladen. Um externe Pakete zu verwenden, müssen Sie eine package.json in Ihrem Konfigurationsverzeichnis erstellen (siehe Abhängigkeiten), oder veröffentlichen Sie das Plugin auf npm und fügen Sie es zu Ihrer Konfiguration hinzu.


Ladereihenfolge

Plugins werden aus allen Quellen geladen und alle Hooks werden nacheinander ausgeführt. Die Ladereihenfolge ist:

  1. Globale Konfiguration (~/.config/dropstone/dropstone.json)
  2. Projektkonfiguration (dropstone.json)
  3. Globales Plugin-Verzeichnis (~/.config/dropstone/plugins/)
  4. Projekt-Plugin-Verzeichnis (.dropstone/plugins/)

Doppelte npm-Pakete mit demselben Namen und Version werden einmal geladen. Ein lokales Plugin und ein npm-Plugin mit ähnlichen Namen werden jedoch beide separat geladen.


Plugin erstellen

Ein Plugin ist ein JavaScript/TypeScript-Modul, das eine oder mehrere Plugin-Funktionen exportiert. Jede Funktion erhält ein Kontextobjekt und gibt ein Hooks-Objekt zurück.


Abhängigkeiten

Lokale Plugins und benutzerdefinierte Tools können externe npm-Pakete verwenden. Fügen Sie eine package.json zu Ihrem Konfigurationsverzeichnis mit den benötigten Abhängigkeiten hinzu.

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

Dropstone führt beim Start bun install aus, um diese zu installieren. Ihre Plugins und Tools können sie dann importieren.

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

Grundstruktur

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

  return {
    // Hook implementations go here
  }
}

Die Plugin-Funktion erhält:

  • project: Die aktuelle Projektinformation.
  • directory: Das aktuelle Arbeitsverzeichnis.
  • worktree: Der Git-Worktree-Pfad.
  • client: Ein Dropstone SDK-Client für die Interaktion mit dem Agent.
  • $: Buns Shell API zum Ausführen von Befehlen.

TypeScript-Unterstützung

Für TypeScript-Plugins können Sie Typen aus dem Plugin-Paket importieren:

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

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

Ereignisse

Plugins können sich auf Ereignisse abonnieren, wie im Abschnitt Beispiele unten zu sehen ist. Hier ist eine Liste der verschiedenen verfügbaren Ereignisse.

Command-Ereignisse

  • command.executed

Datei-Ereignisse

  • file.edited
  • file.watcher.updated

Installationsereignisse

  • installation.updated

LSP-Ereignisse

  • lsp.client.diagnostics
  • lsp.updated

Nachrichtenereignisse

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

Berechtigungsereignisse

  • permission.asked
  • permission.replied

Server-Ereignisse

  • server.connected

Sitzungsereignisse

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

Todo-Ereignisse

  • todo.updated

Shell-Ereignisse

  • shell.env

Tool-Ereignisse

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

Beispiele

Hier sind einige Beispiele für Plugins, die Sie verwenden können, um Dropstone zu erweitern.


Benachrichtigungen senden

Senden Sie Benachrichtigungen, wenn bestimmte Ereignisse auftreten:

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

Wir verwenden osascript, um AppleScript auf macOS auszuführen. Hier verwenden wir es, um Benachrichtigungen zu senden.


.env-Schutz

Verhindern Sie, dass Dropstone .env-Dateien liest:

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

Umgebungsvariablen injizieren

Injizieren Sie Umgebungsvariablen in alle Shell-Ausführungen (KI-Tools und Benutzerterminals):

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

Benutzerdefinierte Tools

Plugins können auch benutzerdefinierte Tools zu Dropstone hinzufügen:

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

Der tool-Helfer erstellt ein benutzerdefiniertes Tool, das Dropstone aufrufen kann. Es nimmt eine Zod-Schema-Funktion und gibt eine Tool-Definition mit:

  • description: Was das Tool tut
  • args: Zod-Schema für die Argumente des Tools
  • execute: Funktion, die ausgeführt wird, wenn das Tool aufgerufen wird

Ihre benutzerdefinierten Tools stehen Dropstone neben integrierten Tools zur Verfügung.

Note:

Wenn ein Plugin-Tool denselben Namen wie ein integriertes Tool hat, hat das Plugin-Tool Vorrang.


Protokollierung

Verwenden Sie client.app.log() statt console.log für strukturierte Protokollierung:

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

Ebenen: debug, info, warn, error. Weitere Details finden Sie in der SDK-Referenz.


Komprimierungs-Hooks

Passen Sie den Kontext an, der einbezogen wird, wenn eine Sitzung komprimiert wird:

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

Der experimental.session.compacting-Hook wird ausgelöst, bevor das LLM eine Fortsetzungszusammenfassung generiert. Verwenden Sie ihn, um domänenspezifischen Kontext einzufügen, den die Standard-Komprimierungsaufforderung übersehen würde.

Sie können auch die Komprimierungsaufforderung vollständig ersetzen, indem Sie output.prompt setzen:

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

Wenn output.prompt gesetzt ist, ersetzt es die Standard-Komprimierungsaufforderung vollständig. Das output.context-Array wird in diesem Fall ignoriert.

Strg+I