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 패키지 모두 지원됩니다.


플러그인 설치 방법

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 스키마 함수를 받고 다음을 포함하는 도구 정의를 반환합니다:

  • description: 도구가 하는 일
  • args: 도구의 인수에 대한 Zod 스키마
  • execute: 도구가 호출될 때 실행되는 함수

사용자 정의 도구는 기본 제공 도구와 함께 Dropstone에서 사용할 수 있습니다.

Note:

플러그인 도구가 기본 제공 도구와 같은 이름을 사용하면 플러그인 도구가 우선합니다.


로깅

console.log 대신 client.app.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 배열은 무시됩니다.

Ctrl+I