CommunityWriting & Editinggithub.com

dsh Plugin Development Skill

AI skill for developing DeepSeek Harness (dsh) plugins — a Cordis-based agent framework. Covers creating and publishing capabilities such as tools, services, LLM adapters, and event listeners.

What is dsh Plugin Development Skill?

This AI skill assists developers in creating plugins for DeepSeek Harness (dsh), a modular agent framework built on Cordis. Plugins are TypeScript modules that export an `apply(ctx: Context)` function and are composed via `cordis.yml`. The skill covers the full plugin development lifecycle: defining plugin structure (function, object, or class), managing dependencies and lifecycle states (PENDING, LOADING, ACTIVE, FAILED, UNLOADING, DISPOSED), and implementing various capabilities like tools (using `defineTool`), services (with provider/consumer patterns), configuration (using Schemastery schema), events (emit, parallel, serial, bail, waterfall modes), and LLM adapters (extending `LlmAdapter` and yielding `StreamChunk`). It also explains the three-layer capability design (Definition → Provider → Consumer), deployment via `cordis.yml` bundles and `cordis.patch.yml` overlays, HMR support for hot reloading, and diagnosing plugin stalls using fiber states. The skill is intended for use in editors like Cursor or Codex to accelerate dsh plugin development with AI-generated context-aware suggestions.

Works with~Claude Code~Codex CLI~Cursor
npx skills add TrueHOOHA/dsh-plugin-dev-skill

Installed? Explore more Writing & Editing skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

dsh Plugin Development

Develop plugins for DeepSeek Harness (dsh), a Cordis-based agent framework. Plugins are TypeScript modules exporting apply(ctx: Context), composed via cordis.yml.

Quick Reference

Plugin Structure

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

Three forms: function (default), object ({ name, inject, apply }), class (extends Service — use only when providing a service to other plugins).

Dependencies: export const inject = ['tools', 'llm'] — framework keeps plugin PENDING until all injected services are ready.

Auto-cleanup: Everything registered through ctx is an effect — unload automatically cleans up. For custom resources (timers, connections), wrap in ctx.effect(() => { ... return () => cleanup }).

Child plugins: const fiber = ctx.plugin(childPlugin) — children inherit parent context; await fiber.dispose() unloads recursively.

Fiber States

PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
                 ↘ FAILED

PENDING → missing service dependency. FAILED → apply threw or config validation failed. Service disappearing at runtime auto-disposes dependents.

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 converts parameters to JSON Schema, validates args, and auto-unregisters on unload. See references/basic/tool.md for details.

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) { /* validated config */ }

Must export BOTH interface Config AND const Config: Schema<Config>. Invalid config → FAILED state with clear error. Use !!js in YAML for dynamic values: config: { apiKey: !!js process.env.API_KEY }. See references/basic/config.md.

Services

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

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

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

Optional: ctx.get('name') → returns undefined when absent.

Service isolation: group: true + isolate: { shell: true } gives each group a unique provider instance. See references/framework/service.md.

Events

ModeCallUse
emitctx.emit(n, ...args)Sync broadcast, ignore returns
parallelawait ctx.parallel(n, ...args)Concurrent listeners
serialawait ctx.serial(n, ...args)Sequential, first non-null wins
bailctx.bail(n, ...args)Sync serial
waterfallctx.waterfall(n, ...args, next)Middleware chain

Waterfall listeners must call next() unless intentionally short-circuiting. Key harness events: agent/step, tools/result, session/event. See references/framework/events.md.

LLM Adapters

class MyAdapter extends LlmAdapter {
  async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
    // Convert → provider API → yield StreamChunk
  }
}
ctx.llm.registerAdapter(['my-provider'], adapter)

StreamChunk order per block: block-start → text-delta/tool-call-delta → block-end → usage → finish. Throw LlmError with stable codes. See references/practice/llm-adapter.md.

Three-Layer Capability Design

Definition (Service abstract class) → Provider (implements) → Consumer (tool)

All three share the same service name via inject. Only split into separate packages when roles need independent evolution. See references/practice/index.md.

cordis.yml / Bundles

Local dev: --patch ./cordis.patch.yml

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

Distribution: npm package with dsh.bundle manifest + cordis.patch.yml. Install: dsh plugin --profile demo add ./hello-plugin. Layer order: profile bundles → profile patch → home patch → --patch overlays. Later layers win by row (no deep merge). See references/basic/publish.md.

HMR

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

  • @deepseek-ai/cordis-plugin-timer to auto-reload on file save.

Diagnosing 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`)

Reference Docs

Detailed guides in the references/ directory:

PathCovers
references/basic/First plugin, tools, config, publishing
references/framework/Lifecycle, services, events
references/practice/Three-layer design, LLM adapters
references/cordis-tutorial/Step-by-step Cordis framework tutorial

Related Skills