Community라이팅 & 에디팅github.com

dsh 플러그인 개발 스킬

DeepSeek Harness(dsh) 플러그인을 개발하기 위한 AI 스킬입니다. Cordis 기반 에이전트 프레임워크에서 tool, service, LLM adapter, event listener 등의 기능을 생성하고 배포할 수 있습니다.

dsh 플러그인 개발 스킬란 무엇인가요?

이 스킬은 DeepSeek Harness(dsh) 플러그인 개발을 위한 포괄적인 가이드를 제공합니다. dsh는 Cordis 기반의 에이전트 프레임워크로, 플러그인은 TypeScript 모듈로 작성되며 `cordis.yml`을 통해 구성됩니다. 이 스킬은 플러그인 구조, 의존성 주입, 자동 정리(auto-cleanup), 자식 플러그인, 파이버 상태 관리, 도구(tool) 등록, 설정(config) 스키마, 서비스 제공 및 소비, 이벤트 시스템, LLM 어댑터, 3계층 기능 설계, 번들 배포, HMR(Hot Module Replacement) 등 핵심 개념을 다룹니다. 특히 `PENDING` 상태 진단 방법과 참조 문서 링크를 제공하여 초보자부터 고급 개발자까지 모두 활용할 수 있습니다. Claude Code, Cursor, Codex 등 주요 AI 에이전트 도구에서 이 스킬을 사용하면 dsh 플러그인 개발 생산성을 크게 높일 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add TrueHOOHA/dsh-plugin-dev-skill

Installed? Explore more 라이팅 & 에디팅 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

dsh 플러그인 개발

DeepSeek Harness (dsh)용 플러그인을 개발합니다. dsh는 Cordis 기반 에이전트 프레임워크입니다. 플러그인은 apply(ctx: Context)를 내보내는 TypeScript 모듈이며, cordis.yml을 통해 구성됩니다.

빠른 참조

플러그인 구조

import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
export function apply(ctx: Context) { /* 기능 등록 */ }

세 가지 형태: 함수 (기본값), 객체 ({ name, inject, apply }), 클래스 (extends Service — 다른 플러그인에 서비스를 제공할 때만 사용).

의존성: export const inject = ['tools', 'llm'] — 프레임워크는 주입된 모든 서비스가 준비될 때까지 플러그인을 PENDING 상태로 유지합니다.

자동 정리: ctx를 통해 등록된 모든 것은 효과(effect)입니다 — 언로드 시 자동으로 정리됩니다. 사용자 정의 리소스(타이머, 연결)는 ctx.effect(() => { ... return () => cleanup })으로 감싸세요.

자식 플러그인: const fiber = ctx.plugin(childPlugin) — 자식은 부모 컨텍스트를 상속받습니다. await fiber.dispose()는 재귀적으로 언로드합니다.

파이버 상태

PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
                 ↘ FAILED

PENDING → 서비스 의존성 누락. FAILED → apply에서 오류 발생 또는 설정 검증 실패. 런타임에 서비스가 사라지면 의존하는 플러그인이 자동으로 해제됩니다.

도구(Tools)

import { defineTool } from '@deepseek-ai/dsh-tools'
export const inject = ['tools']
export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'greet', description: '...',
    parameters: { name: { type: 'string', required: true } },
    output: { schema: { type: 'string' }, render: (_args, value) => [{ type: 'text', text: value }] },
    async execute(args) { return `Hello, ${args.name}!` },
  }))
}

defineTool은 매개변수를 JSON Schema로 변환하고, 인자를 검증하며, 언로드 시 자동으로 등록을 해제합니다. 자세한 내용은 references/basic/tool.md를 참조하세요.

설정(Configuration)

import Schema from '@deepseek-ai/schemastery'
export interface Config { greeting: string; maxRetries: number }
export const Config: Schema<Config> = Schema.object({
  greeting: Schema.string().default('Hello'),
  maxRetries: Schema.number().default(3),
})
export function apply(ctx: Context, config: Config) { /* 검증된 설정 */ }

interface Config와 const Config: Schema<Config>를 모두 내보내야 합니다. 잘못된 설정 → 명확한 오류와 함께 FAILED 상태가 됩니다. YAML에서 동적 값을 위해 !!js를 사용하세요: config: { apiKey: !!js process.env.API_KEY }. 자세한 내용은 references/basic/config.md를 참조하세요.

서비스(Services)

제공: class extends Service { constructor(ctx) { super(ctx, 'name') } }

  • declare module '@deepseek-ai/cordis' { interface Context { name: NameService } }

소비: export const inject = ['name'] → ctx.name.method().

선택적: ctx.get('name') → 서비스가 없을 때 undefined 반환.

서비스 격리: group: true + isolate: { shell: true } → 각 그룹에 자체 제공자 인스턴스를 부여합니다. 자세한 내용은 references/framework/service.md를 참조하세요.

이벤트(Events)

모드호출용도
emitctx.emit(n, ...args)동기 브로드캐스트, 반환값 무시
parallelawait ctx.parallel(n, ...args)동시 리스너
serialawait ctx.serial(n, ...args)순차적, 첫 번째 null 아닌 값 승리
bailctx.bail(n, ...args)동기 직렬
waterfallctx.waterfall(n, ...args, next)미들웨어 체인

워터폴 리스너는 의도적으로 단락시키지 않는 한 반드시 next()를 호출해야 합니다. 주요 하네스 이벤트: agent/step, tools/result, session/event. 자세한 내용은 references/framework/events.md를 참조하세요.

LLM 어댑터

class MyAdapter extends LlmAdapter {
  async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
    // 변환 → 제공자 API → StreamChunk 생성
  }
}
ctx.llm.registerAdapter(['my-provider'], adapter)

블록별 StreamChunk 순서: block-start → text-delta/tool-call-delta → block-end → usage → finish. 안정적인 코드와 함께 LlmError를 던지세요. 자세한 내용은 references/practice/llm-adapter.md를 참조하세요.

3계층 기능 설계

정의 (Service 추상 클래스) → 제공자 (구현) → 소비자 (도구)

세 계층 모두 inject를 통해 동일한 서비스 이름을 공유합니다. 역할이 독립적으로 진화해야 할 때만 별도 패키지로 분할하세요. 자세한 내용은 references/practice/index.md를 참조하세요.

cordis.yml / 번들

로컬 개발: --patch ./cordis.patch.yml

- insert:
    - id: my-plugin
      name: '/absolute/path/to/src/my-plugin.ts'
      config: { greeting: 'Hi' }

배포: dsh.bundle 매니페스트 + cordis.patch.yml이 포함된 npm 패키지. 설치: dsh plugin --profile demo add ./hello-plugin. 레이어 순서: 프로필 번들 → 프로필 패치 → 홈 패치 → --patch 오버레이. 이후 레이어가 행 단위로 우선 적용됩니다 (깊은 병합 없음). 자세한 내용은 references/basic/publish.md를 참조하세요.

HMR

@deepseek-ai/cordis-plugin-hmr + @deepseek-ai/cordis-plugin-logger-console

  • @deepseek-ai/cordis-plugin-timer를 로드하면 파일 저장 시 자동으로 재로드됩니다.

PENDING 진단

import { FiberState, type Context } from '@deepseek-ai/cordis'
for (const fiber of ctx.registry.values().flatMap(r => r.fibers))
  if (fiber.state === FiberState.PENDING)
    console.log(`${fiber.name} is PENDING`)

참조 문서

references/ 디렉토리의 상세 가이드:

경로내용
references/basic/첫 플러그인, 도구, 설정, 배포
references/framework/생명주기, 서비스, 이벤트
references/practice/3계층 설계, LLM 어댑터
references/cordis-tutorial/Cordis 프레임워크 단계별 튜토리얼

관련 스킬