Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Use YourBuddy

Start with a working task. Add specialized capabilities as your needs become clearer.

Start using

  • Complete your first task: connect a model, select a folder, and inspect the generated file.
  • Models: choose an account or API connection.
  • Workspace: work with files, terminals, and changes.
  • Settings: customize the workbench and manage network, updates, and restart.

Available capabilities

  • Default plugins: what comes with Y8, why it was chosen, and what needs configuration.
  • Evaluation: investigate a task and compare controlled changes.
  • Troubleshooting: start with the operation that failed.

Extend as needed

  • Extend Y8: choose between existing plugins, configuration, Skills, and a new plugin.
  • Develop with Creator: describe a need and inspect actual extension interfaces before experimenting.
  • Your first plugin: load a small plugin from a prepared source checkout.
  • Harbor as an example: see how a specialized extension combines several components.

The sidebar’s Help and guides menu links to these guides while you work. See Why YourBuddy for the product philosophy and the roadmap for possible next steps.

1 - Why YourBuddy

Your models. Your tools. Your way.

YourBuddy assembles DeepSeek Harness and community plugins into a desktop workbench. A model understands and generates; a Harness supplies the tools, context, permissions, sessions, and feedback needed to carry out work.

Start using, then extend

The default combination covers model access, workspace tasks, and result inspection. When a specialized need appears, choose an extension approach: reuse a plugin, write a Skill, adjust configuration, or develop a DSH plugin. Small teams can share projects and working instructions while each member keeps their own account and workspace.

Two parts of a specialized workbench

DSH is an AI runtime with a Web UI. Session creation, the model-and-tool execution loop, history, the plugin system, and MCP tool integration are already available. Y8 provides a ready-to-use default composition on that foundation. Customization for a particular domain can focus on two parts:

  • A domain analysis engine: analyze data using domain rules and produce results people can inspect.
  • A practical control panel: let users select inputs, start tasks, adjust parameters, and inspect progress and results.

DSH plugins can implement both parts. A plugin can provide analysis capabilities and extend the workbench interface, organizing domain logic and user actions around the same task. The Harbor development example shows this approach.

Choose what fits

Choose models for their capabilities, availability, and cost. Add tools when they help your task. Give the workbench your own name and Logo without having to assemble the application yourself.

Inspect what happened

Keep the conversation connected to files, command output, and Git differences. A completion message is useful when you can inspect the result behind it.

Improve with evidence

Context Doctor explains context overhead. Harbor Evolution helps analyze completed work and compare controlled changes. A score needs an explanation, and a successful run is not automatically an improvement.

Know where work goes

Project files remain in the workspace you select. Sessions and settings are managed locally. Cloud models, search services, and evaluators receive the inputs required for their operations. Local storage does not mean all computation is offline.

The initial desktop target is macOS Apple Silicon. See downloads, plugin ownership, and the roadmap for scope and limitations.

2 - Your first task

Create a file in a workspace, then inspect the result yourself.

Before you begin

You need a supported Mac, a usable model account or API credentials, and a practice folder. Check download availability first. The desktop package carries its default plugins and managed runtimes; ordinary users do not need a source build. Docker is only needed by evaluation workflows that require it.

Connect and choose a workspace

  1. Launch YourBuddy and wait for runtime preparation.
  2. Open Settings and complete one model setup path.
  3. Add and select a practice directory in the workspace selector. The input becomes available once a workspace is selected.

Ask for an output

Create hello-yourbuddy.md in the current directory with three tasks for this week and an acceptance criterion for each. Tell me where you saved it.

Respond to approvals when the interface requests them. After completion, open the file in Better Sidebar and inspect its contents. In a Git repository, also check the difference. A completion message alone does not establish that a file exists.

Continue when a step blocks you

BlockerGuidance
Unsure how to sign in or choose a modelModel setup
Input is unavailable or files appear in the wrong directoryWorkspace
A preinstalled plugin is not usableDefault plugins and prerequisites

The sidebar Help and guides entry opens this page and troubleshooting while you work. Complete the file task first, then learn how to extend Y8 as needed.

Continue

Keep working in this session, explore the file and terminal panels, or personalize the workbench. If a step fails, use troubleshooting rather than reinstalling all plugins.

3 - Connect a model

Choose the account or provider that fits your task.

GPT Auth

Open Settings → GPT Auth, inspect login state, and follow the login flow. Choose an available Codex model in the model selector. Codex Auth is a community integration using an unofficial account interface; account availability and quota apply. It reuses native Codex login storage.

API providers

Open Settings → Model, select Add provider, or enter DeepSeek credentials in its card. Select the provider and model, then send a simple text request. Credentials configured in another DSH environment are not automatically YourBuddy credentials.

Custom endpoints

Choose Add custom provider for a company gateway or compatible local service. Supply its provider ID, base URL, protocol, credentials, and model. Declared image and tool capabilities must match the actual endpoint. The detailed provider reference explains modality declarations and protocol settings.

Check the active selection

Changing an API model takes effect on the next request. Check the session’s selection before comparing results. A Codex Agent Preset enables delegation; it is distinct from selecting the model for the parent conversation.

Keep secrets out of conversations and screenshots. If a request fails, check the account, selected model, quota, and network settings.

4 - Work with files and results

Keep the conversation beside the work it produces.

Select the right directory

Add and select a workspace before starting. Agent file operations and Harbor tool requests use the session’s working directory. Use a practice folder for unfamiliar tasks and confirm the paths reported by tools.

Inspect outputs

Better Sidebar provides a file tree, editor, previews, terminals, Git views, and background tasks. Open generated Markdown or code, compare it with your request, and inspect command results. Git operations and real terminals affect your actual workspace.

Delegate a focused task

The default Codex preset can delegate a self-contained task to Codex Subagent. Include the objective, relevant files, and completion criteria. It shares the working directory but does not inherit the entire parent conversation; native Codex login and permission settings apply. Loading the provider does not start a child process.

Manage context deliberately

A Skill supplies reusable task instructions; a plugin adds runtime capabilities or UI. Extra instructions and tools can consume context. Use Context Doctor before trimming, then verify a representative task after any change.

See Better Sidebar for optional controls and evaluation for checking repeated task quality.

5 - Workbench settings

Personalize the interface and manage the desktop from one place.

Workbench identity

Open Settings → General → My workbench. Preview and save a sidebar name, image, new-session headline, and optional headline badge, or restore YourBuddy defaults. Empty text fields keep the localized headline and badge defaults. Identity is stored per Profile and survives reloads. It does not change the desktop icon, application name, or complete theme.

Networking

Open Settings → General → Network proxy. Choose direct, system, or a custom fixed proxy. Custom mode requires HTTP and HTTPS addresses without embedded credentials. PAC and automatic proxy discovery are unsupported. A readable PEM certificate bundle can supplement system trust for enterprise networks; certificate verification remains enabled.

Test the desktop draft route and running Host route separately. Save and restart to activate a changed policy, then test again. Existing processes keep the previous policy until restart.

Updates and restart

Use Settings → General → Application lifecycle to check for updates or restart. Restart after installing a new plugin so its Client can be discovered. Bundled plugins update with YourBuddy releases, while upstream version notices do not install anything automatically.

These controls come from the first-party Personal Workbench plugin. Native restart, proxy, and updater operations require the desktop shell and are unavailable in standalone DSH Web.

See downloads for release availability and troubleshooting for failures.

Help and guides

The bottom of the sidebar offers getting started, default plugins, Extend Y8, troubleshooting, and feedback. A question-mark icon remains when the sidebar is collapsed. Guides open in an external browser while the session stays in place. If opening fails, copy the displayed address; guide content requires network access.

6 - Evaluate and improve

Begin with evidence from actual tasks, then build a stable comparison.

Four useful concepts

ConceptQuestion
DatasetWhat should be tested?
GeneratorWho produces the answer or artifact?
Evaluator and criteriaWhat counts as good, and who checks it?
OptimizerWho proposes the next controlled change?

The bundled Harbor Skill guides this setup. Invoke evolve-agent-with-harbor from the conversation and explain your task. The desktop includes the plugin, Skill, and matching Python adapter; do not repeat the standalone package setup command. Candidate execution requires its Docker environment and usable model services; run Doctor before starting.

Without a dataset

Ask to diagnose recent completed sessions in the current working directory. Preview the selected records, evaluator, estimated requests, and evidence-storage notice. Confirm before evaluation. Inspect coverage, evidence, and abstention reasons in the Harbor workbench.

Historical diagnosis does not rerun the Candidate and cannot be used as a promotion baseline. A completed but unscored record is not a zero score. If there is no eligible history, complete a task or supply an explicit Query or Dataset.

With repeatable tasks

Confirm the four concepts, fix the Candidate model, and establish a valid baseline. Change one factor, rerun comparable tasks, and inspect regressions as well as gains. A changed model or evaluator identity can require a fresh baseline.

A single-Query quick diagnostic checks wiring rather than establishing quality under its drafted rubric. Formal promotion requires an accepted policy; evaluator reliability requires independent Ground Truth meta-evaluation. Deployment remains a separate action.

See Harbor Evolution for costs, credentials, and limits.

7 - Troubleshooting

Start with the failing operation and keep its diagnostic message.
SymptomCheck next
Input is unavailableSelect a workspace before starting a session
Model request failsCheck the selected model, login or credentials, quota, and network status
A generated file is missingInspect the session directory and actual tool result, not only the assistant summary
Context Doctor is absentCreate an existing session; a new session without an identifier has no session-level audit control
A plugin installs but has no UIConfirm the YourBuddy Web Profile was targeted, then restart through Application lifecycle
Marketplace install is disabledThe npm package may lack unambiguous Bundle metadata; a GitHub topic alone is insufficient
A proxy test succeeds but requests still failInspect both desktop and Host results; save, restart, and retest changed settings
Harbor has no historyUse the exact directory containing completed business sessions, or supply explicit tasks
An evaluation is unscoredRead evidence and coverage; insufficient evidence can cause legitimate abstention
A help page does not openCopy the address from Help and open it in a browser; check networking, since Help has no offline guide copy
A development tutorial cannot find the pluginUse the tutorial’s absolute path and run the command from a prepared DSH source checkout

Startup and system prompts

Preserve startup diagnostics when runtime preparation fails. Do not delete the application data directory as a first troubleshooting step. Apple signing and notarization are separate from Tauri update signatures; consult the actual release’s system-opening instructions.

Share useful evidence

Include application version, operating system and architecture, the failed operation, and the visible error. Remove credentials, private task text, and sensitive file paths. The macOS application root is ~/Library/Application Support/YourBuddy; project outputs remain in the selected workspace.

Use model setup, settings, or the relevant plugin page for detailed prerequisites. Report an issue.

8 - Extend Y8

Start with one task you want to improve. Y8 uses DSH’s plugin system, so you can adopt an existing extension or develop your own without inventing another plugin format.

For a specialized plugin, start with two questions: what data should the domain analysis engine process and what results should it return? What actions and results should the control panel expose? Extend either or both as needed, reusing the existing DSH runtime. See the product philosophy for this division of responsibilities.

Choose an approach

Your needStart here
A capability already included in Y8Read default plugins and their prerequisites
A capability someone else maintainsSearch the plugin marketplace and check its README and compatibility
A repeatable procedure, prompt, or output standardWrite a Skill for the workspace; a new runtime plugin is unnecessary for instructions alone
Different options or a composition of existing capabilitiesCheck settings, then plugin configuration and Bundle guidance
An existing external serviceCheck the service’s MCP integration before implementing a new connection
A new operation, service, settings card, or interfaceFollow the plugin development path below

Define a small requirement

Copy this outline into your conversation or project README. Answer what you know; the assistant can inspect the existing project for the rest. Give it sample data rather than credentials.

  • Task:
  • Inputs and their source:
  • Expected output and where I will inspect it:
  • Allowed changes and operations that must not run:
  • Required accounts, services, and configuration:
  • One sample input and its expected result:

For example: read failed jobs from the team’s build service, return failure reasons and links, and never trigger reruns. Use a redacted service response as the acceptance example. This describes a possible extension, not a service already integrated in Y8.

Develop and verify

  1. Develop with Creator to inspect available interfaces and experiment with a narrow requirement.
  2. Follow your first plugin to load editable source. This tutorial requires a prepared DSH source checkout, not only the desktop installer.
  3. Add a tool or configuration, changing one behavior at a time and checking its visible result.
  4. Follow packaging and installation to make the source reusable. A temporary dynamic experiment is not an installed package.

For a specialized example, read how Harbor is developed. Framework, events, and service details live in the framework guide; that deeper reference opens on GitHub where it is not published on this site.

Share within a team

Share the project or exact package version, supported DSH version, installation steps, configuration names, and an acceptance example. A teammate should supply their own account and directory, then repeat the example. Keep private sessions and credentials out of the shared files. A local checkout link works only on its author’s machine; use the packaging guide when handing it to another person.

If a documented step fails, report the step, version, and error through troubleshooting. This helps distinguish missing guidance from a development capability that needs work.

Contribute to Y8

Desktop builds and site maintenance are separate contributor tasks. Their GitHub references are the desktop source guide, website maintenance guide, and product notices.

8.1 - Develop with Creator

Creator helps inspect the running DSH composition and try a small plugin. Use it when a task needs a new operation or interface; start with extension choices if you only need configuration or a repeatable procedure.

Prepare the environment

This guide uses the DSH source Web application. Complete the source prerequisites on GitHub, then run the following from the repository root:

pnpm dsh web --no-open --port 0

Open the launch URL printed by the command, including its authentication fragment. The OS chooses an available port. Select a working directory and configure a model before sending a request. In the session’s Agent Preset selector, choose Creator (创造模式, preset ID cordis). If your installed Y8 version does not expose this preset, use this source workflow; installing Y8 alone does not establish a standalone plugin development environment.

Describe the change

Use the requirement outline and ask the assistant to load the Creator preset’s plugin-development Skill. For composition changes, it also uses the preset’s composition-editing Skill.

For a first experiment, ask for one temporary, read-only tool that returns a short checklist for a task, and ask the assistant to inspect the available tool-registration interface first. State the expected result and require it to stop the experiment after verification. Model requests use your configured account.

Inspect before implementing

Creator can list inspection providers, query their actual interfaces, and inspect the source and diagnostics of an existing experiment. Ask it to select Host for operations and services, Client for UI, or both only when needed. Missing capabilities must be reported rather than replaced with guessed APIs.

Ask for a code preview and the expected effect before activation. Dynamic code uses plain JavaScript function bodies, not TypeScript or JSX modules. Defining an experiment does not execute it. Follow any approval request and inspect the final running state; waiting or starting is not success.

Check and stop

Call the new tool or interact with the changed UI, then compare the result with your example. When something fails, ask Creator to inspect that experiment’s diagnostics and source. Stop it when finished and confirm that the tool or UI contribution disappears. Stopping removes registrations; it does not undo files or external operations already performed.

Keep useful work

Dynamic definitions are process-local and disappear on Host restart. A conversation recording of their source does not automatically reload them. Before restarting, save the useful source and requirements in your project, then use the first-plugin tutorial and packaging guide to build and verify a normal plugin. There is no automatic dynamic-code-to-package conversion in this workflow.

For an existing plugin, edit its original project and preserve its identity and configuration. User presets belong in a user-owned copy; do not edit the shipped Creator preset, which application upgrades replace. Exact runtime rules and diagnostics are documented in the dynamic tool reference on GitHub.

8.2 - Your first plugin

This tutorial creates a minimal Harness plugin and loads it into the Web UI. Start from a repository checkout that has completed the run-from-source path.

Create a local project

From the repository root, create a scratch project for the tutorial:

mkdir -p scratch-plugin/src

What is a plugin?

In Harness, a plugin is a TypeScript module that exports an apply function. The framework calls apply when loading the plugin and passes a ctx context object through which the plugin registers capabilities:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // Register capabilities here.
}

That is the complete configuration.

Create the plugin file

Create scratch-plugin/src/my-plugin.ts:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  // Required dependencies are ready before apply runs.
  console.log('[hello-plugin] plugin loaded!')
}

Register it in cordis.yml

Run pwd from the repository root, then create scratch-plugin/cordis.yml as a Web overlay that inserts the local plugin. Replace /absolute/path/to/deepseek-harness below with the printed path:

- insert:
    - id: hello
      name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'

The plugin path must be absolute. A patch file contributes configuration but does not change the profile directory from which the loader resolves module paths.

Start the Web UI with that overlay:

pnpm dsh web --patch ./scratch-plugin/cordis.yml --no-open --port 0

Open the launch URL printed in the terminal, including its authentication fragment. The terminal prints [hello-plugin] plugin loaded! during startup.

Edit and stop

Change the greeting in the plugin, stop the development command with Ctrl+C, and run the same command again. Verify the new greeting in the terminal. Use the newly printed launch URL after each restart; port 0 requests an available port from the OS. Stop the command when finished.

Automatic cleanup

Anything registered through ctx—event listeners, tools, or timers—is cleaned up when the plugin unloads. You do not need to call removeListener or clearInterval manually.

For a resource that needs explicit cleanup, such as a network connection, use ctx.effect() to provide its disposer:

import type { Context } from '@deepseek-ai/cordis'

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('heartbeat')
    }, 5000)

    // The returned function runs when the plugin unloads.
    return () => clearInterval(timer)
  })
}

Declare dependencies

If the plugin consumes another service such as tools or llm, declare it in inject:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-tool-plugin'
export const inject = ['tools']

export function apply(ctx: Context) {
  // ctx.tools is ready here.
  ctx.tools.register(/* ... */)
}

The framework waits for every required service before loading the plugin.

Three plugin forms

In addition to a function module, a plugin can use object or class form.

Object form

import type { Context } from '@deepseek-ai/cordis'

export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {
    // ...
  },
}

Class form

import { Service, type Context } from '@deepseek-ai/cordis'

export default class MyService extends Service {
  static inject = ['tools']

  constructor(ctx: Context) {
    super(ctx, 'myService')
    // Perform synchronous initialization in the constructor.
  }
}

Function form is sufficient in most cases. Use class form when the plugin provides a service to other plugins; see services and dependencies.

Next steps

8.3 - Build a tool

This tutorial adds a greet tool to the Web UI. Complete Your first plugin first and keep its scratch-plugin directory.

Create the tool plugin

Replace scratch-plugin/src/my-plugin.ts with:

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'greet-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'greet',
    description: 'Greet someone by name.',
    parameters: {
      name: { type: 'string', required: true, description: 'The name to greet' },
    },
    output: {
      schema: { type: 'string' },
      render: (_args, value) => [{ type: 'text', text: value }],
    },
    async execute(args) {
      return `Hello, ${args.name}!`
    },
  }))
}

inject makes Cordis wait for the tool registry. defineTool infers and validates args from parameters; execute returns the canonical value declared by output.schema, and output.render converts that value to model-facing content.

Run and call the tool

Restart the development command if it is not running:

pnpm dsh web --patch ./scratch-plugin/cordis.yml --no-open --port 0

Open the launch URL printed in the terminal, including its authentication fragment, and ask: Use the greet tool to greet Ada. The model can call greet and receives Hello, Ada! as the tool result.

Next steps

8.4 - Plugin configuration

Accept configuration supplied through cordis.yml.

Complete your first plugin first. Replace the absolute path below with your checkout path, as in that tutorial; a patch does not change the directory used to resolve plugin modules.

Define the Config type

Export a Config type and a same-named Schemastery schema. Put defaults directly on the schema fields:

import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'

export const name = 'my-plugin'

export interface Config {
  greeting: string
  maxRetries: number
  verbose?: boolean
}

export const Config: Schema<Config> = Schema.object({
  greeting: Schema.string().default('Hello'),
  maxRetries: Schema.number().default(3),
  verbose: Schema.boolean().default(false),
})

export function apply(ctx: Context, config: Config) {
  console.log(config.greeting)  // User value or schema default.
}

Add the configuration to the inserted local plugin row in scratch-plugin/cordis.yml:

- insert:
    - id: hello
      name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
      config:
        greeting: 'Hi there'
        maxRetries: 5

When loading the plugin, Cordis uses the exported schema to validate configuration and fill defaults. Do not export a plain object as Config; it does not implement the Standard Schema interface required by Cordis.

Schema validation

Use Schemastery to express stricter validation:

import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'

export const name = 'validated-plugin'

export interface Config {
  apiKey: string
  timeout: number
  mode: 'fast' | 'accurate'
}

export const Config = Schema.object({
  apiKey: Schema.string().required(),
  timeout: Schema.number().default(30000),
  mode: Schema.union(['fast', 'accurate']).default('fast'),
})

export function apply(ctx: Context, config: Config) {
  // config is validated and type-safe.
}

The schema runs while the plugin loads. Invalid configuration fails the load with an actionable error.

Design principles

Do not hardcode tunable values

Harness requires anything that two deployments may want to set differently to be a configuration field.

// Wrong: hardcoded timeout.
const TIMEOUT = 30000

// Correct: configurable.
export interface Config {
  timeoutMs: number  // Defaults to 30000.
}

The test is whether cordis.yml can change the value without a code edit.

Fail loudly on invalid configuration

Express self-contained constraints in the schema so invalid configuration fails while the plugin loads. References to services or registered resources require dependency injection; the services tutorial introduces that contract.

Apply and verify

Stop the tutorial command and restart it after changing the source or patch. With the example above, the terminal prints Hi there. Replace maxRetries: 5 with maxRetries: wrong-type, restart, and inspect the plugin-load validation error. Restore the valid value before continuing. This workflow does not rely on automatic source or overlay reloading.

Next steps

8.5 - Package and install a plugin

The previous tutorials loaded a local plugin through a --patch overlay. This tutorial packages it as an installable bundle, installs it into a profile with dsh plugin add, and explains the layer order that determines the composed configuration. It assumes the dsh CLI is installed. Complete plugin configuration first.

To use a fresh source checkout instead, complete the run-from-source section, keep this tutorial’s hello-plugin directory at the repository root, and run the remaining dsh ... commands from there as pnpm dsh .... See source execution for build and launcher behavior.

Two concepts, two manifests

Installation is built on two concepts. Both are described by a package.json, but they carry different kinds of manifest under the dsh key, and they answer different questions:

  • A bundle is an npm package that ships a configuration layer. Its manifest declares dsh.bundle, answering “what does this package contribute?”: a patch file that inserts or overrides plugin rows.
  • A profile is a directory under $DSH_HOME/profiles/<name> describing one runnable composition. Its manifest declares dsh.profile, answering “which bundles compose this setup, in what order?”.

A bundle is what you author and distribute; a profile is what a user boots with dsh --profile <name>. Nothing is both.

The bundle manifest

Create the package directory:

mkdir -p hello-plugin
hello-plugin/
├── package.json       # declares dsh.bundle
├── cordis.patch.yml   # the layer applied when a profile lists this bundle
└── index.js           # plugin modules the patch rows reference

Create hello-plugin/package.json:

{
  "name": "dsh-hello-plugin",
  "version": "0.1.0",
  "type": "module",
  "main": "index.js",
  "files": ["index.js", "cordis.patch.yml"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}

Create hello-plugin/index.js with the plugin entry point:

export const name = 'hello-plugin'

export function apply() {
  console.log('[hello-plugin] plugin loaded!')
}

Create hello-plugin/cordis.patch.yml. The patch is a YAML array like the --patch overlays you wrote, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code:

- insert:
    - id: hello
      name: dsh-hello-plugin

A package without the dsh.bundle declaration still installs, but only as a plain dependency: dsh plugin prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable.

The profile manifest

A profile directory holds two files:

  • package.json — the profile’s out-of-tree plugin dependencies (managed by pnpm) plus the dsh.profile manifest with its ordered bundles list.
  • cordis.patch.yml — the user’s own patch layer, applied after every bundle layer.

You never write a profile manifest by hand: dsh --profile <name> --from-default-profile <template> can create one from a shipped application template, while dsh plugin creates a base-backed profile and maintains its installed bundle list. The CLI behavior reference owns the creation rules; the next section shows the plugin path.

Install into a profile

dsh plugin --profile <name> <args...> forwards to pnpm in the profile directory, so every pnpm verb works. From the directory that contains hello-plugin, install the package checkout:

dsh plugin --profile demo add ./hello-plugin

The first use initializes the profile (with @deepseek-ai/dsh-base as its first bundle), pnpm links the checkout, and dsh appends the bundle to dsh.profile.bundles because the package declares dsh.bundle:

{
  "name": "dsh-profile-demo",
  "private": true,
  "dependencies": {
    "dsh-hello-plugin": "link:/path/to/hello-plugin"
  },
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "dsh-hello-plugin"
      ]
    }
  }
}

Verify the layer without booting, then boot:

dsh --profile demo --dump-config   # shows a "# == dsh-hello-plugin" layer
dsh --profile demo

dsh plugin --profile demo remove dsh-hello-plugin removes both the dependency and the layer.

The loading order

The effective configuration composes over an empty root by applying, in order:

  1. Each bundle patch named in the profile’s dsh.profile.bundles list, in list order — @deepseek-ai/dsh-base first, then each installed bundle in the order it was added.
  2. The profile’s own cordis.patch.yml.
  3. The home-level $DSH_HOME/cordis.patch.yml — machine-local preferences shared by every profile.
  4. Each --patch <path> overlay, in argv order.

App arguments are not another patch layer. A surface bundle can resolve them through an ordinary app-owned service, described below.

Later layers win per row, and a patch replaces a row’s entire config value rather than deep-merging keys. Two consequences for bundle authors:

  • Your patch can override rows from earlier layers by id — the same way the dsh-web-app bundle overrides dsh-base rows — but must restate every key the row needs, not just the changed one.
  • Users can override your rows in their profile’s cordis.patch.yml without touching your package, so prefer configuration defaults users are likely to keep and let the schema carry the rest.

In-box bundle names always resolve from the dsh installation itself; pnpm manages only out-of-tree packages, so your bundle can rely on @deepseek-ai/dsh-base being present and current.

Give a surface bundle its own command line

A bundle that defines a runnable app mounts an ordinary provider plugin:

- id: hello-startup
  name: 'dsh-hello-plugin/startup'

The plugin exports inject = ['cmdlineArgs'], calls parseCmdline from @deepseek-ai/dsh-cmdline with its own commander program, and provides its app-owned service from the program’s action. The launcher hands every plugin the same immutable arguments after launcher flags, so app-specific flags need no launcher change and multiple plugins may parse the snapshot. The Loader row needs no launcher marker or special kind.

Rows configured by those arguments inject the provider’s service and read it from their own !!js options, with the deployment value beside it as the fallback:

- id: my-app
  name: '@example/my-app'
  inject: [myAppStartup]
  config:
    port: !!js ctx.myAppStartup.port ?? 8080

On --help, the provider publishes no service, so those rows never activate. Loader mounts the composition once, waits for each row’s ordinary injections, and only then evaluates that row’s !!js config against its injected context.

Installing from GitHub: the build-script catch

Publishing to a registry is not required — users can install straight from a git host:

dsh plugin --profile demo add github:you/hello-plugin

But a git install fetches sources, not built artifacts: nothing runs your build script, so a TypeScript package arrives without its lib/ output and fails to load. Two things must happen, one on each side:

  • The author ships a prepare script — pnpm runs it after a git install — that builds the published entry points from source, self-contained: it must not assume dev-only context such as a sibling monorepo checkout. turtle-ui is a working example: its prepare runs a dedicated tsdown config that transpiles src/ without project references or type checking.

  • The user allowlists the build. pnpm ≥10 refuses to run a git dependency’s prepare script until it is explicitly allowed, so the first add fails; dsh points at the fix — copy the exact package key pnpm printed into the profile’s pnpm-workspace.yaml:

    allowBuilds:
      dsh-hello-plugin: true

    and re-run the add.

Treat that allowance as permission to execute the package’s code on your machine at install time, outside any sandbox the agent runs under. Only allow packages whose source you trust, and pin a commit (github:you/hello-plugin#<sha>) so a later push cannot silently change what runs.

If you would rather not ask users for the allowance, distribute built artifacts instead — neither form needs any build permission:

  • Publish to npm with lib/ built at pnpm publish time; dsh plugin add your-package then installs prebuilt code.
  • Ship a tarball from pnpm pack; users run dsh plugin add ./hello-plugin-0.1.0.tgz.

Next steps