- Clone patch lists per generation (boot + composeLive): the include pushes insert rows by reference and mutates them in place, so a reused object baked user overrides into bundle rows and removal could not revert; the built-bin hot-reload e2e now asserts an override AND its removal reverting. - The headless runner awaits Loader settlement before prompting (its inject gate covers only apiProxy/httpServer) and abandons cleanly when the tree died during the wait. - healProfilesModuleFallback walks the app's full dependency+peer closure: out-of-tree plugins import seam packages (dsh-compact, dsh-subprocess, ...) that only implementations reach, and peers are how seams are declared. - Profile init writes pnpm-workspace.yaml (nodeLinker: hoisted), not .npmrc — pnpm >=10 reads settings from the workspace manifest. - Web dumps reject boot-only flags instead of printing a tree that differs from the same invocation's boot; --port validates at the flag; --dump-default-config no longer parses the (possibly broken) user layer; trustedHosts flag derivation merges over the composed value instead of replacing it; web-runtime gains surfaceContext (headless disables the GUI prompt/bash-vars the old -p never mounted); 'node_modules' is a reserved profile name; plugin-warning names the recovery step; client AGENTS.md registration surfaces point at the web-app bundle. - Ship session-reference/tmux-context/tool-ask-user as app dependencies for terminal front-door patch layers (turtle-ui), same stance as mcp-client.
@deepseek-ai/dsh
English | 中文
The dsh command is the product launcher for profiles: ordered stacks of plugin-bundle patch layers under the user's own overrides. src/args.ts owns the command grammar, and src/bin.ts loads only the selected runner. Invalid commands, options from another mode, configuration errors, and boot failures exit nonzero.
Entry modes
| Command | Purpose |
|---|---|
dsh --profile <name> |
Boot the named profile under $DSH_HOME/profiles/<name>. |
dsh --profile headless "task" |
Run one fresh persisted session, print the final answer, and exit. |
dsh web |
Alias of --profile web with the Web flag family (--host, --port, --dev, ...). |
dsh plugin --profile <name> <pnpm args> |
Manage a profile's plugins by forwarding to pnpm in the profile directory. |
The invoking directory is the default workspace root. The web and headless profiles auto-initialize on first use from shipped templates; any other profile must be created through dsh plugin.
Profiles
A profile directory holds a package.json (out-of-tree plugin dependencies plus the ordered dsh.plugins bundle list) and a cordis.patch.yml (the user's own patch layer, hot-reloaded on long-lived surfaces). The tree composes over an empty root: each bundle's patch in dsh.plugins order, then cordis.patch.yml, then --patch overlays, then flag patches. Bundles named in dsh.plugins resolve from the dsh installation first (@deepseek-ai/dsh-base, @deepseek-ai/dsh-web-app, @deepseek-ai/dsh-headless), then from the profile's own node_modules, where pnpm installs out-of-tree plugins. Use --dump-default-config and --dump-config to inspect the composed tree without booting it.
The CLI behavior reference owns exact layer precedence, flags, shutdown behavior, deployment defaults, and the source launcher.
Development
Production runs require built package and frontend artifacts. From a checkout, pnpm run dsh runs the TypeScript entry and forwards arguments; the source-launcher reference describes the PATH symlink and module-resolution contract.