Skip to main content
M

dsh-fusion

mouyu6508-hue/dsh-fusion

Reports conflicting DeepSeek Harness plugin composition — duplicate loader names across bundles (install-shadow), one id given two names, enable/disable fights, and config keys silently overwritten by a later layer — then emits a patch with the final merged values. Ships a CLI that runs when DSH itself will not boot, plus a fusion_report tool that reads the live loader.

Install

dsh plugin --profile web add github:mouyu6508-hue/dsh-fusion

README

dsh-fusion

English | 中文

Conflict detection and config fusion for DeepSeek Harness plugin composition — find the duplicate loader that keeps your profile from booting, and merge the config that a later layer silently overwrites.

DeepSeek Harness builds a profile in layers: the root cordis.yml → every bundle's cordis.patch.yml → the profile's own cordis.patch.yml. The more plugins you install, the more two things start happening — and neither is obvious:

  • The same loader gets mounted twice across bundles. The plugin market reports it as install-shadow, for example: @tt-a1i/archify-dsh introduced cross-layer duplicate loader names — @deepseek-ai/dsh-skill-filesystem (@deepseek-ai/dsh-base + @tt-a1i/archify-dsh)
  • The same config key gets silently overwritten by a later layer. No error, no log. You think your value took effect; it didn't.

dsh-fusion surfaces both: it reads the real layer stack, shows who overrode whom, and emits one patch containing only the final merged values.

dsh-fusion scan output

Install

From GitHub (current distribution):

dsh plugin --profile web add github:mouyu6508-hue/dsh-fusion

On the desktop client, paste the repository URL into Settings → Plugins.

If this ever lands on npm, the command becomes dsh plugin --profile web add dsh-fusion.

CLI (works even when DSH won't boot)

dsh-fusion scan                        # what's fighting in this profile?
dsh-fusion scan --profile web --json   # machine-readable
dsh-fusion fuse                        # print the fused patch
dsh-fusion fuse --fix --write          # keep one copy of a duplicate, write cordis.fusion.yml

Real output from the bundled demo profile:

profile: examples/conflict-profile
bundles: 3 | layers: 5 | entries: 3

conflicts: 1 | silent overrides: 2

[conflict] same loader mounted 2 times: @deepseek-ai/dsh-skill-filesystem
           ids skill-filesystem, archify-skill-filesystem (from @deepseek-ai/dsh-base + @tt-a1i/archify-dsh).
           the module runs twice; tools/services get registered twice or shadow each other
[override] dsh-dafeiyu config key scale overridden by profile:cordis.patch.yml
           1 → 0.55

fuse --fix turns the first one into a patch you can apply directly:

- id: archify-skill-filesystem
  disabled: true

--prefer <id> picks which copy to keep (default: the first one mounted, i.e. the base layer).

The fusion_report tool

Installing the plugin adds one tool:

ParameterMeaning
profileprofile name to inspect (default desktop)
dirprofile directory instead of a name
writealso write cordis.fusion.yml (default: report only)

It looks at two sources:

  • Static — the layered patch files. Useful before the host is even running.
  • Runtime — asks ctx.loader which entries are actually mounted. Some duplicates only appear at runtime: the plugin's patch file is clean, and it mounts a second copy of a module during apply(). The case above is exactly that — file inspection alone never finds it.

If the host doesn't expose the loader, it degrades to static-only instead of failing.

What it reports

KindSeverityMeaning
duplicate-nameconflictthe same loader is mounted on 2+ rows; the module runs twice
name-mismatchconflictone id given two different names by two layers
disabled-conflictconflictone layer enables, another disables
unknown-targetconflicta patch targets an id nobody inserted (the row is silently dropped)
config-overrideoverridea config key overwritten by a later layer
repeat-rownoticesame id + same name written twice (normal way the market saves settings)
dynamic-confignoticeconfig contains !!js expressions; can't be merged statically

What it does not do

  • It never changes running plugins. Read-only against the loader; no enable/disable. Auto-fixing a live graph breaks worse than it heals.
  • It never removes rows. The fused patch only pins final values; you decide which duplicate to drop (--fix writes the disabled: true for you).
  • It never touches the system prompt. A fusion tool shouldn't add more noise to it.
  • It never executes !!js. Expressions are kept as text; rows that can't be merged are skipped and named in the output.

Requirements

  • Node.js 18+
  • A DSH host that loads bundle plugins (dsh.bundle.patch)
  • Linux, macOS and Windows are all fine

Development

node --test                                   # or run test files directly (the runner spawns children, which some sandboxes block)
node bin/fusion.js scan --dir examples/conflict-profile
  • lib/core.js — pure functions: parse, detect, deep-merge, emit
  • lib/profile.js — filesystem only: read the profile layers in real order
  • lib/runtime.js — loader only: read what's actually mounted
  • lib/index.js — plugin entry: registers fusion_report

Search terms

dsh plugin conflict · install-shadow · duplicate loader entry · profile patch merge · cordis patch layer · silent config override · DSH 插件冲突

MIT

Related plugins