Profiles, Bundles & Patches — dsh Guide
dsh runs on Cordis. The official architecture doc puts it bluntly: “every part of the product is a plugin, including the model adapter, the tool registry, the session log, and the agent loop itself.” There is no privileged core to hook into. You extend dsh by adding a plugin alongside the ones already there.
Getting all those plugins composed into something that boots takes three concepts: bundles, profiles, and patches. This page covers what each one holds and the order they apply in, following the architecture doc and development guide.
The three layers
Section titled “The three layers”Bundle — a distribution format holding a set of Cordis config rows plus the plugin code those rows mount. A bundle is a pre-assembled slice of functionality: model adapters, tools, persistence, an entire web app. What it inserts is ordinary config rows, which means anything it sets up can still be overridden by a layer that loads later.
Profile — a named, runnable composition, stored under $DSH_HOME/profiles/<name>. It records the bundles it stacks and their order, any out-of-tree plugins installed into it, and your own override file. Two profile templates ship with dsh: web for the browser UI, headless for one-shot runs with no server.
Patch — a YAML file that edits rows already loaded, addressing them by id. It can merge into a row’s config, switch a row off, or insert rows that weren’t there before. Patching is how you change a profile without forking the bundles underneath it.
How they stack
Section titled “How they stack”Composition starts from an empty list and applies layers in this order (source: architecture.md):
- Each bundle in the profile’s listed order (e.g.
dsh-base, thendsh-web-app). - The profile’s own
cordis.patch.yml. - The home-level
$DSH_HOME/cordis.patch.yml(applies to every profile). - Any
--patchoverlay passed on the command line.
dsh-base comes first in every profile. It’s the layer carrying model adapters, tools, persistence, sandbox and approval policy, settings, credentials, and telemetry. On top of that, dsh-web-app adds the browser UI, or dsh-headless adds a one-shot runner with no server.
To see the tree your own machine boots, with every layer already applied:
dsh --profile web --dump-configAny row that prints can be overridden by a patch of your own.
Where each piece lives on disk
Section titled “Where each piece lives on disk”- A profile directory (
$DSH_HOME/profiles/<name>/) holds apackage.jsonwith adsh.profile.bundleslist, plus acordis.patch.ymlfor your overrides. - A bundle declares itself the same way: its
package.jsonhas adsh.bundlefield pointing at its own patch file. - Bundles named in
dsh.profile.bundlesresolve from thedshinstallation first, then from the profile’s ownnode_modules(wherepnpminstalls anything out-of-tree you’ve added viadsh plugin add).
A minimal example
Section titled “A minimal example”The snippets below are trimmed down from dsh’s own headless-agent example and the dsh-headless bundle patch. Don’t paste them into a real profile. The row shapes are accurate; the contents are made up to be readable.
A bundle contributes ordinary rows, each with a stable id:
# a bundle's cordis.yml- id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: thinking: enabled reasoningEffort: max
- id: system-prompt name: '@deepseek-ai/dsh-system-prompt' config: persona: 'You are a coding agent.'A patch on top addresses rows by that same id. A partial config merges into the existing row, disabled: true switches one off, and insert: appends rows that didn’t exist:
# a patch (profile's or home-level cordis.patch.yml)- id: system-prompt config: persona: 'You are my custom assistant. Working directory: {{cwd}}.'
- id: some-optional-row disabled: true
- insert: - id: my-extra-tool name: '@deepseek-ai/dsh-tool-my-extra-tool'None of this requires touching the bundle’s source. The patch layer is the entire customization surface.
Further reading
Section titled “Further reading”- Architecture: Profiles and bundles — the authoritative source for this page.
- Development guide: Profiles — directory layout and resolution order.
- Config catalog — every config field every bundled plugin accepts.
- Cordis primer — the plugin framework itself.