dsh-paoding 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +27 -32
  2. package/README_en.md +27 -32
  3. package/bin/dsh-paoding.mjs +73 -11
  4. package/docs/architecture.md +41 -34
  5. package/docs/architecture_en.md +65 -42
  6. package/docs/configuration.md +159 -65
  7. package/docs/configuration_en.md +161 -66
  8. package/docs/images/1.png +0 -0
  9. package/docs/installation.md +100 -237
  10. package/docs/installation_en.md +112 -252
  11. package/docs/orchestration.md +34 -38
  12. package/docs/orchestration_en.md +34 -38
  13. package/package.json +8 -5
  14. package/plugins/paoding-config-ui/api-core.mjs +215 -18
  15. package/plugins/paoding-config-ui/index.mjs +414 -154
  16. package/plugins/paoding-config-ui/lib/base.css +1083 -42
  17. package/plugins/paoding-config-ui/lib/client.js +2266 -252
  18. package/plugins/paoding-config-ui/package.json +2 -2
  19. package/plugins/paoding-config-ui/runtime-inventory.mjs +27 -26
  20. package/presets/orchestrator/agent.cordis.yml +19 -9
  21. package/presets/orchestrator/preset.yml +3 -2
  22. package/presets/orchestrator/restrict.mjs +2 -1
  23. package/tools/install.mjs +9 -4
  24. package/tools/lib/alloc.mjs +60 -29
  25. package/tools/lib/cli.mjs +127 -278
  26. package/tools/lib/compose.mjs +261 -95
  27. package/tools/lib/config.mjs +266 -86
  28. package/tools/lib/host.mjs +55 -24
  29. package/tools/lib/skills.mjs +72 -45
  30. package/tools/lib/state.mjs +172 -40
  31. package/tools/lib/upgrade.mjs +170 -0
  32. package/tools/lib/util.mjs +7 -0
  33. package/tools/lib/version.mjs +233 -0
  34. package/tools/lib/workspaces.mjs +74 -0
  35. package/tools/lib/yaml.mjs +92 -15
  36. package/docs/images/2.png +0 -0
  37. package/docs/images/3.png +0 -0
  38. package/docs/images/4.png +0 -0
  39. package/docs/images/5.png +0 -0
  40. package/docs/images/6.png +0 -0
  41. package/install.sh +0 -31
  42. package/tools/lib/wizard.mjs +0 -516
@@ -4,364 +4,224 @@
4
4
 
5
5
  # Installation
6
6
 
7
- This document is for users who install, upgrade, or uninstall the dsh-paoding「编排模式 (Orchestrator)」preset ("编排模式" is Chinese for "Orchestration Mode"): §1 prerequisites, §2 one-command install with npx and the official `dsh plugin` channel (config UI), §3 interactive wizard install, §4 non-interactive / config-driven install, §5 host patch detection at install time, §6 applying, defaulting, upgrading and uninstalling, §7 the optional visual config UI in the Settings page, §8 troubleshooting.
7
+ This document is for users who install, upgrade, or uninstall the dsh-paoding「编排模式 (Orchestrator)」preset ("编排模式" is Chinese for "Orchestration Mode"): §1 prerequisites, §2 the single official install channel (`dsh plugin add`) and first-install automation, §3 upgrading, §4 dev-checkout install, §5 host patch detection at generation time, §6 the Paoding Config panel, §7 uninstalling, §8 troubleshooting.
8
8
 
9
9
  ## 1. Prerequisites
10
10
 
11
11
  Check the following before installing:
12
12
 
13
- - **Node.js ≥ 18**. `install.sh` checks that `node` is on the PATH at startup (it prints `error: node (>=18) is required…` and exits when missing). The installer (`tools/install.mjs`) uses only Node built-in modules and has zero runtime dependencies — **no `npm install` is needed after cloning**; `./install.sh` works directly (running `node tools/install.mjs` is equivalent).
14
- - **A working DSH host** (the `dsh` command, including its Web/agent runtime). This tool does not modify DSH source code; it only writes into DSH's preset roster and patch layers, so a usable DSH home directory must exist first.
15
- - **Path facts** (constants built into the installer; know them before running):
13
+ - **Node.js ≥ 18**. The DSH host and the plugin chain both run on Node; with an older version `dsh` won't start in the first place. Check with `node --version`.
14
+ - **pnpm available**. `dsh plugin` is a transparent pass-through to pnpm: installing a plugin package into a profile is done by pnpm. If pnpm is missing the install command fails with a pnpm error — install pnpm (`npm i -g pnpm` or `corepack enable`) and retry.
15
+ - **A working DSH host** (the `dsh` command, including its Web/agent runtime; **@deepseek-ai/dsh ≥ 0.1.5**, the minimum supported version). This plugin does not modify DSH source code; it only writes the orchestrator preset into DSH's preset roster and mounts the Paoding Config page into the Web sidebar, so a usable DSH home directory must exist first.
16
+ - **Path facts** (constants built into the generation pipeline; know them before customizing):
16
17
 
17
18
  | Item | Default | Description |
18
19
  |---|---|---|
19
- | `$DSH_HOME` | `~/.dsh` | The DSH home directory (both `install.sh` and `install.mjs` honor the `DSH_HOME` environment variable, defaulting to `$HOME/.dsh`). The home-layer `cordis.patch.yml`, `node_modules`, and `profiles/<name>/cordis.patch.yml` live under it |
20
+ | `$DSH_HOME` | `~/.dsh` | The DSH home directory (the `DSH_HOME` environment variable is honored, defaulting to `$HOME/.dsh`). The home-layer `cordis.patch.yml`, `node_modules`, `profiles/<name>/cordis.patch.yml`, and plugin profile directories live under it |
20
21
  | preset roster root | `$DSH_HOME/.agent-presets/` | The directory `dsh-agent-presets` scans for locally authored presets |
21
- | Install target of this preset | `$DSH_HOME/.agent-presets/orchestrator` | The install artifact: `agent.cordis.yml` (rewritten role config), `preset.yml`, `restrict.mjs` — a static directory |
22
- | Config file | `$DSH_HOME/dsh-paoding.config.yml` | Role/tool assignments saved by the wizard or the config UI (`--config` selects another path) |
23
- | Package copy (npx/npm install) | `$DSH_HOME/dsh-paoding` | When installing via the npx/npm entry, the installer copies the whole package to this stable location and mounts the config UI from there (see §2) |
22
+ | Install target of this preset | `$DSH_HOME/.agent-presets/orchestrator` | The generated artifact: `agent.cordis.yml` (rewritten role config), `preset.yml`, `restrict.mjs`, and the `.generator-version` marker — a static directory |
23
+ | Config file | `$DSH_HOME/dsh-paoding.config.yml` | Role/tool assignments written by the Paoding Config panel's Save & Apply (`0o600`, hand-editable); first-install automation reads and writes it too |
24
24
 
25
- - **Get the source**: clone the repository and enter the directory (skip this step entirely if you just want the one-command npx install in §2):
25
+ - **Environment variables**: `DSH_HOME` overrides the DSH home directory (default `~/.dsh`); `DSH_PAODING_CONFIG` overrides the config-file path (honored by the panel; the in-repo fallback CLI uses `--config <file>` instead).
26
26
 
27
- ```bash
28
- git clone https://github.com/lifangjin/dsh-paoding.git dsh-paoding
29
- cd dsh-paoding
30
- ```
27
+ Inside the repository, `presets/orchestrator/` is the static preset source (the pipeline reads and rewrites `agent.cordis.yml`; `preset.yml` and `restrict.mjs` are copied as-is — the one exception: when `main_agent_display_name` (or a workspace-derived display name) is configured, the `name:` line of `preset.yml` is rewritten), `tools/` is the generator itself (shared by the panel and the fallback CLI), and `plugins/paoding-config-ui/` is the Paoding Config panel plugin. For plain usage you never need to touch any of these — see the next section.
31
28
 
32
- Inside the repo, `presets/orchestrator/` is the static preset source (the installer reads and rewrites `agent.cordis.yml`, and copies `preset.yml` and `restrict.mjs` as-is), `tools/install.mjs` is the installer itself, `install.sh` is a thin shell wrapper, and `plugins/paoding-config-ui/` is the optional visual config UI plugin (see §7).
29
+ ## 2. Install: the single official channel
33
30
 
34
- ## 2. One-Command Install with npx
35
-
36
- The least-effort entry point: no clone, one command (available once the package is published to npm):
31
+ No clone needed — one command, then one restart:
37
32
 
38
33
  ```bash
39
- npx dsh-paoding@latest
34
+ dsh plugin --profile web add dsh-paoding
40
35
  ```
41
36
 
42
- Before the npm release, run it straight from the GitHub repository with the same effect:
37
+ Equivalent shortcut: `npx dsh-paoding@latest` (delegates to the command above; default profile web; requires dsh on PATH).
43
38
 
44
- ```bash
45
- npx github:lifangjin/dsh-paoding
46
- ```
39
+ After installing, **restart DSH (`dsh web`)** and you're done — no further install commands, no buttons to click.
47
40
 
48
- The npm package and its command share the name: package `dsh-paoding`, command `dsh-paoding`. This default route equals `--auto --config-ui` and does three things at once:
41
+ Behind the scenes the command does two things:
49
42
 
50
- 1. generates the orchestrator preset (`$DSH_HOME/.agent-presets/orchestrator`);
51
- 2. writes the base config template (`$DSH_HOME/dsh-paoding.config.yml`);
52
- 3. mounts the web config UI into the DSH Settings page (设置 → 庖丁配置 / Settings → Paoding Config).
43
+ 1. `dsh plugin`, a transparent pass-through to pnpm, installs the npm package `dsh-paoding` into the given profile's `node_modules`;
44
+ 2. the `dsh.bundle.patch` declared at the package root (a `cordis.patch.yml` with a single insert: `id: paoding-config-ui` / `name: dsh-paoding`) is merged by `reconcilePlugins` into that profile's startup layer, and at boot the Paoding Config page is mounted into the Web sidebar's bottom action bar (above the Settings row).
53
45
 
54
- **Out of the box: the base template.** A fresh install writes a template that only contains DSH's built-in base tools — the main agent's 21 core tools plus each delegated role's base set; detected host/MCP tools are **not written in automatically**. The detection pipeline still runs: open Settings → 庖丁配置 and the candidate list shows every recognized tool, so you can tick what you need and hit Save & Apply; or run `npx dsh-paoding --wizard` to assign everything in a guided pass.
46
+ The orchestrator preset needs no separate install — the plugin fills it in automatically at startup; see first-install automation below.
55
47
 
56
- Flags this route accepts:
48
+ ### Profile semantics
57
49
 
58
- | Flag | Effect |
59
- |---|---|
60
- | `--suggest` | On a fresh install, use smart defaults instead: detected MCP/host tools are assigned automatically to roles and the main agent (the old behavior) |
61
- | `--no-ui` | Skip mounting the config UI |
62
- | `--wizard` | Force the interactive wizard |
50
+ `--profile web` decides which profile the plugin is installed into: the Paoding Config page appears only in a DSH Web launched with that profile. `web` is the default profile of `dsh web`, so most machines need no change; multi-profile users run `dsh plugin add` once per profile they want it in. The orchestrator preset itself lives under `$DSH_HOME/.agent-presets/` and is shared machine-wide, independent of profiles — Save & Apply from any profile's panel writes the same preset.
63
51
 
64
- **How the UI is mounted in the npm form.** The package directory npx runs from lives in the cache and can be cleaned at any time, so mounting straight from it is not durable. Instead, the installer copies the whole package to the stable location **`$DSH_HOME/dsh-paoding`** and mounts the config UI from there. The repo-clone route (`./install.sh`) still symlinks directly into the repository (see §7) — the two forms differ only in where the plugin is mounted from; to DSH it is the same plugin.
52
+ ### First-install automation: restart = full install
65
53
 
66
- **Existing config files are respected**: when `$DSH_HOME/dsh-paoding.config.yml` already exists, re-running any entry point (npx or `./install.sh`) applies your config file instead of overwriting your choices.
54
+ At startup the plugin checks whether the orchestrator preset is in place: if the `$DSH_HOME/.agent-presets/orchestrator` directory is missing, or its `.generator-version` marker does not match the plugin package version, it automatically performs one install equivalent to `--auto` — applying `~/.dsh/dsh-paoding.config.yml` when it exists, writing the base template otherwise (DSH base tools only; see below). It runs the same detection/generation pipeline as the panel's Save & Apply (`collectState` / `generateAndInstall`) — the same pipeline, but the startup self-heal detects with a pure file scan and no runtime detection facts while the panel detects with them; when a bundle-form plugin is installed on the host, the two detection inventories may differ, and the outputs differ slightly with them.
67
55
 
68
- When it finishes, **restart DSH (`dsh web`)**; the remaining steps are covered in §6.
56
+ So **`dsh plugin add` + restart = a complete install**: preset generated, config template on disk, panel ready — all in one step. If the automatic generation fails it never takes the plugin down — a warning is logged, and you can apply manually from the panel.
69
57
 
70
- ### The official plugin channel: per-profile UI install
58
+ ### The version marker and auto-regeneration
71
59
 
72
- Besides npx, the config UI can also be installed on its own through DSH's official plugin channel:
60
+ The `.generator-version` marker (its content = the generator version) is written by the plugin's startup self-heal after it generates the preset. The panel's Save & Apply (and the fallback CLI) do a full-directory rebuild and write no marker — the rebuild removes the old marker, so the next DSH start's self-heal finds it missing, runs one extra generation, and rewrites the marker (same output, just one extra round). The version-mismatch trigger is unchanged: after a plugin upgrade the marker no longer matches the package version, so the next DSH start regenerates the preset against the new version — the "upgraded the plugin but the preset is stale" drift is eliminated. The marker triggers regeneration only when missing or version-mismatched, and the panel does not watch the config file; to regenerate from the current config right away, hit Save & Apply.
73
61
 
74
- ```bash
75
- dsh plugin --profile web add dsh-paoding@latest
76
- ```
62
+ ### Base template and tool opt-in
77
63
 
78
- Restart DSH (`dsh web`) afterwards and the「庖丁配置」(Paoding Config) section appears in Settings; uninstall with `dsh plugin --profile web remove dsh-paoding`. The mechanism in one line: the `dsh-paoding` npm package declares a `dsh.bundle.patch` at its root (a `cordis.patch.yml` with one insert row, `id: paoding-config-ui` / `name: dsh-paoding`), and `reconcilePlugins` folds it into that profile's startup layer — the same npm package as the npx channel, just a second way to install it.
64
+ A fresh install (no config file) generates the base template: only DSH's built-in base tools — the main agent's 21 core tools plus each delegated role's base set. Detected host/MCP tools are **not written in** by default. The detection pipeline still runs: open 庖丁配置 in the sidebar, the candidate list shows every recognized tool, opt in as needed, and hit Save & Apply.
79
65
 
80
- **This channel carries the UI only** (by design it cannot install the orchestrator preset or the config file — that half goes through npx / `./install.sh`), so the full setup takes two steps:
66
+ ### Apply & activate
81
67
 
82
- ```bash
83
- dsh plugin --profile web add dsh-paoding@latest # the config UI, effective in the given profile only
84
- npx dsh-paoding@latest --no-ui # the preset + config template; --no-ui avoids mounting the UI twice
85
- ```
68
+ Once installed:
86
69
 
87
- Or keep it to the single npx command — it mounts the UI by default. **The recommendation stands**: one-command npx remains the first choice; the plugin channel is there for people who prefer the official channel or want the UI isolated per profile.
88
-
89
- **Semantic difference**: a UI installed via the plugin channel takes effect only in the profile you named (e.g. `web`); the npx / `./install.sh` mount writes into the global `$DSH_HOME/cordis.patch.yml` and takes effect in every profile.
70
+ 1. **Restart the host, or open a new session** (a preset is loaded when a session is created; the preset selector refreshes per new session).
71
+ 2. Pick「**编排模式 (Orchestrator)**」in the **new-session preset selector**.
72
+ 3. To make it the default for every new session: set it in **Settings → Agent Presets**.
90
73
 
91
- **Don't mix the two**: with both mounts side by side on one machine, the Settings page shows a duplicate「庖丁配置」section — two insert rows from different sources, both active. To migrate from the old global mount to the plugin channel: run `dsh plugin --profile web add dsh-paoding`, then delete the `paoding-config-ui` section from `$DSH_HOME/cordis.patch.yml` and the `$DSH_HOME/node_modules/paoding-config-ui` symlink, and restart; the reverse migration works the same way.
74
+ **Applied a hand-edited config — now what?** Open the 庖丁配置 (Paoding Config) entry in the sidebar bottom action bar (above the Settings row) — the panel loads the hand-edited config file as its current values — then hit「**保存并应用**」(Save & Apply). That is the only apply trigger for a pure plugin user; the panel does not watch the config file, and the version marker only triggers auto-regeneration on new versions.
92
75
 
93
- ## 3. Interactive Installation
76
+ ## 3. Upgrading
94
77
 
95
- ### Starting the wizard
78
+ **One-click upgrade in the panel (recommended)**: the panel compares your local version against the latest npm registry publish (GitHub release as fallback) and points to the release page when a newer one is out. Click the Upgrade button and the panel runs this in place:
96
79
 
97
80
  ```bash
98
- cd dsh-paoding
99
- ./install.sh
81
+ dsh plugin --profile <name> update dsh-paoding
100
82
  ```
101
83
 
102
- Starting the wizard is a thin pass-through: `install.sh` forwards all arguments to `node tools/install.mjs`. Run it bare in a terminal and the interactive wizard starts. The wizard only auto-starts when **stdin is a TTY**; on a non-TTY it refuses and suggests `--auto` (see §4). Use `--wizard` to force the wizard in scripts/CI. After a welcome line, questions proceed through the six stages below; **pressing Enter on any question accepts the default / keeps the current value**.
103
-
104
- Before asking anything, the wizard runs a full detection pass (the same pipeline shared with `--auto` / `--dry-run`) and prints the result as a **detection report**; when a config file already exists, its contents seed the initial values (anything unset falls back to smart defaults or the static base).
105
-
106
- ### The six wizard stages
107
-
108
- **Stage ① Detection report**: lists the tools detected this run; it matches the detection pipeline described in §5:
109
-
110
- - **MCP servers**: one line per enabled server, with its name, transport, and the resolved **exact tool names** (`mcp__<server>__<tool>`, e.g. `mcp__codegraph__codegraph_explore`, `mcp__tavily__tavily_search`). Well-known servers (tavily, codegraph) are resolved from a static table; unknown servers go through a live JSON-RPC handshake (stdio / streamable-http); servers whose handshake failed or whose transport is unsupported (sse) show as having no resolvable tools (the install still proceeds).
111
- - **Local tool plugins**: e.g. `magic-memory` → tool `memory_search`.
112
- - **Installed skills**: four roots are scanned — `$DSH_HOME/skills`, `~/.agents/skills` (`DSH_AGENTS_HOME` overrides `~/.agents`), and `.dsh/skills` plus `.agents/skills` under the working directory where the installer runs (in each root a directory or a `.md` file counts as one skill, named by its basename).
113
-
114
- **Stage ② Tool assignment**: for every MCP server that resolved at least one tool, the wizard prints its tools and the **current assignment** (seeded from the existing config or smart defaults), then asks where to assign the whole group: one of the three default roles `search_external` / `design` / `implement`, the `主 agent` (main agent), `不分配` (assign to none), an existing custom role, or `[0]` to create a new role (Enter keeps the current assignment). Choosing the main agent removes that server's tools from every role and adds them to the main-agent extras (`main_agent_extra`); choosing "assign to none" removes them from both the roles and the main agent.
84
+ (the profile is auto-detected; usually nothing to fill in). The upgrade only swaps the plugin package inside the profile — the running process's code is untouched; on success it asks you to **restart DSH**, and at restart the preset regenerates itself against the new version via the `.generator-version` marker.
115
85
 
116
- **Stage ③ Custom sub-agents**: there are two entry points — typing `[0]` while assigning a server in stage ② (that server's tools are then merged into the new role automatically), or answering the subsequent "创建自定义角色?[y/N]" loop (any number of roles may be created). Each new role is filled in step by step:
86
+ Manual equivalent: run the `dsh plugin update` command above yourself, restart, done.
117
87
 
118
- 1. **toolName**: must start with a lowercase letter and contain only lowercase letters, digits, and underscores (regex `/^[a-z][a-z0-9_]*$/`). An invalid name, a name that already exists, `done`, or an empty line all abort this creation (the reason is printed and control returns — the wizard does not re-prompt).
119
- 2. **Tool-pool multi-select**: the tool pool = the three default roles' static allow ∪ every detected host tool, listed by index. Type comma-separated indexes (e.g. `1,3,5`); `all` selects everything, `none` clears.
120
- 3. **One-line persona**: type a short description; it is expanded to `You are the <name> agent. <描述>`. Leaving it blank makes the generator use the same-sentence default description (`You are the <name> agent. Handle tasks delegated to this role.`).
88
+ A **dev link: checkout** (see §4) still gets version checks (the update card appears when a newer version exists) but cannot be upgraded in place — the upgrader requires the package to really live under a profile's node_modules, which a link straight into the repo never matches. Hitting Upgrade reports exactly that and suggests running `dsh plugin add dsh-paoding@latest` to switch back to the registry version.
121
89
 
122
- The new role's toolName is automatically injected into the main agent's restrict `config.allow` at install time (no need to edit `restrict.mjs`), so the main agent can delegate to it by calling that toolName in conversation.
90
+ If the check fails (offline, rate-limited), it stays silent and nothing is interrupted.
123
91
 
124
- **Skill-typed play example**: to turn "making PPTs" into a dedicated agent — in stage ③ set `toolName` = `ppt`,
125
- tick `skill` in the tool pool (the sub-agent loads skill rules on demand via the skill tool) plus `read` /
126
- `write` / `edit` / `bash` etc., and write a one-line persona such as "You are the ppt agent. Turn outlines into
127
- HTML slides: load the html-ppt skill, plan the deck, write the HTML files, then report the output path."; then
128
- in stage ⑤ assign the `html-ppt` skill to the `ppt` role (an Available skills guidance line is appended to its
129
- persona). After install, a single "turn this outline into a PPT" delegates to a dedicated slide-making
130
- sub-agent; any installed skill can become a dedicated role the same way.
92
+ ## 4. Dev-checkout install (link: form)
131
93
 
132
- **Stage ④ Fine-tuning the default roles (optional)**: for `search_external` / `design` / `implement` one by one, the wizard asks whether to change the persona (multi-line input; a lone `.` line ends it, blank lines keep the default), add tools by tool-pool index, and remove tools by index from the current list — every question can be skipped with Enter. Three more questions follow: **deleting built-in roles** (space-separated names; Enter deletes none, and already-deleted roles stay deleted; `search_internal_deep` cannot be deleted), the **main-agent name** (Enter keeps the default or the existing name), and **dedicated models for the built-in roles** (after a yes/no gate the wizard asks each role for a model id — Enter means follow the main agent's session model; provider is only asked when the model is non-empty). These land in the config keys `roles_remove` / `main_agent_name` / `roles.<toolName>.model` (+`provider`) — see [Configuration](configuration_en.md) 2.3, 2.5 and 5.1. Last, the wizard prints the main agent's current tool surface and asks whether to **remove base tools** (by index, Enter to skip) and **restore previously removed tools** (by index, Enter to skip) — emptying the main agent's tools entirely is refused on the spot.
133
-
134
- **Stage ⑤ Skill assignment**: three consecutive questions:
135
-
136
- - **Role skill assignment**: for each detected skill, choose which roles it should be soft-guided to (the assignable list = roles whose allow contains `skill`; `[0]` assigns to none, Enter keeps the current value). A skill assigned to a role is written at install time into that role's persona as a trailing `Available skills: …` guidance line. Because DSH makes skill directories fully visible to every role, this is a **soft constraint (guidance), not hard isolation**.
137
- - **Main-agent skills `main_agent_skills` (soft guidance)**: when selected, the generator appends one compact skill row per skill to the **main agent's** persona (`# skill: <name> — <描述>` plus `Read full rules: <absolute SKILL.md path>`). The main agent carries no skill tool / skill catalog (directory injection drops to zero); when a task matches a skill it loads the full rules on demand with its own `read` tool — one persona line per skill, nothing paid when unused.
138
- - **`main_agent_skills_inline` (optional, hard inline)**: the wizard then asks which of those skills must take effect unconditionally by **inlining the full text** — each selected skill's SKILL.md (YAML frontmatter stripped) is embedded verbatim in the main-agent persona, at a fixed cost per response; suited to style skills such as `caveman` that must always apply. A skill listed in both places is only inlined; its soft row is not written.
139
-
140
- Skills whose SKILL.md cannot be found are **warned about and skipped** (the install does not abort).
141
-
142
- **Stage ⑥ Confirm → install → persist**: the wizard prints a summary — every role (including custom ones) with its tool count and persona first line, the main agent's extras/removals/skills (soft rows and inlined ones), and each skill's target roles — plus, only when applicable, three extras: a per-role model suffix (`| 模型: provider/model`, "model", shown only for roles with one configured), a 「内置角色已删除」("built-in roles deleted") line, and a 「主 agent 名称」("main-agent name") line (those two are printed only when built-in roles were deleted / the name was changed) — then asks 「确认安装并写入配置?[Y/n]」(default Y; answering n prints 「已取消,未写入任何文件」and exits). On confirmation:
143
-
144
- - the target directory `$DSH_HOME/.agent-presets/orchestrator` is rebuilt: `agent.cordis.yml` is rewritten according to the assignments (role allow lists and personas, custom-role blocks, main-agent restrict `config.allow` injection; the generated YAML is structurally validated before writing), while `preset.yml` and `restrict.mjs` are copied as-is;
145
- - an install report is printed (scanned patch layers, detected tools, each role allow's intent → kept → removed with removal reasons, and next steps);
146
- - all choices are serialized into the config file (below).
147
-
148
- ### Config persistence
149
-
150
- The wizard (and the config UI's "保存并应用" (Save & Apply) button) persists every choice to **`$DSH_HOME/dsh-paoding.config.yml`** (a header comment in the file states it was generated by the interactive wizard, may be hand-edited, and that changes are applied by re-running `./install.sh --auto`). After editing the config (by hand or by re-running the wizard), run:
151
-
152
- ```bash
153
- ./install.sh --auto
154
- ```
155
-
156
- to **idempotently** apply the new assignments to the preset (the config is applied when present; a fresh install writes the base template instead — `--suggest` for smart defaults — see §4). The config file's key structure is documented in [Configuration](configuration_en.md).
157
-
158
- ## 4. Non-interactive Usage
159
-
160
- When running without a terminal (scripts, CI, scheduled re-runs), use the non-interactive flags below. Typical invocations:
94
+ To hack on the plugin or the preset sources, point the plugin at the repository in link form (pnpm link semantics):
161
95
 
162
96
  ```bash
97
+ git clone https://github.com/lifangjin/dsh-paoding.git
163
98
  cd dsh-paoding
164
- ./install.sh --auto # apply the config when present, else write the base template (--suggest for smart defaults, below)
165
- ./install.sh --auto --dry-run # only print the detection report and the allow lists to be generated; write nothing
166
- ./install.sh --auto --config /path/to/dsh-paoding.config.yml
167
- ./install.sh --auto --config-ui # apply the preset and mount the visual config UI (see §7)
168
- ./install.sh --wizard < answers.txt # force the wizard even on a non-TTY, reading answers from stdin
99
+ dsh plugin --profile web add link:"$PWD"
169
100
  ```
170
101
 
171
- CLI options (from `tools/install.mjs` usage):
172
-
173
- | Option | Description | Default |
174
- |---|---|---|
175
- | `--auto` | Non-interactive: apply the config file when present; otherwise write the **base template** (DSH's built-in base tools only; with `--suggest` it uses smart defaults — identical to the static preset — zero regression — when no host tools exist) | off |
176
- | `--config <file>` | Config file path (`--config=<file>` also accepted) | `$DSH_HOME/dsh-paoding.config.yml` |
177
- | `--wizard` | Force the interactive wizard; reads answers line by line from stdin even when it is not a TTY, for scripts/CI (the npx entry uses it to reach the wizard too) | off |
178
- | `--suggest` | On a fresh install, use smart defaults instead: detected MCP/host tools are assigned automatically to roles and the main agent (the old behavior) | off |
179
- | `--no-ui` | Skip mounting the config UI (the npx one-command install mounts it by default; this turns that off) | off |
180
- | `--profile <name>` | Which profile's patch layer is scanned | `web` |
181
- | `--patch <file>` | Extra patch override file(s) to scan; repeatable (see §5) | none |
182
- | `--dry-run` | Print only the detection report and the allow lists to be generated; write nothing | off |
183
- | `--config-ui` | Also mount the config UI into the DSH Settings page while applying the preset (see §7) | off |
184
- | `--help` / `-h` | Print usage and exit | — |
185
-
186
- Environment: `DSH_HOME` overrides the home directory (default `~/.dsh`).
187
-
188
- **Base template** (the new default on a fresh install — `--auto` with no config file): only DSH's built-in base tools are written — the main agent's 21 core tools plus each delegated role's base set; detected host/MCP tools are not written in. The detection report still prints and the config UI's candidate list still shows every recognized tool, so you can opt in after installing.
189
-
190
- **Smart defaults** (only with `--auto --suggest` and no config file — the old behavior): `codegraph` MCP tools and `memory_search` go to the main agent; other MCP servers are routed to the three default roles by tool-name keywords (names matching `search|research|crawl|extract|map|web` → `search_external`; `code|fs|file|write|edit|bash|exec|run` → `implement`; `design|render|image|screenshot|paint` → `design`; otherwise → `search_external`); a role that would end up with an empty allow refuses the **installation**.
102
+ The package sits directly on the repository directory; changes go live via HMR or a restart, with no reinstalls. This form cannot be upgraded in place (see §3); to switch back to the registry version, run `dsh plugin --profile web add dsh-paoding@latest`.
191
103
 
192
- Writing `main_agent_extra: []` explicitly in the config means "intentionally empty": re-runs never backfill it from smart defaults; only configs that lack the key get backfilled.
104
+ An **in-repo fallback CLI** also exists (not advertised; for developers who cloned the repo): `node tools/install.mjs --auto` (apply the config if present, else write the base template), `--dry-run` (print the detection report and the would-be allow lists without writing anything), `--config <file>` (select a config file), `--suggest` (on a fresh install without a config file, seed smart defaults that assign host tools; the default base template leaves host tools out), `--profile <name>` (which profile's patch layer to scan, default `web`). Without `--auto` (and without the read-only `--dry-run`) it refuses to run — interactive install has been removed; visual configuration belongs to the Web panel that ships with the plugin channel.
193
105
 
194
- **Non-TTY stdin guard**: when stdin is not a TTY and none of `--auto`, `--config` (no existing config), or `--dry-run` is given, the installer errors out immediately (suggesting `--auto`) instead of letting the wizard hang in a terminal-less environment. `--dry-run` is safe to use without a TTY.
106
+ ## 5. Host patch detection
195
107
 
196
- On success the output prints the next steps: restart the host or open a new session, then pick「编排模式 (Orchestrator)」in the preset selector (see §6).
108
+ The reason the generation pipeline detects anything: DSH runs `tools.restrict()` when a subagent is created, and **every name on the allow list must exist in the subagent's visible registration surface**, or the creation is rejected (unknown tools). The only machine-dependent part of that surface is the host patch layer, so instead of hard-coding allows the generator reads the patch layer at generation time, detects which tools are **actually enabled**, and rewrites the `toolFilter.allow` of the three default roles (`search_external` / `design` / `implement`) and of any custom role you create as "configured/static intent ∩ actually detected tools" — the generated preset never goes stale because a host tool was disabled.
197
109
 
198
- ## 5. Host Patch Detection
110
+ ### Patch layers read
199
111
 
200
- Why the installer exists: DSH runs `tools.restrict()` when a child agent is created, and **every name in an allow list must exist in the registry visible to the child**, otherwise creation is rejected (unknown tools). The only machine-dependent part of that registry is the host patch layer, so the installer never ships a fixed allow list: at install time it reads the patch layers, detects which tools are **actually enabled**, and rewrites the `toolFilter.allow` of the three default roles (`search_external` / `design` / `implement`) and of any custom roles you create using「configured / static intent ∩ actually detected tools」— the installed preset can never go stale when a host tool is disabled.
201
-
202
- ### Patch layers scanned
203
-
204
- All layers are included in order (`--patch` adds extra layers on top of the home/profile ones; detection unions the enabled entries across all layers):
112
+ All layers are **included** (detection unions the enabled entries across layers):
205
113
 
206
114
  | # | Layer | Path | When missing |
207
115
  |---|---|---|---|
208
116
  | 1 | home layer | `$DSH_HOME/cordis.patch.yml` | skipped |
209
- | 2 | profile layer | `$DSH_HOME/profiles/<profile>/cordis.patch.yml` | skipped (default profile = `web`; change with `--profile`) |
210
- | 3 | extra overrides | `--patch <file>` (repeatable) | warned and skipped |
117
+ | 2 | profile layer | `$DSH_HOME/profiles/<profile>/cordis.patch.yml` | skipped (default profile = `web`) |
118
+ | 3 | extra overrides | `--patch <file>` (repeatable; fallback-CLI flag) | warning and skipped |
211
119
 
212
- ### Detection of MCP servers and local plugins
120
+ ### MCP servers and local tool plugins
213
121
 
214
- - **Enablement**: a patch entry counts as enabled unless it is disabled (`enabled !== false` and no `disabled`); plugins additionally consult `config.enabled`.
215
- - **MCP server recognition**: the entry name ends with `dsh-mcp-client`, or the config carries a `serverName` with transport `stdio` / `streamable-http` / `sse` (or a `command`).
216
- - **Exact tool-name resolution** (`toolFilter.allow` matches exact names — no globs, so names must be spelled out): well-known servers come from a static table — `tavily` → 5 tools (`tavily_search` / `tavily_crawl` / `tavily_extract` / `tavily_map` / `tavily_research`), `codegraph` → `codegraph_explore`; unknown servers go through a live JSON-RPC handshake (stdio spawns the process and sends `initialize` + `tools/list`; streamable-http uses POST; **15-second timeout**), resolving the returned tool names to `mcp__<server>__<tool>`.
217
- - **Local tool plugins**: patch entry names are matched against a whitelist and their tools registered, e.g. `magic-memory` → `memory_search` (adding a plugin requires extending the whitelist — see `KNOWN_HOST_PLUGINS` in `tools/install.mjs`).
218
- - **Skipped without failing**: servers whose handshake fails, times out, or uses an unsupported transport such as `sse` are skipped — the affected roles simply lack those tools and the install does not error (the report marks them `handshake failed` with the reason; URLs/keys are redacted).
122
+ - **Enabled test**: a patch entry counts as enabled unless disabled (`enabled !== false` and no `disabled`); plugins additionally check `config.enabled`.
123
+ - **MCP server recognition**: the entry's package-name segment **exactly equals** `dsh-mcp-client` (scoped/sub-path forms compare the last `/` segment; a name like `not-dsh-mcp-client-foo` does not match), or the config carries a `serverName` with transport `stdio` / `streamable-http` / `sse` (or has a `command`).
124
+ - **Exact tool-name resolution** (`toolFilter.allow` matches exact names, no globs — names must be spelled out): the detection order is that **every `stdio` / `streamable-http` server gets a live JSON-RPC handshake first** (stdio spawns the process and sends `initialize` + `tools/list`; streamable-http POSTs; **15-second timeout**), and the returned tools become `mcp__<server>__<tool>` names; the static known table (`tavily` → 5 tools `tavily_search` / `tavily_crawl` / `tavily_extract` / `tavily_map` / `tavily_research`, `codegraph` → `codegraph_explore`) is only the degraded fallback when a **known** server's handshake fails — tools are kept per the table, the report flags a static table, and no tools are lost; only an unknown server that fails the handshake is skipped entirely.
125
+ - **Local tool plugins**: matched against a whitelist of patch entry names, registering their tools, e.g. `magic-memory` → `memory_search` (new plugins extend the whitelist — `KNOWN_HOST_PLUGINS` in `tools/lib/host.mjs`).
126
+ - **Skipped, not fatal**: "servers whose handshake fails, times out, or uses a transport without live discovery (`sse`) are skipped" applies to **unknown servers** only — such a server is skipped entirely, the role simply lacks those tools, and generation raises no error (the report flags `handshake failed` with the reason; URLs and keys are masked); known servers fall back to the static table on a failed handshake and keep their tools, so they never land here.
219
127
 
220
- ### Allow-list rewriting
128
+ ### Allow-rewrite rules
221
129
 
222
- For every rewritten role: allow entries that **depend on the host** (`mcp__*` and known plugin tools) are kept only inside the intersection with the detection inventory; **base tools guaranteed by the standard composition** (read/write/edit/glob/grep/bash/skill/web_search and similar) are untouched and never filtered. The result = configured intent ∩ actually enabled tools: disabled host-tool names are dropped automatically, so `tools.restrict()` never reports unknown tools. The main-agent side works the same way: host-dependent names in `restrict.mjs`'s base allow are filtered by the detection result, host tools from `main_agent_extra` and custom-role toolNames are injected into `config.allow`, and `main_agent_remove` entries are removed (an empty result **refuses to install**, preventing a runtime rejection of an empty allow). Other static delegation rows that the installer does not rewrite (e.g. `search_internal_deep`) stay as-is.
130
+ For each rewritten role, every allow name is checked against a **whitelist**: it must belong to the **preset's own tool face** (the `restrict.mjs` main-agent allow ∪ the static role allows in the source preset) or to the **detection inventory**; a name in neither (a `mnemon_*` left over from a disabled plugin, a typo) is dropped, and the removed list in the Preview (or fallback CLI) report says why. Within that, names that **depend on the host** (`mcp__*` and known plugin tools) survive only when actually detected, even when they are part of the source preset; plugin tools registered directly under their own names (such as `mnemon_*`, no `mcp__` prefix) are reconciled against the detection inventory the same way; the **base tools guaranteed by the standard composition** (read/write/edit/glob/grep/bash/skill/web_search etc.) are already part of the preset face and pass without detection. The result = configured intent ∩ actually enabled tools: disabled host tools drop out automatically, so `tools.restrict()` never hits unknown tools. The main-agent side works the same way: host-dependent names in `restrict.mjs`'s base allow are filtered by detection, host tools added via `main_agent_extra` and custom-role toolNames are injected into `config.allow`, and `main_agent_remove` removes entries (an empty removal result is **rejected**, guarding against an empty runtime allow that would refuse to load). Delegation rows the generator does not rewrite (e.g. `search_internal_deep`) are kept as-is.
223
131
 
224
- **When to re-run**: after changing patch configuration (enabling/disabling MCP servers or plugins, adding/removing `--patch` files), re-run `./install.sh --auto` (the npx entry works the same) to resync the allow lists; the installer also compares timestamps of patch files against the last generated output and prints a reminder when a patch is newer. Use `--dry-run` first to preview what would be removed.
132
+ **When to re-apply**: after changing patch configuration (enabling/disabling MCP servers or plugins, adding/removing extra patch files), hit Save & Apply once in 庖丁配置 to sync the allows — to preview what would be dropped first, use Preview, or the fallback CLI's `--dry-run`. The fallback CLI also compares patch-file timestamps against the last generation and prints a reminder when a patch has changed since.
225
133
 
226
134
  ### Parsing and validation
227
135
 
228
- Patch and config files are parsed with the **`yaml` package**, loaded lazily via `createRequire` from `$DSH_HOME/node_modules` (typically a symlink to DSH's npx node_modules, which ships yaml v2); when unavailable, a **minimal built-in YAML subset parser** is used (expects a top-level list/mapping; handles common scalars and `- insert:` wrapping). Before parsing, cordis `!!js` expressions found in real patch files are neutralized (`process.platform` / `process.cwd()` are replaced with their evaluated values). Role allow/persona blocks in the source `agent.cordis.yml` are located by yaml AST byte ranges where possible (indent-preserving, byte-exact replacement), falling back to a structured regex; the generated result is structurally validated as YAML (after substituting `!!js` placeholders) before anything is written, so structural errors abort without writing.
136
+ Patch and config files are parsed with the **`yaml` package** — loaded lazily via `createRequire` from `$DSH_HOME/node_modules` (that directory is managed by the dsh host, profile installs included, and ships yaml v2); when unavailable it falls back to a **built-in minimal YAML-subset parser** (expects a top-level list/mapping, supports common scalars and the `- insert:` wrapper). Before parsing, the cordis `!!js` expressions found in real patch files are neutralized (`process.platform` / `process.cwd()` replaced by their evaluated values). Locating the role allow/persona blocks in the source `agent.cordis.yml` prefers yaml AST byte ranges (indentation-preserving, exact replacement) and falls back to structural regexes; before anything is written, the generated text passes a YAML structural validation (parse with `!!js` placeholders) — a structural error aborts the write entirely.
229
137
 
230
138
  ### Typical examples
231
139
 
232
- Example 1 — disabling tavily: after commenting out tavily's mount row in `$DSH_HOME/cordis.patch.yml`:
233
-
234
- ```bash
235
- ./install.sh --auto --dry-run # preview: search_external's allow would drop the 5 mcp__tavily__*
236
- # entries; the report lists removed names + reasons
237
- # (e.g. "tavily MCP not enabled")
238
- ./install.sh --auto # apply
239
- ```
240
-
241
- Example 2 — disabling a plugin: re-run `--auto` after disabling magic-memory; `memory_search` is dropped from every role's allow automatically. To restore it, re-enable the plugin and re-run.
242
-
243
- Example 3 — extra `--patch` file: keep additional MCP/plugin config in a separate file and pass `--patch <file>` so detection includes it (no need to touch the home/profile layer files).
244
-
245
- ## 6. Apply & Activate
140
+ Example 1 — disabling tavily: after commenting out tavily's mount rows in `$DSH_HOME/cordis.patch.yml`, click Preview in 庖丁配置 — the `search_external` allow drops the 5 `mcp__tavily__*` names, with removed entries and reasons listed (e.g. "tavily MCP 未启用" / "tavily MCP not enabled"); then hit Save & Apply (developers can also use the fallback CLI: `node tools/install.mjs --dry-run` to preview, `--auto` to apply).
246
141
 
247
- After the install finishes (wizard or `--auto`), the artifact is the static directory under `$DSH_HOME/.agent-presets/orchestrator/` — nothing needs a restart to be safe, and re-running the installer after config changes simply rewrites this directory:
142
+ Example 2 — disabling a plugin: disable magic-memory and re-apply; `memory_search` drops out of every role allow automatically. To restore, re-enable the plugin and re-apply.
248
143
 
249
- 1. **Restart the host, or open a new session** (presets are loaded when a session is created; the preset selector refreshes per new session).
250
- 2. Pick「**编排模式 (Orchestrator)**」in the **new-session preset selector**.
251
- 3. To make it the default for every new session: set it in **Settings → Agent Presets**.
144
+ Example 3 — extra patch files (fallback CLI only, for developers who cloned the repo): put extra MCP/plugin config in a separate file and include it at detection time with `--patch <file>` (no need to touch the home/profile layer files); panel detection reads only the home/profile layers and has no equivalent entry.
252
145
 
253
- **Upgrading**: regenerate from the updated repo — pull the new source with `git pull`, then:
146
+ ## 6. The Paoding Config panel
254
147
 
255
- ```bash
256
- cd dsh-paoding
257
- ./install.sh --auto # applies the existing config idempotently (try --auto --dry-run first)
258
- ```
259
-
260
- On the npx/npm entry it is even simpler: re-run `npx dsh-paoding@latest` (or `npx github:lifangjin/dsh-paoding` before the npm release) — the config file is applied when present, as always.
261
-
262
- Because the artifact is a static directory generated from the repo source, upgrading / reinstalling / uninstalling never affects the host's other configuration (MCP, plugins, sessions), with zero coupling to DSH source code.
263
-
264
- **Uninstalling**: delete the preset directory (config UI uninstall is in §7; `dsh-paoding.config.yml` is not removed with the preset — keeping or deleting it does not affect other presets, and a later install with `--auto` applies it again):
265
-
266
- ```bash
267
- rm -rf "$DSH_HOME/.agent-presets/orchestrator"
268
- rm -rf "$DSH_HOME/dsh-paoding" # npx/npm installs only: remove the package copy the installer made
269
- ```
270
-
271
- After that,「编排模式 (Orchestrator)」no longer appears in the preset selector of new sessions.
272
-
273
- ## 7. Config UI (Optional)
274
-
275
- When hand-editing `dsh-paoding.config.yml` or using the terminal wizard is not desired, the visual config UI can be mounted as a plugin into the **设置 → 庖丁配置** section (Settings → Paoding Config) of the DSH Web GUI. It shares the same detection/generation pipeline as the CLI (`collectState` / `generateAndInstall`), so it is WYSIWYG.
276
-
277
- ### Mounting
278
-
279
- ```bash
280
- cd dsh-paoding
281
- ./install.sh --auto --config-ui
282
- ```
283
-
284
- After applying the preset, `--config-ui` **idempotently** does two things: it symlinks this repo's `plugins/paoding-config-ui` into `$DSH_HOME/node_modules/paoding-config-ui` (skipped when the link already exists and points here; an existing link to another source errors out, telling you to delete the stale link first), and it ensures an enabled mount row exists in `$DSH_HOME/cordis.patch.yml` (appended when missing; automatically un-commented when commented out):
285
-
286
- ```yaml
287
- - insert:
288
- - id: paoding-config-ui
289
- name: paoding-config-ui
290
- ```
291
-
292
- Then **restart DSH (`dsh web`)**; the "庖丁配置" (Paoding Config) section appears in the Settings page.
293
-
294
- On the npx/npm entry there is no repository directory to link: the installer instead copies the whole package to `$DSH_HOME/dsh-paoding` (see §2) and mounts from there; only the clone-and-run-`./install.sh` route uses the symlink shown above. Both forms present the same `paoding-config-ui` package to DSH, so the loading mechanism in the next subsection is identical.
295
-
296
- Another route to the same section is DSH's official plugin channel: `dsh plugin --profile web add dsh-paoding@latest` installs the UI on its own, per profile (see §2). Don't keep that mount side by side with `--config-ui`'s global one, or the Settings page ends up with a duplicate Paoding Config section.
148
+ If you'd rather not hand-edit `dsh-paoding.config.yml`, open the **庖丁配置 (Paoding Config)** entry in the sidebar bottom action bar (above the Settings row). It shares the exact same detection/generation pipeline (`collectState` / `generateAndInstall`) as the first-install automation and the fallback CLI — what you see is what gets written; the panel ships with the plugin channel, so installing the plugin is all it takes.
297
149
 
298
150
  ### Loading mechanism
299
151
 
300
- - cordis resolves the package name `paoding-config-ui` in the patch row via `$DSH_HOME/node_modules`;
301
- - DSH's client-modules half-scans the package's `dsh.client` declaration in `package.json` plus `exports["./client"]`, compiling `lib/client.js` into the browser's `__DSH_BOOT__` (served at `/plugins/paoding-config-ui/client.js`);
302
- - the browser kernel creates a loader entry for each manifest plugin and activates it (the bundle registers via `window.__ModuleLoader__.load({ id: "paoding-config-ui", factory })`; the id must equal the package name / graph-row id);
303
- - the client `apply()` waits for the settings shell to declare its slot via `ctx.slots.inject("settings.section")`, then registers the section with `slots.register()` — `id: "paoding"`, `order: 50`, `label: 庖丁配置` ("Paoding Config") (the same pattern as the built-in General section; id/order/label drive the settings navigation).
152
+ - cordis resolves the package name `dsh-paoding` from the patch row against the profile's `node_modules`;
153
+ - DSH's client-modules node half-scan reads the package's `dsh.client` declaration and `exports["./client"]`, compiling `lib/client.js` into the browser's `__DSH_BOOT__` (served as `/plugins/paoding-config-ui/client.js`);
154
+ - the browser core creates and activates a loader entry for every plugin in the manifest (the bundle registers as `window.__ModuleLoader__.load({ id: "paoding-config-ui", factory })`; the id equals the patch row's id);
155
+ - the client `apply()` registers the 庖丁配置 entry through the host's `sidebar.footer.action` keyed slot (`slots.register`, the same slot as the plugin-hub cordis badge, rendered in the action bar above the Settings row) rather than injecting sidebar DOM itself; the MutationObserver is only used to bridge the workspace "…" menu (recognizing the menu DOM and adding a 庖丁-config deep-link item). Clicking mounts the container (`data-dsh-paoding-view`) into the session's middle column and renders the full-page configurator with `createRoot`, the open state driven by `html[data-dsh-paoding-active]`, mutually exclusive with the memory / taskboard / ssh full-page panels.
304
156
 
305
- ### API and security
157
+ ### Data interface and security
306
158
 
307
- The panel talks to the same-origin **`/api/paoding/*`** routes: the Node side registers prefix routes — `GET /api/paoding/state` (cached detection state), `POST /api/paoding/rescan` (forced re-detection, including MCP handshakes), `POST /api/paoding/preview` (generate without writing), `POST /api/paoding/apply` (install and save the config) — reusing the detection/generation pipeline of `tools/install.mjs` via `plugins/paoding-config-ui/api-core.mjs`. The routes carry a **browser trust fence**: the Host must be loopback or in `webRuntime.trustedHosts`, and cross-site requests are rejected (mirroring DSH's `/api` gateway fence semantics, because the longer `/api/paoding` prefix would hit this plugin and bypass that gateway). The config UI has **no separate server or port**: the DSH Web port listens on `127.0.0.1` only, never exposed to the public network.
159
+ Panel data goes over the **same-origin `/api/paoding/*`**: the Node side registers prefix routes — `GET /api/paoding/state` (cached detection state), `GET /api/paoding/version` (latest-version check, the data source of the version/update card), `GET /api/paoding/models` (model list from the host LLM runtime), `GET` + `POST /api/paoding/workspaces` (list / add workspaces), `POST /api/paoding/rescan` (forced re-detection, MCP handshakes included), `POST /api/paoding/preview` (generate without writing), `POST /api/paoding/apply` (install and save config), `POST /api/paoding/upgrade` (in-place `dsh plugin update`), `GET /api/paoding/client.css` (statically served panel stylesheet) — reusing the detection/generation pipeline in `tools/` (`plugins/paoding-config-ui/api-core.mjs`). The routes carry a **browser trust fence**: the Host must be loopback or listed in `webRuntime.trustedHosts`, and cross-site requests are rejected (mirroring DSH's `/api` gateway fence semantics, since the longer `/api/paoding` prefix would otherwise hit this plugin and bypass the gateway). The panel has **no separate server or port**: the DSH Web port listens on `127.0.0.1` only and is never exposed to the outside.
308
160
 
309
161
  ### Panel capabilities
310
162
 
311
- - **Status & actions**: top buttons「**保存并应用**」(Save & Apply)、「预览生成」(Preview)、「重新检测」(Rescan); a detection summary with chips (current profile, each MCP server with its tool count, plugin chips, skill counts, etc.).
312
- - **Agent tool assignment cards**: a fixed card for the main agent (unchecking a base tool adds it to `main_agent_remove`, removing it from the main agent; checking a host tool adds it to `main_agent_extra`; two skill groups「read 按需」and「内联全文」; plus a「人设追加」editor that can edit, clear, or restore the persona tail to its default) — plus cards for the three built-in roles `search_external` / `design` / `implement` and any custom role (with a delete button); every role card lets you check/uncheck base and host tools and edit the persona. A「新建自定义 agent 角色」button sits at the bottom.
313
- - **Skill assignment**: one row per skill; check which agents it is assigned to (written into the corresponding persona's `Available skills` soft guidance; role skills are soft-guided, main-agent skills have the separate read-on-demand / inline groups).
314
- - **Preview**: generates `agent.cordis.yml` without writing, showing each role allow's intent → kept counts and the full generated text.
315
- - **Save & Apply**: after confirmation, writes the preset (overwriting `$DSH_HOME/.agent-presets/orchestrator`) and saves the current assignments to `$DSH_HOME/dsh-paoding.config.yml` — equivalent to `./install.sh --auto` plus the wizard's config save; on success it prompts that a DSH restart or a new session is required (and that a patch newer than the generated output takes effect after restart).
163
+ - **State and actions**: the page header is a title + version/update card (in-place new-version notice and one-click upgrade) + subtitle, and the body goes straight to the workspace and role cards with no separate detection-summary/chips section; the action buttons「**保存并应用**」(Save & Apply), Preview, and Rescan sit in a sticky bottom dock, clickable from any scroll position.
164
+ - **Agent tool-assignment cards**: one fixed card for the main agent (unchecking a base tool adds it to `main_agent_remove`, dropping it from the main agent; checking an MCP / plugin tool adds it to `main_agent_extra`; skills in two groups, "read on demand" and "inline full text"; plus a "persona extra" editor — edit, clear, or restore the default) + three built-in role cards (`search_external` / `design` / `implement`) + any number of custom role cards (with a delete button); every role card presents its tools in three groups — base / MCP / plugin — with per-item checkboxes, and the persona is editable. "Create a custom agent role" at the bottom.
165
+ - **Skill assignment**: one row per skill, check which agents it goes to (written into the matching personas as `Available skills` soft guidance; role skills are soft guidance, main-agent skills have the read-on-demand/inline groups).
166
+ - **Preview**: generates `agent.cordis.yml` without writing, showing each role's intent → kept counts plus the full generated text.
167
+ - **Save & Apply**: after confirmation, writes the preset (overwriting `$DSH_HOME/.agent-presets/orchestrator`) and saves the current assignments to `$DSH_HOME/dsh-paoding.config.yml`; success asks you to restart DSH or start a new session (and flags when a patch is newer than the generated output). Opening the panel loads the existing config file as the current values — hand-edited choices are visible and editable right away.
316
168
 
317
- ### Uninstalling the UI
169
+ ## 7. Uninstalling
170
+
171
+ Three commands, and it's gone cleanly:
318
172
 
319
173
  ```bash
320
- rm -f "$DSH_HOME/node_modules/paoding-config-ui" # 1) remove the symlink
321
- # 2) comment out the three-line paoding-config-ui mount row in $DSH_HOME/cordis.patch.yml
322
- # 3) restart DSH (dsh web)
174
+ dsh plugin --profile web remove dsh-paoding
175
+ rm -rf "${DSH_HOME:-$HOME/.dsh}/.agent-presets/orchestrator"
176
+ rm -rf "${DSH_HOME:-$HOME/.dsh}/.agent-presets"/orchestrator-*
323
177
  ```
324
178
 
325
- The section disappears from the Settings page; the generated preset and the config file are unaffected. For the full npx/npm uninstall (including removing `$DSH_HOME/dsh-paoding`), see §6. A UI installed through the plugin channel is removed differently: `dsh plugin --profile web remove dsh-paoding`, which affects that profile only.
179
+ The first removes the plugin and the sidebar panel (scoped to the given profile), the second deletes the orchestrator preset directory, and the third removes the per-workspace preset directories (`orchestrator-<basename>`) created when you configured individual workspaces (if you never did, the glob matches nothing and it's harmless) — the base `orchestrator` directory is already gone with the previous command and is not covered by that glob. The config file `~/.dsh/dsh-paoding.config.yml` is not removed with them — keep it or delete it, it does not affect any other DSH configuration, and a reinstall's first-install automation would apply it again.
180
+
181
+ Uninstalling touches nothing else on the host (MCP, plugins, sessions), and stays decoupled from DSH source code; "编排模式 (Orchestrator)" disappears from the preset selector for new sessions.
326
182
 
327
183
  ## 8. Troubleshooting
328
184
 
329
- **Node.js missing**
185
+ **pnpm missing**
330
186
 
331
- Running `./install.sh` reports `error: node (>=18) is required to run the dsh-paoding installer`: `node` is not on the PATH or is older than 18. Install Node.js ≥ 18 and retry.
187
+ `dsh plugin add` fails with a pnpm error: `dsh plugin` relies on pnpm to install packages. Install it (`npm i -g pnpm` or `corepack enable`) and retry.
332
188
 
333
- **Preset missing from the selector**
189
+ **The plugin is installed but "编排模式 (Orchestrator)" is not in the preset list**
334
190
 
335
- Presets are loaded when a session is created: restart the host (`dsh web`) or open a new session and look at the selector again. If it is still missing, check the install target: `ls "$DSH_HOME/.agent-presets/orchestrator"` should show three files (`agent.cordis.yml`, `preset.yml`, `restrict.mjs`); if empty, the `$DSH_HOME` used at install time probably differs from the home directory DSH actually uses (re-run with an explicit `export DSH_HOME=…`).
191
+ The preset is generated automatically when the plugin starts: first make sure you **restarted DSH** after installing; presets load when a session is created, so after the restart you also need a new session for it to appear in the selector. Still nothing? Check the DSH log for `paoding-config-ui` warnings (a failed auto-generation is logged; apply manually from the panel), then check `ls "${DSH_HOME:-$HOME/.dsh}/.agent-presets/orchestrator"` — an empty directory usually means the plugin's runtime `$DSH_HOME` differs from the home directory you assumed (export `DSH_HOME=…` explicitly before launching DSH).
336
192
 
337
- **unknown tools on child creation**
193
+ **`tools.restrict()` reports unknown tools / subagent creation is rejected**
338
194
 
339
- An allow list contains a name absent from the child's registry — most often because the installer was **not re-run after enabling/disabling an MCP server or plugin**. Re-run `./install.sh --auto` to resync (use `--auto --dry-run` first to see what would be removed); hand-written names of tools that are not mounted trigger the same error.
195
+ A name on the allow list does not exist in the subagent's registration surface — most often because an MCP server or plugin was enabled/disabled **without re-applying**. Hit Save & Apply in 庖丁配置 to sync (Preview first if you want the drop list); hand-written names for tools that aren't installed trigger the same error.
340
196
 
341
- **"requires a terminal" error**
197
+ **No 庖丁配置 entry in the sidebar**
342
198
 
343
- When stdin is not a TTY (scripts, CI, remote execution) and none of `--auto` / `--config` / `--dry-run` is passed, the installer refuses to start so the wizard cannot hang: use `./install.sh --auto` (applies the config when present, writes the base template otherwise), or feed answers from stdin in scripts via `./install.sh --wizard < answers.txt`. The npx one-command install carries `--auto` built in and runs fine without a TTY.
199
+ First confirm the plugin is installed into the profile your DSH actually runs with (the `--profile` value used with `dsh plugin add`) and that you restarted DSH after installing — the panel is scoped per profile, so if it lives outside `web` you must launch DSH with that profile to see it. If both hold and it's still missing, check the startup log for whether the plugin loaded at all.
344
200
 
345
- **A role is missing an MCP tool**
201
+ **An MCP tool is missing from a role**
346
202
 
347
- MCP servers whose handshake failed or whose transport is unsupported are skipped (the report marks them `handshake failed`): the affected roles simply lack those tools and the install does not error. Verify the server can start / be reached on its own and re-run; well-known servers (tavily, codegraph) are resolved from the static table without a handshake and never hit this case.
203
+ At detection every stdio / streamable-http server gets a live handshake first: an unknown server whose handshake fails is skipped entirely (the report flags `handshake failed`) and the role simply lacks those tools — generation raises no error. Known servers (tavily, codegraph) fall back to the static table when their handshake fails and keep their tools, with the report flagging a static table — seeing that flag means the server was unreachable at detection time and the table's tool face may be stale. Check the server can start/connect on its own, then re-apply.
348
204
 
349
- **Skills not appearing in a persona**
205
+ **Skills not written into a persona**
350
206
 
351
- Role skills can only be assigned to roles whose allow contains `skill`; main-agent skills go through `main_agent_skills` (soft rows) or `main_agent_skills_inline` (inline). A skill whose SKILL.md cannot be found (searched under `$DSH_HOME/skills`, `~/.agents/skills`, and the working directory's `.dsh`/`.agents/skills`) is warned about and skipped — make sure the skill is installed under one of those roots.
207
+ Skills can be assigned to any role — the generation layer appends an `Available skills:` soft-guidance line to each assigned role's persona; but to actually load a skill a role needs `skill` / `read` tools of its own (a role without the `skill` tool only gets the guidance line — nothing loads). Main-agent skills use `main_agent_skills` (soft rows) or `main_agent_skills_inline` (inline). A skill whose SKILL.md cannot be found (looked up per the configuration target: global scans the two user roots only, workspaces add the project-level roots — configuration guide §9) is warned about and skipped — make sure the skill is installed under one of the matching roots.
352
208
 
353
- **Where is the config file**
209
+ **I hand-edited the config file — how do I apply it?**
354
210
 
355
- Both the wizard and the config UI write to `$DSH_HOME/dsh-paoding.config.yml` (default `~/.dsh/dsh-paoding.config.yml`); the CLI can point elsewhere with `--config <file>`, and the config UI's status area shows the current config file path. To reset all choices: delete the file — `./install.sh --auto` (or re-running the npx one-command install) then falls back to the base template; add `--suggest` to fall back to smart defaults instead.
211
+ Open the 庖丁配置 (Paoding Config) entry in the sidebar bottom action bar (above the Settings row) — the panel loads the hand-edited file as its current values — and hit Save & Apply. The panel does not watch the config file, so hand edits do not take effect on their own; the version marker likewise only triggers auto-regeneration on new versions.
356
212
 
357
- **The installed preset or config is broken**
213
+ **Where is the config file?**
358
214
 
359
- The preset is a static directory, so simply reinstall: fix the config and run `./install.sh --auto` (or delete `$DSH_HOME/.agent-presets/orchestrator` and re-run the wizard). Note the installer refuses installs where a role's allow would be empty or the main agent's tools would be emptied, preventing presets that are bound to fail at runtime.
215
+ The panel's Save & Apply writes to `$DSH_HOME/dsh-paoding.config.yml` (default `~/.dsh/dsh-paoding.config.yml`; move it with the `DSH_PAODING_CONFIG` environment variable; the fallback CLI uses `--config <file>`). To reset every choice: delete the file and restart DSH — first-install automation treats it as "no config" and writes the base template.
360
216
 
361
- **The Config UI section is missing**
217
+ **Cleaning up leftovers from the old npx/npm installs**
362
218
 
363
- If you installed through the plugin channel, first make sure the profile you passed to `dsh plugin add` matches the profile this DSH actually runs (the UI is per-profile) and that you restarted after installing. For the `--config-ui` global mount, check in order: whether DSH was restarted after mounting; for npx/npm installs, whether `$DSH_HOME/dsh-paoding` exists (npx cache cleanup never touches it; if it is missing, re-run the one-command install); for clone installs, whether the `$DSH_HOME/node_modules/paoding-config-ui` symlink points at this repo's `plugins/paoding-config-ui` (`--config-ui` errors when it points elsewhere); and whether the `paoding-config-ui` mount row in `$DSH_HOME/cordis.patch.yml` is enabled (not commented out). Once all are fine, restart DSH and reopen the Settings page.
219
+ Released versions 0.2.x and earlier supported a direct `npx dsh-paoding` install: the whole package was copied to `~/.dsh/dsh-paoding`, and a `paoding-config-ui` mount row was written into the global `~/.dsh/cordis.patch.yml` (npx/npm form) or a `~/.dsh/node_modules/paoding-config-ui` symlink created (clone form). The new mount always goes through the plugin channel and no longer uses any of those locations (today's `npx dsh-paoding@latest` is merely a shortcut that delegates to `dsh plugin add`, and writes none of them either). To clean up:
364
220
 
365
- **Two duplicate Paoding Config sections**
221
+ ```bash
222
+ rm -rf ~/.dsh/dsh-paoding # package copy from the old npx/npm form
223
+ rm -f ~/.dsh/node_modules/paoding-config-ui # symlink from the old clone form
224
+ # then delete the id: paoding-config-ui mount section (three lines) from ~/.dsh/cordis.patch.yml
225
+ ```
366
226
 
367
- The global mount and the plugin channel are both present — two insert rows from different sources, both active (see "Don't mix the two" in §2). Pick one: to stay on the plugin channel, follow the migration steps in §2 and delete the `paoding-config-ui` section from `$DSH_HOME/cordis.patch.yml` plus the `$DSH_HOME/node_modules/paoding-config-ui` symlink; to return to the global mount, run `dsh plugin --profile web remove dsh-paoding`. Restart DSH afterwards.
227
+ Restart DSH afterwards. If the sidebar ever shows a duplicate entry, this cleanup fixes that too — the plugin channel is now the only mount source.