Use dsh-harmony
Modify compiled DeepSeek Harness plugins at runtime through a separate Patch provider. Never edit or replace the installed target package.
Decide first
Identify the active profile, target package, installed version, compiled target file, runtime side, and intended behavior before writing a Patch.
| Need | Choose |
|---|---|
Patch a browser bundle such as lib/client.js |
Source Patch |
| Match syntax, literals, imports, or arbitrary compiled structure | Source Patch |
| Decorate a named Node.js function or class method | Semantic Patch |
| Load a target package that publishes TypeScript instead of runnable JavaScript | Loader Patch, plus exact Source Patches when needed |
| Change one or more compiled React call sites | dsh-harmony-react element(), which produces a Source Patch |
| Decorate or replace one initialized variable or named function React component binding | dsh-harmony-react component(), which produces a Source Patch |
| Expose explicit preview elements or editable variables to dsh-webui-studio | dsh-harmony-react/studio |
Use a Source Patch for every browser target. Semantic handlers execute in Node.js and do not support browser bundles, generators, or non-identifier parameters.
Install Harmony
Require Node.js ^22.22.3 or >=24.11.1 and either @deepseek-ai/dsh@0.1.0-rc.8 or @deepseek-ai/dsh@0.1.1-rc.1 for the current release.
node --version
npm install -g @deepseek-ai/dsh@0.1.1-rc.1
npm install -g dsh-harmony
dsh web
Verify the selected profile:
dsh harmony status
dsh harmony status --profile tui
Alternatively, install dsh-harmony through dsh plugin --profile web add dsh-harmony, then choose Install and restart on first boot. Use Settings -> Harmony in WebUI or dsh harmony --profile <name> in the terminal.
When working from a repository checkout, read its package.json first and follow its declared engines and peerDependencies; they supersede versions shown here.
Author a provider
Create an ordinary DSH plugin. Declare CommonJS Patch modules under dsh.harmony in package.json:
{
"name": "my-harmony-provider",
"dsh": {
"plugin": {
"compatibility": {
"requires": {
"base-plugin": "^2"
},
"conflicts": {
"legacy-patches": "*"
},
"integrates": {
"optional-renderer": "^1"
}
}
},
"harmony": {
"patches": ["./patches/answer.patch.cjs"],
"after": ["base-patches"],
"before": ["ui-patches"]
}
}
}
Treat before and after as provider package ordering constraints, not dependencies. Any DSH plugin may describe package relationships in dsh.plugin.compatibility: requires reports unavailable required plugins, conflicts warns about incompatible active plugins, and integrates reports available optional integrations. Keys are package names and values are semver ranges. These declarations never change plugin state or block startup. Disabling a Patch does not disable its owning plugin. Add inject = ['harmony'] only when the provider plugin itself requires the Harmony service.
Give each user-facing Patch a concise description of its effect. Harmony exposes it in status output and Settings.
Source Patch
Inspect the installed target's compiled file. Select the narrowest stable TypeScript AST shape with TSQuery and edit the current in-memory source through MagicString:
/** @type {import('dsh-harmony').HarmonyPatch} */
module.exports = {
id: 'answer-value',
description: 'Changes answer() to return 42.',
target: {
package: 'some-dsh-plugin',
version: '^1.2.0',
file: 'lib/index.js',
},
select: 'FunctionDeclaration[name.name="answer"] NumericLiteral',
expect: 1,
apply({ node, sourceFile, edit }) {
edit.overwrite(node.getStart(sourceFile), node.getEnd(), '42')
},
}
Always set a target version and an exact expect count. Positions passed to edit refer to the source produced by all earlier Patches. Keep edits local and non-overlapping; do not write target files.
React Patch
Use element() for concrete compiled jsx/jsxs calls and component() for an initialized component variable whose value is shared by every call site. Both return ordinary Source Patches and use Harmony's normal ordering:
const { element } = require('dsh-harmony-react')
module.exports = element({
id: 'wrap-submit',
description: 'Wraps the submit button with the provider boundary.',
target: {
package: 'some-dsh-plugin',
version: '^1.2.0',
file: 'lib/client.js',
},
select: { component: 'SubmitButton' },
expect: 1,
operation: {
kind: 'wrap',
with: { module: 'my-provider', export: 'SubmitBoundary' },
},
})
Element operations are replace, wrap, insert-before, insert-after, transform-props, and remove. Component operations are decorate and replace; their selector must match a variable declaration with an initializer or a named function declaration with a body. Harmony React rewrites a matched function declaration into an initialized binding so every call site sees the same composed result. That binding is not hoisted: use a core Source Patch when the target reads the component before its declaration. Name-based Component selectors emit Preview call-path traces; raw TSQuery selectors cannot infer a binding name and do not. Use a core Source Patch for component internals, string literals, or any other arbitrary source change.
Loader Patch
Use a Loader Patch only when the target package publishes TypeScript source that Node cannot load from node_modules. It enables Harmony's TypeScript transpiler for that package's .ts, .tsx, .mts, and .cts module graph before Node's default loader runs:
/** @type {import('dsh-harmony').HarmonyPatch} */
module.exports = {
id: 'load-published-typescript',
target: {
package: 'typescript-only-plugin',
version: '^1.0.0',
file: 'index.ts',
},
loader: 'typescript',
}
The target file is the compatibility anchor used for binding and status. Loading is limited to TypeScript files inside that exact package and version; unrelated packages retain Node's default behavior. Declare ordinary Source Patches separately when the source also needs modification. Harmony applies those exact-file edits before transpiling the module.
Semantic Patch
Use a semantic operation only for a named Node.js function declaration or class method:
/** @type {import('dsh-harmony').HarmonyPatch} */
module.exports = {
id: 'answer-after',
target: {
package: 'some-dsh-plugin',
version: '^1.2.0',
file: 'lib/index.js',
function: 'answer',
},
operation: 'after',
expect: 1,
handler({ result }) {
return result + 1
},
}
Choose the operation deliberately:
before: optionally replace the argument array.after: optionally replace the sync or async result.around: call or skipinvoke(args?)around the next layer.replace: own the function throughinvoke(args?); only one enabled replacement may target it.
Install and validate a provider
Install the provider into the same profile as its targets, then validate the runtime result:
dsh plugin --profile web add ./my-harmony-provider
dsh harmony status --profile web
dsh harmony status --json --profile web
dsh harmony inspect some-dsh-plugin --file lib/index.js --profile web
dsh harmony disable my-harmony-provider/optional-patch --profile web
dsh harmony enable-provider my-harmony-provider --profile web
dsh harmony patch-order show --profile web
dsh harmony patch-order move my-harmony-provider/optional-patch --after another-provider/base --profile web
dsh harmony patch-order auto --profile web
dsh harmony provider-order move my-harmony-provider --after another-provider --profile web
dsh harmony inspect some-dsh-plugin --patch my-harmony-provider/optional-patch --summary --profile web
dsh harmony reload my-harmony-provider --profile web
Require all intended Patches to reach bound. Treat status exit code 1 as a Patch, reload, or ordering failure. patch-order show and provider-order show also exit 1 when constraints are violated. Use inspect to compare the original source, every ordered intermediate result, and the final source; add --summary to omit source or --patch <key> to select one Patch. reload requires a live Host. Confirm hot reload or restart behavior through the target feature.
Use Settings or dsh harmony to reorder and enable or disable Patches. Do not edit $DSH_HOME/profiles/<name>/harmony.json while the profile is running; UI and CLI changes are preflighted and committed transactionally.
Control a profile from another plugin
Use the Cordis service when already inside the running profile. order and patchOrder must be complete permutations of their current lists; omitted fields keep their current values. The result identifies the committed Patch generation and reload status:
export const inject = ['harmony']
export async function apply(ctx) {
const current = ctx.harmony.profile()
const result = await ctx.harmony.updateProfile({
order: current.order,
disabled: ['my-provider/optional-patch'],
})
ctx.logger.info(`Harmony generation ${result.generation}: ${result.reload.state}`)
}
Call the public profile update API from any other local process. It automatically uses the running Harness transaction when that profile is active, and otherwise validates and atomically updates the stopped profile on disk:
import {
preflightHarmonyProfileUpdate,
readHarmonyProfile,
updateHarmonyProfile,
} from 'dsh-harmony'
const current = readHarmonyProfile(profileDir)
const candidate = preflightHarmonyProfileUpdate(profileDir, { order: current.order })
const saved = await updateHarmonyProfile(profileDir, { disabled: candidate.disabled })
console.log(saved.mode === 'live' ? saved.reload.state : 'saved for next start')
Offline preflight validates and normalizes profile state without writing it. It does not start a Host or bind target Patches; runtime binding still happens when that profile starts. Do not import internal control/profile modules or edit harmony.json directly.
Diagnose failures
Check these in order:
- Confirm the selected profile contains both the provider and target.
- Confirm
target.versionaccepts the installed target version andfilenames a compiled file that exists. - Compare
expectwith the actual selector match count against the current compiled shape. - Inspect earlier Patch outputs; a prior Patch may have changed or removed the selected node.
- Replace a browser Semantic Patch with a Source Patch.
- Resolve duplicate semantic
replaceownership, overlapping source edits, or violated provider order. - Check
dsh.plugin.compatibilityfindings for unmet requirements or conflicts, and treat contradictorybefore/afterconstraints as ordering problems. - When load or reload performance is in question, start DSH with
DSH_HARMONY_PERF=1and compare the reportedprepareMs,transformMs,hostReloadMs,clientRebuildMs, andtotalMsfields. Node.js diagnostic tools may subscribe to thedsh-harmony:loaddiagnostics channel instead of enabling logs.
Harmony skips an individual Patch that cannot match or apply, marks it failed, and continues with later Patches. Treat the warning and status exit code 1 as work to fix even though the Host remains available. A provider declaration that cannot load or a target reload that cannot commit still rolls back the candidate generation. Never repair a failure by modifying the installed target package or weakening expect without verifying the new compiled structure.
Completion check
- Keep Patch IDs stable and unique within the provider.
- Pin a compatible target version and compiled file.
- Require exact matches and inspect the final transformed source.
- Verify the intended behavior in the correct profile and runtime side.
- Confirm installed target files remain unchanged.
Use the current project documentation as the authority for details: installation, Patch authoring, operations, React integration, Studio integration, CLI, and limitations.