dsh-plugin-tool-management 0.18.5 → 0.19.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 (46) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +254 -221
  3. package/README_EN.md +223 -224
  4. package/docs/images/1-EN.png +0 -0
  5. package/docs/images/1.png +0 -0
  6. package/docs/images/2-EN.png +0 -0
  7. package/docs/images/2.png +0 -0
  8. package/docs/images/3-EN.png +0 -0
  9. package/docs/images/3.png +0 -0
  10. package/docs/images/4-EN.png +0 -0
  11. package/docs/images/4.png +0 -0
  12. package/docs/images/5-EN.png +0 -0
  13. package/docs/images/5.png +0 -0
  14. package/docs/images/6-EN.png +0 -0
  15. package/docs/images/6.png +0 -0
  16. package/docs/images/7-EN.png +0 -0
  17. package/docs/images/7.png +0 -0
  18. package/docs/images/8-EN.png +0 -0
  19. package/docs/images/8.png +0 -0
  20. package/docs/images/logo.png +0 -0
  21. package/docs/images/promo-cover-EN.png +0 -0
  22. package/docs/images/promo-cover.png +0 -0
  23. package/docs/update.md +28 -500
  24. package/lib/client.js +1857 -913
  25. package/lib/context-inject-frame.js +13 -0
  26. package/lib/context-inject-select.js +4 -3
  27. package/lib/context-inject.js +4 -3
  28. package/lib/index.js +100 -19
  29. package/lib/op-registry.js +16 -1
  30. package/lib/ops/candidates.js +1 -1
  31. package/lib/ops/prompts.js +64 -7
  32. package/lib/ops/trash-retention.js +55 -0
  33. package/lib/prompts/service.js +191 -25
  34. package/lib/scene-prompt-sync.js +1 -1
  35. package/lib/scenes/candidates.js +162 -69
  36. package/lib/settings-sidecars.js +2 -1
  37. package/lib/subagents/catalog.js +2 -2
  38. package/lib/subagents/persona-parse.js +36 -1
  39. package/lib/subagents/persona-write.js +7 -1
  40. package/lib/subagents/run-provider.js +55 -21
  41. package/lib/subagents/service.js +3 -3
  42. package/lib/subagents/tools.js +17 -2
  43. package/lib/tools/scene.js +4 -1
  44. package/lib/tools/subagent.js +13 -1
  45. package/lib/trash-retention.js +256 -0
  46. package/package.json +1 -1
package/README_EN.md CHANGED
@@ -1,224 +1,223 @@
1
- # dsh-plugin-tool-management
2
-
3
- [![npm version](https://img.shields.io/npm/v/dsh-plugin-tool-management?logo=npm&color=cb3837)](https://www.npmjs.com/package/dsh-plugin-tool-management)
4
- [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
- [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js)](package.json)
6
- [![GitHub](https://img.shields.io/badge/GitHub-ouli--1242%2Fdsh--plugin--tool--management-181717?logo=github)](https://github.com/ouli-1242/dsh-plugin-tool-management)
7
-
8
- [![DSH Market](https://raw.githubusercontent.com/2BingLing/dsh-market/master/assets/readme/badge-listed-en.svg)](https://dsh.market/)
9
- [![awesome-dsh-plugin](https://img.shields.io/badge/awesome--dsh--plugin-listed-3fb950)](https://awesome-dsh-plugin.com)
10
- [![dshfind](https://dshfind.com/api/badge/ouli-1242/dsh-plugin-tool-management?lang=en)](https://dshfind.com/en/plugins/ouli-1242/dsh-plugin-tool-management)
11
- [![0xsline](https://img.shields.io/badge/0xsline-listed-3fb950)](https://github.com/0xsline/awesome-deepseek-harness)
12
-
13
- [简体中文](README.md) · **English** · [Changelog](CHANGELOG.md) · [Release overview](docs/update.md)
14
-
15
- An **MCP, skills, scenes, memories, subagents, prompts & archived sessions** manager for DeepSeek Harness. Eight tabs: Scenes / MCP / Skills / Subagents / Prompts / Memories / Sessions / Host.
16
-
17
- ```sh
18
- dsh plugin --profile web add dsh-plugin-tool-management@latest
19
- ```
20
-
21
- Hard-refresh the browser (Cmd/Ctrl+Shift+R) afterwards — a **Tools** panel in Settings means it worked. The plugin only writes its own files, never touches skill sources, and your configuration survives restarts and upgrades.
22
-
23
- ---
24
-
25
- ## Screenshots
26
-
27
- | | |
28
- |:---------------------------------:|:----------------------------------:|
29
- | ![Scenes](docs/images/1-EN.png) | ![MCP](docs/images/2-EN.png) |
30
- | **Scenes** | **MCP** |
31
- | ![Skills](docs/images/3-EN.png) | ![Subagents](docs/images/4-EN.png) |
32
- | **Skills** | **Subagents** |
33
- | ![Prompts](docs/images/5-EN.png) | ![Memories](docs/images/6-EN.png) |
34
- | **Prompts** | **Memories** |
35
- | ![Sessions](docs/images/7-EN.png) | ![Host](docs/images/8-EN.png) |
36
- | **Sessions** | **Host** |
37
-
38
- ## Highlights
39
-
40
- In one line: **configure "work / writing / coding" each as a scene and switch the whole stack with one click — and the model always sees whatever this plugin manages.**
41
-
42
- | Highlight | What it means |
43
- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44
- | One-click scene switch | Each scene carries its own MCP / skills / personas / memories (plus a bound set of quick prompts, if you set one); entering applies them, leaving restores. The model can switch them for you too |
45
- | Manage without leaving chat | Type `/` in the composer for a “Tools” section: enter a scene, toggle skills / MCP / subagents / prompts / memories — one click, done |
46
- | Memories reach the model | A few `.md` files under a scene become its knowledge base — injected automatically, no copy-pasting |
47
- | Notes for MCP servers | Write "if A is down, fall back to B" as a note; the model sees it every turn and acts on it |
48
- | Disable a single tool | Keep a server but mute one tool: invisible and uncallable. "Restart" only reconnects and never flips your switches |
49
- | Skills at a glance | The copy in effect is marked *preferred*; shadowed ones name the source that wins |
50
- | Subagent = one file, one role | Write a role file and delegate to it; only the result comes back, and it never clutters your History |
51
- | Several prompt presets | Keep multiple `AGENTS.md` baselines and switch with one click; each scene can bind its own |
52
- | Sessions no longer lost | Archives grouped by project, searchable, batch-restorable; imports Claude Code / Cursor / Codex transcripts |
53
- | The model always sees it | One context message per domain, republished only when the content changes. Under suppressing presets nothing is injected by default — force any domain on in the Host tab |
54
- | Lock it and relax | Locking a scene makes create/update/delete across the five domains read-only until you unlock |
55
- | Deleted is not gone | Skills / memories / personas / presets / scenes land in a recycle bin and can be restored (permanent session deletion is the exception) |
56
- | Safe by default | Secrets masked, plaintext needs a token, only the plugin's own files are written. Masking is display-only — see Configuration & security |
57
-
58
- ## Quick start
59
-
60
- Prerequisites: DSH installed (`dsh web` runs), Node.js ≥ 18.
61
-
62
- ```sh
63
- dsh plugin --profile web add dsh-plugin-tool-management@latest # install / update
64
- dsh plugin --profile web remove dsh-plugin-tool-management # uninstall
65
- ```
66
-
67
- Hard-refresh the browser — a **Tools** panel with eight tabs means it worked. Client changes hot-reload; host-side changes need `dsh web` restarted.
68
-
69
- You can also ask the model:
70
-
71
- ```text
72
- Install the dsh-plugin-tool-management plugin:
73
- dsh plugin --profile web add dsh-plugin-tool-management@latest
74
- Then remind me to hard-refresh the browser.
75
- ```
76
-
77
- ## Features
78
-
79
- ### Slash commands (manage from the chat box)
80
-
81
- - **Type `/` to get a “Tools” section**: six rows — scenes / skills / MCP servers / subagents / prompts / memories. Click a row to drop into that domain's items in place (the breadcrumb's “Tools” step goes back), click an item to apply it. **The menu stays open after a click** — keep toggling the next one; batch toggling is the main use.
82
- - **State is visible**: enabled items carry a green check in the icon slot, disabled ones an empty box, and the grey text on the right is the item's own note (scene description / skill description / MCP note / persona description / preset description / memory description, clipped to 40 chars; rows without one fall back to “Enabled / Not enabled”). Each domain row shows a summary (`now: Work`, `3 on / 1 off`), so you know where you stand without opening it. The check flips the moment you click (optimistic); a failed write flips back and says why.
83
- - **Long lists stay usable**: every domain lists all of its items and the panel scrolls; skills group by source directory, memories by scene, enabled rows first inside each group. Each external skill directory also gets one "directory name · directory" row pinned at the top of its group — clicking it toggles the entire source (it is listed while enabled too, so you can switch it off here; its skills collapse with it and come straight back when it is enabled again). The two always-on reserved scenes ("Global" and the shared baseline) don't appear under scenes, but the memories domain includes the global ones by default and pins that group at the top: they aren't in the current scene yet they hold in every conversation.
84
- - **Only reversible actions**: entering a scene and the various toggles. Clicking the scene that's already active leaves it (only global and shared keep injecting); the one thing that can't be switched off is the global prompt — there is no "apply nothing" state for a baseline, so clicking the preset already in effect just says it is the current one. Create / delete / edit / import / export stay on their own pages.
85
- - **Built on the host's official channel**: registered through DSH's input-trigger interface (the same one the official skills menu uses) — no patching of official packages. If the host lacks that service the entry simply never appears and the compatibility page says so. Each section gets its own checkbox (“Tools” / “Quick prompts”) inside the “Slash command entry” group under “Compatibility → Injection”, and the tag at the end of each row says whether it really mounted this startup. The two sections register independently, so switching one off leaves the other alone; turning both off restores the stock `/` menu (restart DSH for the slash-command change to take effect).
86
- - **“Tools › Prompts” holds both kinds of prompt, and every row there is a switch**: the global group is which baseline is written into `AGENTS.md` (single choice), the quick group is whether that snippet is on at all — the same action and the same state as the pill switch on the Prompts page. Quick prompts that are switched off stay listed here: the place you manage things from should not hide an item the moment you manage it.
87
- - **A separate “Quick prompts” section is for reuse**: only the enabled ones appear, and one click replaces the typed token with the saved text in the composer — **nothing is sent for you**; the send key stays yours. Those rows carry no check mark on purpose: clicking one inserts text, it doesn't toggle state.
88
-
89
- ### Scenes & memories
90
-
91
- - **A scene = a group, a memory = a `.md` file**: `memories/<scene>/<name>.md`, the whole body is injected; file names may be non-ASCII.
92
- - **"Enabled" and "entered" are two axes.** Enabled decides which scene the injection follows (single-select; none enabled = only `global` and `_shared` inject). Entering applies the scene's whole MCP / skills / personas set and writes its bound prompt into `~/.dsh/AGENTS.md` (5 generations of backups, restored on exit). **They stay identical, and the server is what holds that invariant**: `rules-set-active` brings the runtime in line before it writes the enabled set — and writes nothing at all if the runtime refuses — so every entry point (the UI toggle, the `/` menu, the model tool) needs a single call.
93
- - **Scene profile**: each scene combines MCP tools / skills / subagents / memories. The rule is *ticked = on, unchecked = off*, and a section that was never created means that whole domain is off. Leaving restores the pre-scene state verbatim. While a scene is active, the switches in those three domains still work and are written back into its profile; only locking freezes them.
94
- - **Scene lock**: once locked, create/update/delete across MCP / skills / subagents / memories / prompts is read-only (UI disabled plus a server-side guard). Creating, renaming, binding a prompt, activating or entering a scene, the recycle bin, MCP restart and exports stay outside the frozen list. A scene must be running to lock, and a locked scene cannot be turned off until you unlock it.
95
- - **Deleting a scene deletes its memories too**: record, profile and every memory land in one recycle-bin entry and come back as a whole. A running scene refuses deletion; renaming follows through to the directory and profiles.
96
- - **Import / export**: `.md` / `.zip` (directory name = scene, bundles carry attachments); same names are skipped, never overwritten, and over-limit items are reported. Export packs the memories you tick and only reads source files.
97
- - **Injection budget**: 128 KiB by default; oversized memories are skipped with a list. Deletes go to the recycle bin.
98
- - **Subagent sessions do not receive memories**: memories are the parent's situation, not the facts a child needs. A child's context stays "persona + task" — put what it needs into the task. Other domains are unaffected (MCP / skills / prompts still inject; the persona catalog follows each persona's `catalogDepth`).
99
-
100
- ### Subagents
101
-
102
- - **One file per persona**: `subagents/<persona>.md`, frontmatter entirely optional. `output:` takes one requirement per line — a checkable output contract (format, severities, bans) gets followed where one abstract sentence does not.
103
- - **The persona reaches the child inside a role frame**: the body is used verbatim under a `# Persona: <name>` heading with an authorization line and a boundary line (it governs *how* it works, not *what* it may do). The persona file itself needs no change.
104
- - **Delegation**: `subagent_manager_run` runs with the persona, returns only its final result, and never enters History. By default the child starts fresh (so the task must be self-contained); with `inherit: true` it is seeded with this conversation's **finished** turns — not the current one, so a delegation made mid-turn still needs the full story.
105
- - **On/off and catalog**: a disabled persona is neither injected nor visible to the model (file untouched); new / imported / restored personas start **disabled**. The catalog lists names and descriptions only, so the model knows what it can delegate to; personas are renameable and scene bindings follow.
106
- - **Catalog injection depth (`catalogDepth`)**: default `1` = top-level sessions only; `2` also reaches subagent sessions; the UI's "No nesting limit" writes `99`. **It does not limit nesting** — how deep delegation may go is the host's decision, and this plugin never passes `maxDepth`.
107
- - **Tool limits per agent preset**: one allow or deny list per preset (mutually exclusive). Two behaviours worth knowing: **an allow list is merged back with every currently running `mcp__*` tool**, so ticking only `read` will not stop an MCP server that exposes a shell; and **if none of the listed names exists right now, the result is "no restriction"** (fail-open). Naming the reserved `run_code` is stripped from the list with a note, because the host would throw and the subagent would never start.
108
- - **Reasoning effort** sits next to *model* in the advanced options, and the available levels are declared by the selected model. After a model change, a level no longer offered is cleared and you are told. Leaving it empty while a model is set falls back to that model's own default — not the main session's level.
109
-
110
- ### MCP servers
111
-
112
- - **CRUD with immediate effect**: written into `cordis.patch.yml`, backed up before every change. Levels are global / project, and new servers default to global.
113
- - **Per-tool switches**: disable individual tools (invisible to the model and blocked at call); whole servers support bulk.
114
- - **Stopped servers still show their tools**: one that is not running keeps the names and descriptions last seen (marked "as of last run"); one that never ran can be probed with "start server to read tools".
115
- - **Status and notes enter the context**: currently usable servers are listed, and **one that connected before but cannot right now is listed and labelled too** — so the model says "it is not connected, check it" instead of reading "configured but unreachable" as "not configured" and suggesting you install one. Servers that never connected are omitted; your notes travel along as decision hints.
116
- - **Secret masking**: a credential is replaced **as a whole** by `••••••`, and only for keys that look sensitive; `$VAR` and `!!js` are indirections and stay readable, and a URL's query string, its `user:pass@` userinfo and its `#fragment` are all masked. "Reveal" needs a token. A masked value is never written back into the patch file — on save the plugin restores the stored value for any field you left alone, and refuses the whole save when a URL has nothing to restore from (a silently stripped query string would leave an address that connects but fails authentication).
117
- - **Migrate & back up**: cross-level migration rolls back on failure; JSON export/import.
118
-
119
- ### Skills
120
-
121
- - **Sources at a glance**: project / DSH / Agents / Codex / Claude / custom directories, grouped by source.
122
- - **Opposite permissions**: default sources are readable and their skills deletable; external directories can be disabled or removed but their files stay read-only.
123
- - **Remove ≠ disable**: removing stops scanning the directory at all (nothing is written, restorable); disabling keeps it listed but uncallable.
124
- - **Who wins a shared name**: the winning copy is marked "preferred", shadowed ones name the source that wins, and enabling a shadowed copy says plainly that it will not take effect.
125
- - **Custom directories / ZIP import & export / recycle bin.**
126
-
127
- ### Prompt presets
128
-
129
- - Multiple `~/.dsh/AGENTS.md` baselines, applied with one click (the host re-reads that file each turn, so it lands next turn), with 5 generations of backups.
130
- - **It remembers the last applied preset**: even after you hand-edit `AGENTS.md`, the origin is still reported (flagged "the file changed since").
131
- - Each preset can carry a one-line description that lives in a sibling `meta.json` — shown in this panel, never injected.
132
- - **A referenced preset cannot be deleted** (scene binding / current `AGENTS.md` content / the copy to restore on scene exit); deletes go to the recycle bin.
133
- - **The switch on the left of a card is the answer to “is this one in effect”**: flip it on and that preset is written into `AGENTS.md`; clicking the one that is already on writes it again (undoing hand edits). There is no "apply nothing" state for a baseline, so this switch cannot be turned off — light up a different one instead (single choice).
134
- - **While a scene drives the baseline, flipping another one rebinds that scene** to the chosen preset (written into the scene profile, realigned immediately); it is refused only while the scene is locked. The global baseline is restored from the snapshot on exit.
135
- - **Quick prompts** (the second section on the same page): save a piece of text you keep typing, then hit `/` in the composer and click it — it lands verbatim in the input, ready to edit and send. It is **never injected**; its switch controls exactly one thing — whether the row still shows up in the `/` “Quick prompts” section (text and note survive when it's off; the same switch is reachable under “Tools › Prompts”). Stored apart from the baselines, deletable to the recycle bin, exportable and included in the migration snapshot.
136
- - **A scene can bind a set of quick prompts**: a new dropdown under “Prompt preset” in “Edit scene”. The default, “The enabled ones (keep as is)”, means the scene does not touch this domain at all; switch it to “Pick per scene” and entering that scene enables exactly the checked ones and disables the rest, **leaving the scene restores the switches as they were before entering**. Checking nothing is "disable all inside this scene", which is the opposite of "not bound".
137
- - **Both groups are collapsible cards** (click the header to fold, and a search query forces them open). The quick group's header carries a “Select all / Deselect all” button that applies to the currently visible rows only; the global group has none — that one is single choice, so "apply every baseline" has no meaning.
138
-
139
- ### Archived sessions
140
-
141
- - Grouped by project, searchable, batch restore / delete, retention auto-cleanup; a workspace whose registration was deleted is rebuilt from the session directories and can be re-registered in one click.
142
- - Import Claude Code / Cursor / Codex / any text — pick a registered workspace, browse to a folder, or give a path directly; export Markdown / JSONL in three cumulative modes: text only → plus tool calls and results and attachment names → plus reasoning. **An export is still a readable transcript, not a full backup**: the added blocks are capped and say so, and token stats are not included. Use archiving when you need the full record.
143
-
144
- ### Host compatibility
145
-
146
- The plugin uses the host's own `@deepseek-ai/*` libraries at runtime — they must be the same physical modules, or every "adapt to the host" decision degrades into guesswork.
147
-
148
- - **Host tab**: host version, usable capability count, per-action routing (native / adapter / unavailable), degradations and reasons. The page has three write entries: **access token** (needs a restart), **injection settings**, **model tool table**.
149
- - **Command line**: `node scripts/doctor.mjs` (health check), `node scripts/host-deps.mjs --fix` (align dependencies), `npm run sync:profile` (mirror the build into the profile's local install — a `file:` install is a hard-linked copy, so files a build *adds* never show up there on their own).
150
- - Under a **suppressing preset** such as `minimal`, this plugin's injection is **off by default** (following the preset's intent) and the prompt and skills are absent because their official rows are not mounted — the Host tab marks this per column, and its Injection block can force any domain back on.
151
- - **Unchecking "Skills" or "Prompt" really stops them** — under standard presets the host delivers those two itself, so unchecking also stops the host's copy. The other three domains are only ever sent by this plugin, so their toggles work as written.
152
- - **What an injection looks like**: one `<system-reminder>` per domain — a `#` section title, then the body (MCP and subagents add one line each). The frame carries **only what is now**: no "when to recall me" instructions, no explanation of the other sections. Any `</system-reminder>` inside your content is escaped, and each message closes with "this copy replaces earlier copies of the same kind in this session" — injections are re-sent only when the content changes, so the model must be told which copy to trust.
153
-
154
- ---
155
-
156
- ## Where data lives
157
-
158
- | Content | Location |
159
- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
160
- | MCP definitions | `~/.dsh/cordis.patch.yml` (written by the plugin; pre-write copies land in the hub's `backups/`) |
161
- | Skill policy / custom dirs | `~/.dsh/tool-management/skills-state.json` |
162
- | Skills / memories / personas / presets | `~/.dsh/tool-management/{skills,memories,subagents,prompts}/` |
163
- | Subagent toggles | `~/.dsh/tool-management/subagents-index.json` |
164
- | Recycle bin (skills / personas / presets / scenes) | `~/.dsh/tool-management/trash/{skills,subagents,prompts,scenes}-trash/` |
165
- | Memory recycle bin | `~/.dsh/tool-management/memories-trash/` (a directory at the hub root, not under `trash/`) |
166
- | Archive ledger / retention | `~/.dsh/tool-management/history-*.json` |
167
- | Memory index / scenes / profiles | `~/.dsh/tool-management/memories-index.json` |
168
- | MCP sidecars (disabled tools / known tools / notes / settings) | `~/.dsh/tool-management/mcp-*.json` |
169
- | Injection settings (six domain switches) | `~/.dsh/tool-management/inject-settings.json` |
170
- | Model tool table (switched-off tools + saved sets) | `~/.dsh/tool-management/tool-table.json` |
171
- | Scene page preference (preview card before entering a scene) | `~/.dsh/tool-management/scene-settings.json` |
172
- | Runtime log / patch backups | `~/.dsh/tool-management/tool-management.log` · `backups/` |
173
-
174
- **No user data is stored inside the plugin's install directory** (`dsh plugin update` replaces it wholesale). `backups/` keeps **5 copies per patch file** — the global patch and each profile patch count separately — so one bulk action can consume all five slots of a layer.
175
-
176
- ## Configuration & security
177
-
178
- | Field | What it does |
179
- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
180
- | `token` | Access token. Once set, **every write and every plaintext credential** requires `x-dsh-token`; with no token the plaintext endpoints are closed entirely. Prefer not to keep it in the config? Use the `DSH_PLUGIN_TOOL_MANAGEMENT_TOKEN` environment variable. |
181
- | `tokenDisabled` | `true` = the token stays in the config but is not enforced (this is the line written by "Turn protection off"). Writes no longer need it; revealing plaintext still does. Turning it back on needs no retype. |
182
- | `maxBodyBytes` | Request body cap, 88 MiB by default. |
183
-
184
- **Plaintext on disk (read this)**: masking is **display-only**. MCP `env` / `headers` and the plugin's own `token` stay in cleartext inside `~/.dsh/cordis.patch.yml` (and each profile copy), and every config change copies the **whole file** into `~/.dsh/tool-management/backups/` — unencrypted, never rotated, not even reclaimed on uninstall. One secret can therefore exist as `5 × (patch files holding it) + 1` plaintext copies. The token gate decides *who may read plaintext over HTTP*; it does nothing about *reading the files*, where the only real defence is your filesystem permissions. **To clean up: Settings → Tools → Host → "Clean old backups"** — pick how many to delete per layer and confirm once more in the dialog; only backup files are touched. You can also delete them by hand.
185
-
186
- - **Browser**: reads and writes go through cookies, no token needed; **plaintext credentials** (reveal / export) do. The token is managed in the **Access token** block on the Host tab — filling it once unlocks the current run, writing it into the config needs a restart.
187
- - **Every destructive direction asks for the current token again** (turn protection off / change / delete). Being unlocked this run does not count — otherwise anyone who can open the GUI could drop the guard.
188
- - **curl / scripts**: send `x-dsh-token`, or carry the browser cookie.
189
- - **HTTP status convention**: except for the fence's 401 / 403, a missing token or a business failure is **HTTP 200 + `{ ok: false, error }`** — branch on `body.ok`, not on the status code.
190
- - **Port forwarding**: when the host has no `connection` service the fence falls back to "loopback Host + same origin", which any local process can imitate with `Host: localhost`. If you forward the port to a LAN or the public internet, **configure a token** — a stranger injecting MCP commands is remote code execution. The `dir-list` call behind the folder picker lists any absolute path (read-only) under the same precondition.
191
- - **`__` in a server name (naming advice)**: a tool's full name is `mcp__<server>__<tool>`, so an extra `__` inside a server name makes it **ambiguous** — `mcp__a__b__c` can be server `a`'s tool `b__c` or server `a__b`'s tool `c`; the same goes for two server names that are `__`-prefix related (`a` and `a__b`). This release deliberately **does not tighten validation** (the 5 places that accept `__` today would turn existing servers from "manageable" into "unmanageable" while the servers themselves keep running); instead the Host tab shows an informational note. The tools keep working — only a checkbox in the UI may fail to land on the key used in the disabled-tools table. **Avoid `__` when naming and none of this applies.**
192
-
193
- ## FAQ
194
-
195
- | Symptom | Fix |
196
- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
197
- | Pages missing after install | Hard refresh; restart DSH if that fails. |
198
- | Duplicate MCP tabs | Remove the stale loader row from `cordis.patch.yml`, restart. |
199
- | Broken config, DSH won't boot | Restore the newest `cordis.patch.yml.<level>.bak-<timestamp>` from `~/.dsh/tool-management/backups/` (5 per patch file). |
200
- | Action stopped working after a DSH upgrade | Settings → Tools → **Host** for the reason; `doctor.mjs` → `host-deps.mjs --fix`. |
201
- | The model cannot call a tool it should have | Check the **Model tool table** on the Host tab — 15 tools ship switched off. The panel and scripts are unaffected either way. |
202
- | Still asked to confirm with `approval=never`? | No card appears — straight through with a log line. Switch back to "workspace write" to get the questions again. |
203
- | `subagent_manager_run` reports provider unavailable | The provider is not registered: `spawn` (default) / `fork` (`inherit`) come from `@deepseek-ai/dsh-subagent-spawn-in-process` / `-fork-in-process` — mount and restart. |
204
- | Scene binds persona A, but the official `subagent` ran something else | Those two are host tools and this plugin cannot hide them; it writes the boundary into the context instead — work matching a persona goes to `subagent_manager_run`, host tools only when no persona fits or a background job is needed. |
205
-
206
- ---
207
-
208
- ## Development
209
-
210
- ```bash
211
- npm install
212
- npm run build # tsc + sync client
213
- npm test # build + i18n + contract tests (pure-function invariants & host contracts)
214
- npm run check:i18n # dictionary self-check
215
- npm run doctor # host compatibility check
216
- ```
217
-
218
- `lib/` is not tracked — run `npm run build` after cloning. Changes need `dsh web` restarted. Runtime deps: `fflate` (export bundling) and `js-yaml` (parse check before writing host patches, loaded lazily); `@deepseek-ai/*` all come from the host.
219
-
220
- > **Deployment note**: the profile mirror produced by `npm run build` does **not** include `node_modules`, so a runtime dependency added locally (e.g. `js-yaml`) must either be installed on the profile side or accepted as "patch-write check skipped and reported" — the missing checker only disables that one safety net and never blocks writes. Regular `npm install` deployments are unaffected.
221
-
222
- ## License
223
-
224
- MIT
1
+ <p align="center">
2
+ <img src="docs/images/logo.png" width="120" height="120" alt="dsh-plugin-tool-management">
3
+ </p>
4
+
5
+ # dsh-plugin-tool-management
6
+
7
+ [![npm version](https://img.shields.io/npm/v/dsh-plugin-tool-management?logo=npm&color=cb3837)](https://www.npmjs.com/package/dsh-plugin-tool-management)
8
+ [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
9
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js)](package.json)
10
+ [![GitHub](https://img.shields.io/badge/GitHub-ouli--1242%2Fdsh--plugin--tool--management-181717?logo=github)](https://github.com/ouli-1242/dsh-plugin-tool-management)
11
+
12
+ [![DSH Market](https://raw.githubusercontent.com/2BingLing/dsh-market/master/assets/readme/badge-listed-en.svg)](https://dsh.market/)
13
+ [![awesome-dsh-plugin](https://img.shields.io/badge/awesome--dsh--plugin-listed-3fb950)](https://awesome-dsh-plugin.com)
14
+ [![dshfind](https://dshfind.com/api/badge/ouli-1242/dsh-plugin-tool-management?lang=en)](https://dshfind.com/en/plugins/ouli-1242/dsh-plugin-tool-management)
15
+ [![0xsline](https://img.shields.io/badge/0xsline-listed-3fb950)](https://github.com/0xsline/awesome-deepseek-harness)
16
+
17
+ [简体中文](README.md) · **English** · [Changelog](CHANGELOG.md) · [Release overview](docs/update.md)
18
+
19
+ An **MCP, skills, scenes, memories, subagents, prompts & archived sessions** manager for DeepSeek Harness. Eight tabs: Scenes / MCP / Skills / Subagents / Prompts / Memories / Sessions / Host.
20
+
21
+ ```sh
22
+ dsh plugin --profile web add dsh-plugin-tool-management@latest # web
23
+ dsh plugin --profile desktop add dsh-plugin-tool-management@latest # desktop
24
+ ```
25
+
26
+ Hard-refresh the browser after installing for web (Cmd/Ctrl+Shift+R), or restart the desktop app after installing for desktop — a **Tools** panel in Settings means it worked. The plugin only writes its own files, never touches skill sources, and your configuration survives restarts and upgrades.
27
+
28
+ ---
29
+
30
+ ## Demo
31
+
32
+ <video controls src="https://github.com/user-attachments/assets/91b9673d-d201-462a-83de-8bc663c7629d"></video>
33
+
34
+ Every interface shown is a real screenshot of the running product — no redrawn UI, no fabricated controls.
35
+
36
+ Music: Kevin MacLeod「Limit 70」, [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
37
+
38
+ ---
39
+
40
+ ## Screenshots
41
+
42
+ | | |
43
+ |:---------------------------------:|:----------------------------------:|
44
+ | ![Scenes](docs/images/1-EN.png) | ![MCP](docs/images/2-EN.png) |
45
+ | **Scenes** | **MCP** |
46
+ | ![Skills](docs/images/3-EN.png) | ![Subagents](docs/images/4-EN.png) |
47
+ | **Skills** | **Subagents** |
48
+ | ![Prompts](docs/images/5-EN.png) | ![Memories](docs/images/6-EN.png) |
49
+ | **Prompts** | **Memories** |
50
+ | ![Sessions](docs/images/7-EN.png) | ![Host](docs/images/8-EN.png) |
51
+ | **Sessions** | **Host** |
52
+
53
+ ## Highlights
54
+
55
+ In one line: **configure "work / writing / coding" each as a scene and switch the whole stack with one click — and the model always sees whatever this plugin manages.**
56
+
57
+ | Highlight | What it means |
58
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
59
+ | One-click scene switch | Each scene carries its own MCP / skills / personas / memories; entering applies them, leaving restores. The model can switch for you too |
60
+ | Manage without leaving chat | Type `/` for a "Tools" section and toggle any domain's items — one click, done |
61
+ | Memories reach the model | A few `.md` files under a scene become its knowledge base, injected automatically |
62
+ | Notes for MCP servers | A note you write is seen by the model every turn and acted on |
63
+ | Disable a single tool | Mute one tool on a server: invisible to the model and uncallable |
64
+ | Skills at a glance | The copy in effect is marked "preferred"; shadowed ones name the winning source |
65
+ | Subagent = one file, one role | Write a role file and delegate to it; only the result comes back, never in History |
66
+ | Several prompt presets | Keep multiple `AGENTS.md` baselines and switch with one click; each scene can bind its own, or none |
67
+ | Sessions no longer lost | Archives grouped by project, searchable, batch-restorable; imports Claude Code / Cursor / Codex transcripts |
68
+ | The model always sees it | One context message per domain, republished only when the content changes |
69
+ | Lock it and relax | Locking a scene makes create/update/delete across the five domains read-only |
70
+ | Deleted is not gone | Skills / memories / personas / presets / scenes land in a recycle bin (permanent session deletion excepted); tick items to delete in bulk, or set a retention period so it cleans itself (keep-forever by default) |
71
+ | Safe by default | Only the plugin's own files are written; secrets masked, plaintext needs a token |
72
+
73
+ ## Quick start
74
+
75
+ Prerequisites: DSH installed (`dsh web` runs), Node.js ≥ 18. Pick the profile matching your client: `--profile web` for the browser app, `--profile desktop` for the desktop app. The desktop profile is initialized by DSH Desktop itself — open the app once, then quit it completely before running the command.
76
+
77
+ ```sh
78
+ dsh plugin --profile web add dsh-plugin-tool-management@latest # web · install / update
79
+ dsh plugin --profile web remove dsh-plugin-tool-management # web · uninstall
80
+ dsh plugin --profile desktop add dsh-plugin-tool-management@latest # desktop · install / update
81
+ dsh plugin --profile desktop remove dsh-plugin-tool-management # desktop · uninstall
82
+ ```
83
+
84
+ Hard-refresh the browser (web) or restart the desktop app, then a **Tools** panel with eight tabs means it worked. Client changes hot-reload; host-side changes need the client restarted (`dsh web` / the desktop app).
85
+
86
+ You can also ask the model:
87
+
88
+ ```text
89
+ Install the dsh-plugin-tool-management plugin:
90
+ dsh plugin --profile web add dsh-plugin-tool-management@latest
91
+ Then remind me to hard-refresh the browser.
92
+ ```
93
+
94
+ For the desktop app, swap `web` for `desktop` and end with "remind me to restart the desktop app" instead.
95
+
96
+ ## Features
97
+
98
+ ### Slash commands (manage from the chat box)
99
+
100
+ - **Type `/` for a "Tools" section**: scenes / skills / MCP servers / subagents / prompts / memories. Click a row to enter that domain, click an item to apply it; the menu stays open.
101
+ - **State is visible** (green check / empty box / note in grey), and long lists scroll on their own; each external skill directory gets one row that toggles the whole source.
102
+ - **Only reversible actions**: entering a scene and the various toggles; create / delete / edit / import / export stay on their own pages.
103
+
104
+ ### Scenes & memories
105
+
106
+ - **A scene = a group, a memory = a `.md` file**: `memories/<scene>/<name>.md`, the whole body is injected.
107
+ - **"Enabled" and "entered" are two axes**: enabled decides which scene the injection follows (single-select; none enabled = only `global` and `_shared`); entering applies the scene's whole MCP / skills / personas set and writes its bound prompt into `AGENTS.md`.
108
+ - **Scene profile**: ticked = on, unchecked = off, a section never created means that domain is off; leaving restores the pre-scene state.
109
+ - **Every domain offers "None"**: for MCP / skills / personas / memories / global prompt it means the scene leaves that domain alone; for quick prompts it means none are enabled while the scene is on, and it is the default for a new scene.
110
+ - **Scene lock** makes those five domains read-only; deleting a scene takes its memories with it into one recycle-bin entry. Import / export `.md` / `.zip`, same names skipped.
111
+
112
+ ### Subagents
113
+
114
+ - **One file per persona**: `subagents/<persona>.md`, frontmatter entirely optional.
115
+ - **Delegation**: `subagent_manager_run` runs with the persona, returns only its final result, and never enters History; `inherit: true` seeds the child with this conversation's finished turns.
116
+ - **On/off**: a disabled persona is neither injected nor visible to the model; new / imported / restored personas start disabled.
117
+ - **Tool limits per agent preset**: one allow or deny list per preset (mutually exclusive). Model and provider are picked from the host LLM catalogue; empty means inherit the main session.
118
+
119
+ ### MCP servers
120
+
121
+ - **CRUD with immediate effect**: written into `cordis.patch.yml`, backed up before every change.
122
+ - **Per-tool switches**: disable individual tools, or a whole server; a stopped server still shows the tools last seen.
123
+ - **Secret masking**: a credential is replaced as a whole, and "Reveal" needs a token; a masked value is never written back.
124
+ - A per-server note travels along as a decision hint; migration rolls back on failure, with JSON export/import.
125
+
126
+ ### Skills
127
+
128
+ - **Sources at a glance**: project / DSH / Agents / Codex / Claude / custom directories, grouped by source.
129
+ - **Opposite permissions**: default sources are readable and their skills deletable; external directories can be disabled or removed but their files stay read-only.
130
+ - **Remove ≠ disable**: removing stops scanning the directory at all (nothing is written, restorable); disabling keeps it listed but uncallable.
131
+ - The winning copy of a shared name is marked "preferred"; custom directories, ZIP import/export and the recycle bin are all here.
132
+
133
+ ### Prompt presets
134
+
135
+ - Multiple `~/.dsh/AGENTS.md` baselines, applied with one click (next turn), with 5 generations of backups; each preset can carry a one-line description that is never injected.
136
+ - **The switch on the left of a card is the answer to "is this one in effect"**: flip it on and that preset is written into `AGENTS.md`; flip it off to **un-apply**, which restores the state from before you started applying (and deletes the file if there was none back then). The confirmation says which one it will be, and a receipt afterwards states what happened.
137
+ - **Two kinds cannot be deleted directly**: a scene-bound preset, and the one currently applied. The copy auto-created on first open can be deleted. Deletes always go to the recycle bin.
138
+ - **Quick prompts** (the second section on the same page): save a piece of text you keep typing, then hit `/` and click it; a scene can bind a set of them.
139
+ - **This page's recycle bin is split in two by domain**, each half scrolling on its own with its own batch bar.
140
+
141
+ ### Archived sessions
142
+
143
+ - Grouped by project, searchable, batch restore / delete, retention auto-cleanup; a workspace whose registration was deleted can be re-registered in one click.
144
+ - Import Claude Code / Cursor / Codex / any text; export Markdown / JSONL in three cumulative modes: text only → plus tool calls → plus reasoning. **An export is a transcript, not a backup**.
145
+
146
+ ### Host compatibility
147
+
148
+ - **Host tab**: host version, usable capability count, per-action routing (native / adapter / unavailable), degradations and reasons; three write entries: access token, injection settings, model tool table.
149
+ - **Command line**: `node scripts/doctor.mjs` (health check), `node scripts/host-deps.mjs --fix` (align dependencies).
150
+ - Under a **suppressing preset** such as `minimal`, this plugin's injection is off by default; the Host tab can force any domain back on.
151
+ - Unchecking "Skills" or "Prompt" really stops them — under standard presets the host delivers those two itself.
152
+
153
+ ---
154
+
155
+ ## Where data lives
156
+
157
+ | Content | Location |
158
+ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
159
+ | MCP definitions | `~/.dsh/cordis.patch.yml` (written by the plugin; pre-write copies land in the hub's `backups/`) |
160
+ | Skill policy / custom dirs | `~/.dsh/tool-management/skills-state.json` |
161
+ | Skills / memories / personas / presets | `~/.dsh/tool-management/{skills,memories,subagents,prompts}/` |
162
+ | Subagent toggles | `~/.dsh/tool-management/subagents-index.json` |
163
+ | Recycle bin (skills / personas / presets / scenes) | `~/.dsh/tool-management/trash/{skills,subagents,prompts,scenes}-trash/` |
164
+ | Memory recycle bin | `~/.dsh/tool-management/memories-trash/` (a directory at the hub root, not under `trash/`) |
165
+ | Archive ledger / retention | `~/.dsh/tool-management/history-*.json` |
166
+ | Recycle-bin retention | `~/.dsh/tool-management/trash-retention.json` (`0` = keep forever) |
167
+ | Memory index / scenes / profiles | `~/.dsh/tool-management/memories-index.json` |
168
+ | MCP sidecars (disabled tools / known tools / notes / settings) | `~/.dsh/tool-management/mcp-*.json` |
169
+ | Injection settings (six domain switches) | `~/.dsh/tool-management/inject-settings.json` |
170
+ | Model tool table (switched-off tools + saved sets) | `~/.dsh/tool-management/tool-table.json` |
171
+ | Scene page preference (preview card before entering a scene) | `~/.dsh/tool-management/scene-settings.json` |
172
+ | Runtime log / patch backups | `~/.dsh/tool-management/tool-management.log` · `backups/` |
173
+
174
+ **No user data is stored inside the plugin's install directory** (`dsh plugin update` replaces it wholesale). `backups/` keeps **5 copies per patch file** — the global patch and each profile patch count separately.
175
+
176
+ ## Configuration & security
177
+
178
+ | Field | What it does |
179
+ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
180
+ | `token` | Access token. Once set, **every write and every plaintext credential** requires `x-dsh-token`; with no token the plaintext endpoints are closed entirely. Prefer not to keep it in the config? Use the `DSH_PLUGIN_TOOL_MANAGEMENT_TOKEN` environment variable. |
181
+ | `tokenDisabled` | `true` = the token stays in the config but is not enforced (this is the line written by "Turn protection off"). Writes no longer need it; revealing plaintext still does. |
182
+ | `maxBodyBytes` | Request body cap, 88 MiB by default. |
183
+
184
+ **Plaintext on disk (read this)**: masking is **display-only**; MCP `env` / `headers` and the plugin's own `token` stay in cleartext inside `cordis.patch.yml` (and each profile copy), and every config change copies the whole file into `~/.dsh/tool-management/backups/` — unencrypted, never rotated, not reclaimed on uninstall. One secret can therefore exist as `5 × (patch files holding it) + 1` plaintext copies. The token gate decides *who may read plaintext over HTTP*; it does nothing about *reading the files*, where the only defence is your filesystem permissions. **To clean up: Settings → Tools → Host → "Clean old backups"**.
185
+
186
+ - **Browser**: reads and writes go through cookies, no token needed; **plaintext credentials** (reveal / export) do. The token is managed in the **Access token** block on the Host tab.
187
+ - **Every destructive direction asks for the current token again** (turn protection off / change / delete). Being unlocked this run does not count.
188
+ - **curl / scripts**: send `x-dsh-token`, or carry the browser cookie. Except for the fence's 401 / 403, failures come back as **HTTP 200 + `{ ok: false, error }`** — branch on `body.ok`.
189
+ - **Port forwarding**: when the host has no `connection` service the fence falls back to "loopback Host + same origin", which any local process can imitate. If you forward the port to a LAN or the public internet, **configure a token**.
190
+ - **Avoid `__` in a server name**: a tool's full name is `mcp__<server>__<tool>`, so an extra `__` inside a server name makes it ambiguous; the Host tab shows an informational note.
191
+
192
+ ## FAQ
193
+
194
+ | Symptom | Fix |
195
+ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
196
+ | Pages missing after install | Hard refresh; restart DSH if that fails. |
197
+ | Duplicate MCP tabs | Remove the stale loader row from `cordis.patch.yml`, restart. |
198
+ | Broken config, DSH won't boot | Restore the newest `cordis.patch.yml.<level>.bak-<timestamp>` from `~/.dsh/tool-management/backups/` (5 per patch file). |
199
+ | Action stopped working after a DSH upgrade | Settings → Tools → **Host** for the reason; `doctor.mjs` → `host-deps.mjs --fix`. |
200
+ | The model cannot call a tool it should have | Check the **Model tool table** on the Host tab — 15 tools ship switched off. The panel and scripts are unaffected either way. |
201
+ | Still asked to confirm with `approval=never`? | No card appears — straight through with a log line. Switch back to "workspace write" to get the questions again. |
202
+ | `subagent_manager_run` reports provider unavailable | The provider is not registered: `spawn` (default) / `fork` (`inherit`) come from `@deepseek-ai/dsh-subagent-spawn-in-process` / `-fork-in-process` — mount and restart. |
203
+ | Scene binds persona A, but the official `subagent` ran something else | Those two are host tools and this plugin cannot hide them; work matching a persona goes to `subagent_manager_run`, host tools only when no persona fits or a background job is needed. |
204
+
205
+ ---
206
+
207
+ ## Development
208
+
209
+ ```bash
210
+ npm install
211
+ npm run build # tsc + sync client
212
+ npm test # build + i18n + contract tests (pure-function invariants & host contracts)
213
+ npm run check:i18n # dictionary self-check
214
+ npm run doctor # host compatibility check
215
+ ```
216
+
217
+ `lib/` is not tracked — run `npm run build` after cloning. Changes need `dsh web` restarted. Runtime deps: `fflate` (export bundling) and `js-yaml` (parse check before writing host patches, loaded lazily); `@deepseek-ai/*` all come from the host.
218
+
219
+ > **Deployment note**: the profile mirror produced by `npm run build` does **not** include `node_modules`, so a runtime dependency added locally (e.g. `js-yaml`) must either be installed on the profile side or accepted as "patch-write check skipped and reported". Regular `npm install` deployments are unaffected.
220
+
221
+ ## License
222
+
223
+ MIT
Binary file
package/docs/images/1.png CHANGED
Binary file
Binary file
package/docs/images/2.png CHANGED
Binary file
Binary file
package/docs/images/3.png CHANGED
Binary file
Binary file
package/docs/images/4.png CHANGED
Binary file
Binary file
package/docs/images/5.png CHANGED
Binary file
Binary file
package/docs/images/6.png CHANGED
Binary file
Binary file
package/docs/images/7.png CHANGED
Binary file
Binary file
package/docs/images/8.png CHANGED
Binary file
Binary file
Binary file
Binary file