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:
- Globale Konfiguration (
~/.config/dropstone/dropstone.json) - Projektkonfiguration (
dropstone.json) - Globales Plugin-Verzeichnis (
~/.config/dropstone/plugins/) - 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.editedfile.watcher.updated
Installationsereignisse
installation.updated
LSP-Ereignisse
lsp.client.diagnosticslsp.updated
Nachrichtenereignisse
message.part.removedmessage.part.updatedmessage.removedmessage.updated
Berechtigungsereignisse
permission.askedpermission.replied
Server-Ereignisse
server.connected
Sitzungsereignisse
session.createdsession.compactedsession.deletedsession.diffsession.errorsession.idlesession.statussession.updated
Todo-Ereignisse
todo.updated
Shell-Ereignisse
shell.env
Tool-Ereignisse
tool.execute.aftertool.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 tutargs: Zod-Schema für die Argumente des Toolsexecute: 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.