dsh-plugin-tool-management 0.13.0 → 0.15.0

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 (62) hide show
  1. package/CHANGELOG.md +560 -474
  2. package/README.md +91 -123
  3. package/README_EN.md +99 -126
  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/update.md +391 -337
  21. package/lib/audit-log.js +157 -0
  22. package/lib/client.js +4204 -2454
  23. package/lib/compat/patch-dialect.js +3 -1
  24. package/lib/compat/preset-reach.js +43 -9
  25. package/lib/compat/probe.js +149 -7
  26. package/lib/context-inject.js +91 -35
  27. package/lib/index.js +479 -114
  28. package/lib/mcp/loader-token.js +53 -28
  29. package/lib/mcp/manager.js +149 -21
  30. package/lib/mcp/secret-guard.js +21 -0
  31. package/lib/mcp/state-section.js +13 -11
  32. package/lib/memories/archive-engine.js +70 -2
  33. package/lib/memories/archive.js +8 -1
  34. package/lib/memories/constants.js +72 -8
  35. package/lib/memories/index-io.js +7 -1
  36. package/lib/memories/projection.js +67 -31
  37. package/lib/memories/service.js +17 -2
  38. package/lib/memories/snapshot.js +113 -22
  39. package/lib/op-registry.js +26 -6
  40. package/lib/ops/candidates.js +11 -0
  41. package/lib/ops/compat.js +113 -31
  42. package/lib/ops/scene-records.js +34 -0
  43. package/lib/ops/scene-sync.js +12 -1
  44. package/lib/ops/sessions.js +24 -9
  45. package/lib/ops/snapshot.js +596 -0
  46. package/lib/prompts/service.js +25 -2
  47. package/lib/scenes/candidates.js +66 -5
  48. package/lib/sessions/bridge.js +5 -0
  49. package/lib/sessions/workspace.js +17 -5
  50. package/lib/skills/core.js +105 -3
  51. package/lib/skills/service.js +34 -2
  52. package/lib/subagents/service.js +37 -4
  53. package/lib/subagents/tools.js +11 -4
  54. package/lib/tools/deps.js +16 -1
  55. package/lib/tools/mcp.js +291 -50
  56. package/lib/tools/memory.js +131 -80
  57. package/lib/tools/prompt.js +27 -13
  58. package/lib/tools/scene.js +351 -0
  59. package/lib/tools/skills.js +93 -17
  60. package/lib/tools/subagent.js +56 -45
  61. package/lib/tools/table.js +208 -15
  62. package/package.json +24 -20
package/README_EN.md CHANGED
@@ -12,14 +12,13 @@
12
12
 
13
13
  [简体中文](README.md) · **English** · [Changelog](CHANGELOG.md) · [Release overview](docs/update.md)
14
14
 
15
- - An **MCP, skills, scenes, memories, subagents, prompts & archived sessions** manager for DeepSeek Harness.
16
- - Eight tabs: **Scenes**, **MCP**, **Skills**, **Subagents**, **Prompts**, **Memories**, **Sessions**, **Host**.
15
+ An **MCP, skills, scenes, memories, subagents, prompts & archived sessions** manager for DeepSeek Harness. Eight tabs: Scenes / MCP / Skills / Subagents / Prompts / Memories / Sessions / Host.
17
16
 
18
17
  ```sh
19
18
  dsh plugin --profile web add dsh-plugin-tool-management@latest
20
19
  ```
21
20
 
22
- Hard-refresh the browser (Cmd/Ctrl+Shift-R) afterwards — a **Tools** panel in Settings means it worked. No hand-editing of `cordis.patch.yml`, no skill source files touched, configuration survives restarts and upgrades.
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.
23
22
 
24
23
  ---
25
24
 
@@ -38,22 +37,22 @@ Hard-refresh the browser (Cmd/Ctrl+Shift-R) afterwards — a **Tools** panel in
38
37
 
39
38
  ## Highlights
40
39
 
41
- In one line: **set up a scene for each kind of work — "day job / writing / coding" — and switch the whole stack with one click. Everything the plugin manages, the model can actually see.**
42
-
43
- | Highlight | What it means |
44
- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
- | One-click scene switch | Each scene carries its own set: which MCP servers, which skills, which personas, which memories; flip it on and the whole stack follows, turn it off and everything comes back |
46
- | Memories reach the model on their own | Write a few `.md` files under a scene and they become its knowledge base — injected automatically, no copy-pasting every session |
47
- | Notes for MCP servers | Write "if A is down, fall back to B" as a note — the model sees it 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 switches |
49
- | Skills at a glance | Which copy is in effect, which is shadowed by a same-name skill, which is preferred — all marked in the list |
50
- | Subagent = one file, one role | Write a role file and delegate; only the result comes back and it never clutters your History; which roles are available can follow the scene |
51
- | Several prompt presets | Keep multiple AGENTS.md baselines (terse mode, teaching tone, …), switch with one click; a scene can bind its own |
52
- | Sessions no longer lost | Archive grouped by project, searchable, batch-restorable; import transcripts from Claude Code / Cursor / Codex |
53
- | The model always sees it | Everything the plugin manages (memories / MCP / skills / subagents / prompts) is announced to the model — one message per domain, republished only on change; under Minimal nothing is injected by default (follows the preset), force any domain on in the Host tab |
54
- | Lock it and relax | Lock a scene to make create/update/delete in all five domains read-only; unlock first to change anything (creating/renaming/binding/activating a scene, the recycle bin, restart, export and token settings are **not** in the frozen list — see Scene lock) |
55
- | Deleted is not gone | Deletes of skills / memories / personas / presets / scenes land in a recycle bin and can be restored; they also zip out and back in. **Session history is the exception: permanent delete skips the recycle bin and cannot be undone** |
56
- | Safe by default | Secrets masked, plaintext needs a token; only the plugin's own files are written, skill sources stay untouched, and your config survives restarts and upgrades. **Masking is display-only**: secrets stay in cleartext in `cordis.patch.yml` and in up to 5 backup copies per patch file (see Configuration & security) |
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; entering applies them, leaving restores. The model can switch them for you too |
45
+ | Memories reach the model | A few `.md` files under a scene become its knowledge base — injected automatically, no copy-pasting |
46
+ | 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 |
47
+ | Disable a single tool | Keep a server but mute one tool: invisible and uncallable. "Restart" only reconnects and never flips your switches |
48
+ | Skills at a glance | The copy in effect is marked *preferred*; shadowed ones name the source that wins |
49
+ | Subagent = one file, one role | Write a role file and delegate to it; only the result comes back, and it never clutters your History |
50
+ | Several prompt presets | Keep multiple `AGENTS.md` baselines and switch with one click; each scene can bind its own |
51
+ | Sessions no longer lost | Archives grouped by project, searchable, batch-restorable; imports Claude Code / Cursor / Codex transcripts |
52
+ | 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 |
53
+ | Lock it and relax | Locking a scene makes create/update/delete across the five domains read-only until you unlock |
54
+ | Deleted is not gone | Skills / memories / personas / presets / scenes land in a recycle bin and can be restored (permanent session deletion is the exception) |
55
+ | 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
56
 
58
57
  ## Quick start
59
58
 
@@ -61,7 +60,7 @@ Prerequisites: DSH installed (`dsh web` runs), Node.js ≥ 18.
61
60
 
62
61
  ```sh
63
62
  dsh plugin --profile web add dsh-plugin-tool-management@latest # install / update
64
- dsh plugin --profile web remove dsh-plugin-tool-management # uninstall
63
+ dsh plugin --profile web remove dsh-plugin-tool-management # uninstall
65
64
  ```
66
65
 
67
66
  Hard-refresh the browser — a **Tools** panel with eight tabs means it worked. Client changes hot-reload; host-side changes need `dsh web` restarted.
@@ -74,145 +73,119 @@ dsh plugin --profile web add dsh-plugin-tool-management@latest
74
73
  Then remind me to hard-refresh the browser.
75
74
  ```
76
75
 
77
- The model can manage everything above via 20 tools (`mcp_manager_*` / `skill_manager_*` / `prompt_manager_*` / `memory_manager_*` / `subagent_manager_*`); scripts use `POST /dsh-plugin-tool-management/api` (`{op, args}` protocol).
78
-
79
- Those 20 tool definitions total roughly 3,450 tokens, and the tool table is **sent with every single request**. Switch off the ones you never use in the **"Model tool table"** block on the **Host tab** (one by one, or a whole domain at once) — a switched-off tool is not sent at all, which is the only thing that genuinely saves tokens. The model then cannot call it, but **the panel is unaffected** (the 114 ops and the tool table are independent). Nothing is switched off by default.
80
-
81
- One tool also **yields automatically**: when the official `skill` tool is mounted in that conversation (every standard-class preset mounts it), our `skill_manager_read` ("load a skill body by name") is not sent — the two do the same job. The minimal preset has no official one, so ours stays.
82
-
83
- ---
84
-
85
76
  ## Features
86
77
 
87
78
  ### Scenes & memories
88
79
 
89
- - **A scene = a group, a memory = a `.md` file**. `memories/<scene>/<name>.md`, the whole body is injected, file names can be non-ASCII.
90
- - **Single-choice toggle**: only one scene at a time (others greyed out); turning all off = only `global` and `_shared` inject. New scenes start off.
91
- - **Scene-bound prompt**: switching scenes rewrites `~/.dsh/AGENTS.md` (5-gen backup, auto-restore on exit).
92
- - **Scene profile**: every scene combines MCP tools / skills / subagents / memories (any mix); opening a scene applies and narrows injection, closing restores verbatim (the toggle is the only entry). Checked means on, unchecked means off, and **a missing section means nothing is checked — so that whole domain is off** (a scene with no MCP section therefore stops every MCP server, and exit starts them again). While a scene is active those switches (MCP, skills, subagents) still work — changes are written into that scene's profile too (they take effect immediately and are kept for the next visit); only locking freezes them. The memory domain has a single source of truth and never goes through the profile.
93
- - **"Entering/leaving the mode" and "enabling a scene" are two independent state axes**: the UI toggle is the only entry point (it moves both axes at once). If you bypass the UI and call the HTTP API directly, note that `scene-mode-set{scene:null}` only drops the runtime snapshot and does **not clear the enabled set** — memories and the prompt keep being injected for that scene; a full exit also needs `rules-set-active{scenes:[]}`.
94
- - **Import**: `.md` / `.zip` (dir name = scene, bundles carry attachments), same names skipped never overwritten, over-limit items reported.
95
- - **Export**: pick memories and zip them, keeping the `scene/name` layout; bundle memories bring their attachments along. Sources are read-only.
96
- - **Injection budget**: default 128 KiB, oversized memories skipped with a list. Deletes go to recycle bin.
97
- - **Subagent sessions do not receive memories**: memories are injected into the **top-level session only** — they are the parent's situation, not the facts a child needs; a child's context stays "persona + task", it can read memories on demand with `memory_manager_list/read`, and relevant facts belong in the `task`. Other domains are unaffected (MCP / skills / prompts still inject; the persona catalog follows each persona's `catalogDepth`).
98
- - **Scene lock**: once locked, create/update/delete across MCP / skills / subagents / memories / prompts is read-only — UI disabled plus a server-side guard; a scene must be running to lock, and a locked scene can't be closed until unlocked. **What freezes is the content, not the scene itself**: creating/deleting/renaming a scene, rebinding its prompt, switching the active set, recycle-bin restore and purge, MCP restart, export, injection settings and token settings are all outside the frozen list (they are not "content of the scene profile"). Switching the active set *is* refused while a locked scene is in effect — clearing it would leave the model-side write guard with nothing to check while the runtime still runs that scene's profile.
99
- - **Deleting a scene deletes its memories too**: the scene record, profile and every memory go into one recycle-bin entry, restored as a whole; a running scene refuses deletion. Scene names are renameable (dir and profile follow, memory bodies untouched).
80
+ - **A scene = a group, a memory = a `.md` file**: `memories/<scene>/<name>.md`, the whole body is injected; file names may be non-ASCII.
81
+ - **"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). Both the UI toggle and `scene_manager_switch` move the two axes together.
82
+ - **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.
83
+ - **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.
84
+ - **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.
85
+ - **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.
86
+ - **Injection budget**: 128 KiB by default; oversized memories are skipped with a list. Deletes go to the recycle bin.
87
+ - **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`).
100
88
 
101
89
  ### Subagents
102
90
 
103
- - **One file per persona**: `agents/<persona>.md`, frontmatter entirely optional.
104
- - **A persona reaches the subagent's system prompt inside a role frame**: the body is used verbatim, wrapped in a `# Persona: <name>` heading, one authorizing line ("specified by the caller; where it conflicts with your default inclinations, it wins; the task states what to achieve, and this persona governs how") and one boundary line (it governs *how* you work, not *what* you may do), with the body fenced under `## Persona`. The frame follows the body's language. This makes the model read it as "I have been assigned a role" rather than as two stray sentences following the identity line — **the persona file itself needs no change**. The frame does not include `description`: that field exists so the *caller* can pick a persona; an introduction meant for the child belongs in the body.
105
- - **Tool limits per Agent preset**: each preset gets its own allow/deny list (mutually exclusive), effective at runtime by the current preset — fixes the old "union of all presets" list that broke subagents after a preset switch. Two behaviours to know: **① an allow list is merged back with every currently running `mcp__*` tool** (the official `allow` means "cut everything outside the list", and not merging would cut MCP too), so ticking only `read` will not stop an MCP server that exposes a shell; **② when none of the listed names exists right now, the result is "no restriction"** (fail-open, not fail-closed) — on minimal presets an allow list easily ends up empty. The opposite failure is naming the reserved tool `run_code`: the official `tools.restrict()` throws outright (the subagent never starts), so the plugin strips it from the list and says so in the result.
106
- - **`output:` hard output contract**: frontmatter may carry multiple `output:` lines (**one requirement per line**); the role frame renders them as a separate `## Output requirements (hard)` section after the persona definition. Use the prompt for how the role thinks (prose) and `output:` for what the deliverable must look like (format, severities, required markers, bans) — a persona with only an abstract requirement produces output nobody can check, and only checkable requirements actually get followed. The editor has an "Output requirements (hard)" box, and you can write the lines into the file directly.
107
- - **Run and discard**: `subagent_manager_run` runs with the persona, returns only the result, never enters History. A child's context is **persona + task** only: memories are not injected (the child can read them with `memory_manager_*`, and relevant facts belong in the `task`). Scenes can bind which personas are available.
108
- - **Two context modes**: by default the child starts fresh (it cannot see this conversation, so the task must be complete); with `inherit` the child is seeded with this conversation's **finished** turns (the same mechanism as the host's `subagent_fork`) and the task only states what is new. Only finished turns are inherited — a delegation made during the current turn cannot pass that turn's content (measured: a parent that delegated while still reading left the child to start from scratch), so treat `task` as self-contained in that case. The boundary against the host's two delegation tools (`subagent` / `subagent_fork`) is written into the context by this plugin: **work that matches a persona goes here**, and the host tools are for when no persona fits or a background job is needed.
109
- - **On/off toggles**: a disabled persona is not injected and invisible to the model (file untouched); newly created / imported / restored personas start **disabled** (since v0.8.5, the same rule as skills / MCP) — switch them on before delegating. Entering a scene applies the profile's persona list exactly (checked on, everything else off; no section = all off) and exit restores the pre-scene switches. Inside a scene these toggles still work and are synced into the scene profile (only locking freezes them); leaving the scene restores the pre-scene state.
110
- - **Persona catalog enters the system prompt**: names + descriptions only, so the model knows what it can delegate to; personas are renameable, scene bindings follow.
111
- - **Catalog injection (`catalogDepth`)**: which sessions the standing persona catalog is injected into — default `1` = top level only; `2` also reaches subagent sessions; `3` goes two levels down; the UI's “No nesting limit” writes `99`, which reaches every session depth. **It does not limit nesting**: subagents can always delegate further, which the host decides (`dsh-tool-subagent` defaults to `maxDepth: 3`), and this plugin no longer passes `maxDepth` at all. When the standing catalog is not injected, `subagent_manager_list` still returns every persona. One rule drives all three places (catalog filter, domain declaration, live panel), so the UI and the actual injection cannot disagree.
91
+ - **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.
92
+ - **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.
93
+ - **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.
94
+ - **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.
95
+ - **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`.
96
+ - **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.
97
+ - **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.
112
98
 
113
99
  ### MCP servers
114
100
 
115
- - **CRUD + immediate effect**: writes to `cordis.patch.yml`, HMR picks it up.
116
- - **Per-tool switches**: disable individual tools (invisible to the model, blocked at call), whole-server batch.
117
- - **Stopped servers still show their tools**: a server that is not running keeps the tool names and descriptions last seen (marked "as of last run"); one that has never run can be probed with "start server to read tools".
118
- - **Secret masking**: a credential is replaced **as a whole** by `••••••`, and only for keys that look sensitive (token / secret / password / auth / api key and friends); `$VAR` and `!!js` are indirections, so they stay readable, and a URL's query string becomes `?<redacted>`. "Reveal" needs a token.
119
- - **A masked value is never written back into the patch file**: the edit dialog prefills secret fields with the masked text, so on save the plugin restores the value found in the patch file (= that field was left alone). When there is no original value, or the stored one is itself masked (meaning the real value was destroyed earlier), the key is skipped and the panel says so — in the latter case you have to fill it in again. Secrets already stored as a mask are reported as soon as you open the page. **A URL gets the same protection in a different shape**: its query string is masked as `?<redacted>` and is likewise restored from the stored value on save; when the patch holds nothing to restore, **the whole save is refused** with a prompt to retype the full URL — dropping the query silently would leave an address that connects but fails authentication, which is harder to notice than an error.
120
- - **Migrate & back up**: cross-project/global migration rolls back on failure; JSON export/import.
121
- - **Status & notes enter the system prompt**: currently usable servers are listed; **one that has connected before but cannot right now is also listed and labelled** (`(当前未连上;上次连上时 N 个工具)`) — so the model says "it is not connected, check it" instead of reading "configured but unreachable" as "not configured here" and suggesting you install one. Servers that have never connected are omitted. Your notes travel along as decision hints; levels are "global / app", new servers default to global. Note: a persona-complete preset such as minimal suppresses that section — there the model reads server names, enablement, tool counts and notes with `mcp_manager_list`.
101
+ - **CRUD with immediate effect**: written into `cordis.patch.yml`, backed up before every change. Levels are global / project, and new servers default to global.
102
+ - **Per-tool switches**: disable individual tools (invisible to the model and blocked at call); whole servers support bulk.
103
+ - **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".
104
+ - **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.
105
+ - **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 becomes `?<redacted>`. "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).
106
+ - **Migrate & back up**: cross-level migration rolls back on failure; JSON export/import.
122
107
 
123
108
  ### Skills
124
109
 
125
- - **Sources at a glance**: project / DSH / Agents / Codex / Claude / custom dirs, grouped by source.
126
- - **Opposite permissions**: default sources must be read but skills can be deleted; external dirs can be disabled/removed but skills are read-only.
127
- - **Remove ≠ disable**: remove = directory not scanned at all (files untouched, restorable); disable = still listed but not callable.
128
- - **Same-name skills: see which copy is in effect**: the winning copy is marked "preferred", shadowed ones name the source that wins, and enabling a shadowed copy says so instead of pretending it worked.
129
- - **Custom dirs / ZIP import & export / recycle bin**.
130
- - **Directories can be injected**: when a preset mounts no official skills-directory line (e.g. Minimal), this plugin's injection domain delivers them as a fallback, governed by the switches on the Host page (name + blurb; the body is still read from disk).
110
+ - **Sources at a glance**: project / DSH / Agents / Codex / Claude / custom directories, grouped by source.
111
+ - **Opposite permissions**: default sources are readable and their skills deletable; external directories can be disabled or removed but their files stay read-only.
112
+ - **Remove ≠ disable**: removing stops scanning the directory at all (nothing is written, restorable); disabling keeps it listed but uncallable.
113
+ - **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.
114
+ - **Custom directories / ZIP import & export / recycle bin.**
131
115
 
132
116
  ### Prompt presets
133
117
 
134
- - Multiple `~/.dsh/AGENTS.md` baselines, one-click apply (the host re-reads that file every turn, so it takes effect on the next turn), 5-gen backup.
135
- - **Remembers the preset applied last**: even after you hand-edit `AGENTS.md`, the model can still answer "which preset is this from" (flagged "changed since").
136
- - **Description**: one line saying what a preset is for — shown in this panel only. It lives in a sibling `meta.json`, never in AGENTS.md, so it is never injected into prompts.
137
- - Create with body inline, edit can change id (= dir rename, scene bindings follow). A referenced preset cannot be deleted (scene binding / current contents of `AGENTS.md` / the one to restore when leaving the scene); deletes go to the recycle bin.
138
- - **The body can be injected**: when a preset mounts no official AGENTS.md line (e.g. Minimal), this plugin's injection domain delivers the body of `~/.dsh/AGENTS.md` as a fallback (64 KiB cap; can be turned off on the Host page).
139
- - **While a scene drives the baseline, Apply rebinds the scene to that preset** (bound by a scene, currently in AGENTS.md, or the baseline to restore on scene exit); deletes go to recycle bin.
140
- - **While a scene drives the baseline, Apply rebinds the scene to that preset** (written into the scene profile, and the binding realigns immediately; allowed while unlocked — v0.9 reversed the old "other presets are refused" behavior); **only locking refuses**. The global baseline is restored from the snapshot on scene exit.
118
+ - 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.
119
+ - **It remembers the last applied preset**: even after you hand-edit `AGENTS.md`, the origin is still reported (flagged "the file changed since").
120
+ - Each preset can carry a one-line description that lives in a sibling `meta.json` — shown in this panel, never injected.
121
+ - **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.
122
+ - **While a scene drives the baseline, Apply 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.
141
123
 
142
124
  ### Archived sessions
143
125
 
144
- - Grouped by project, search, batch restore / delete, retention auto-cleanup.
145
- - Workspace registration deleted → group rebuilt from session dirs, one-click re-register.
146
- - Import Claude Code / Cursor / Codex / any text; export Markdown / JSONL. **An export is a readable transcript, not a full backup**: only user/assistant text blocks survive — tool calls, images, reasoning and token stats are dropped, and a `## User` line inside a Markdown body will be read as a turn boundary on re-import (the exporter does not escape it). Use archiving if you need the full record.
126
+ - 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.
127
+ - Import Claude Code / Cursor / Codex / any text; export Markdown / JSONL. **An export is a readable transcript, not a full backup**: only user/assistant text blocks survive — tool calls, images, reasoning and token stats are dropped. Use archiving when you need the full record.
147
128
 
148
129
  ### Host compatibility
149
130
 
150
- The plugin uses the host's own `@deepseek-ai/*` libraries at runtime — they must be the same physical modules, or every "adapt to host" decision degrades into guesswork.
131
+ 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.
151
132
 
152
- - **Host tab**: host version, usable capability count, per-action routing (native/adapter/unavailable), degradations & reasons. The check-up itself is read-only, but the page has three explicit write entries: **access token** (writes the profile's `cordis.patch.yml`, takes effect after restart), **injection settings** (writes `inject-settings.json`, takes effect immediately) and **model tool table** (writes `tool-table.json`, takes effect immediately).
153
- - **Command line**: `node scripts/doctor.mjs` (check), `node scripts/host-deps.mjs --fix` (align deps), `npm run sync:profile` (mirror the build into the profile's local install — a `file:` install is a hard-linked copy, so files ADDED by a build never show up there on their own).
154
- - Under a **suppressing preset** such as `minimal` (persona `complete` / runtime context off) this plugin's injection is **off by default** (following the preset's intent), and the prompt and the skills are missing because their official rows are not mounted — the Host tab marks this per column, and its "Injection" block can force any domain back on.
155
- - **Unchecking "Skills" or "Prompt" really stops them**: under standard presets the host delivers those two itself (the plugin steps aside), so unchecking now also stops the host's copy — the `skill-catalog` / `agent-instructions` messages are no longer let through on that step. The other three domains (memory / MCP / subagents) are only ever sent by this plugin, so their toggles were already complete.
156
- - **What an injection looks like**: one `<system-reminder>` per domain — a `##` title, a **bold one-line action** (the decision point at which to recall it), the tool name / trigger, then the body; any `</system-reminder>` inside the body is escaped (your own text cannot close the frame early). Each message ends with "this copy replaces the earlier one in this session" — injections are only re-sent when the content changes, so the older copy is still in context and the model must know which to trust. The wording follows Claude Code's and Codex CLI's official injections item by item.
133
+ - **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**.
134
+ - **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).
135
+ - 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.
136
+ - **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.
137
+ - **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.
157
138
 
158
139
  ---
159
140
 
160
141
  ## Where data lives
161
142
 
162
- | Content | Location |
163
- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
164
- | MCP definitions | `~/.dsh/cordis.patch.yml` (written by the plugin; pre-write copies land in the hub's `backups/`) |
165
- | Skill policy / custom dirs | `~/.dsh/tool-management/skills-state.json` |
166
- | Skills / memories / personas / presets | `~/.dsh/tool-management/{skills,memories,subagents,prompts}/` |
167
- | Subagent toggles | `~/.dsh/tool-management/subagents-index.json` |
168
- | Recycle bin (skills/personas/presets/scenes) | `~/.dsh/tool-management/trash/{skills,subagents,prompts,scenes}-trash/` |
169
- | Memory recycle bin | `~/.dsh/tool-management/memories-trash/` (**a directory at the hub root, not under `trash/`**) |
170
- | Archive ledger / retention | `~/.dsh/tool-management/history-*.json` |
171
- | Memory index / scenes / profiles | `~/.dsh/tool-management/memories-index.json` |
172
- | MCP sidecars (disabled tools / known tools / notes / settings) | `~/.dsh/tool-management/mcp-*.json` |
173
- | Injection settings (five domain switches / suppressing-preset policy) | `~/.dsh/tool-management/inject-settings.json` |
174
- | Model tool table (tools not sent to the model) | `~/.dsh/tool-management/tool-table.json` |
175
- | Scenes-page preference (preview card before entering a scene) | `~/.dsh/tool-management/scene-settings.json` |
176
- | Runtime log / patch backups | `~/.dsh/tool-management/tool-management.log` · `backups/` |
177
-
178
- **No user data is stored inside the plugin's install directory** (`dsh plugin update` replaces it wholesale).
179
-
180
- **`backups/` keeps 5 copies per patch file**: backup names carry a layer tag (`.global` / `.profile-<name>`), so the global patch and each profile patch keep 5 each — not 5 in total. A single bulk action (e.g. `mcpm-set-all`, or the several writes of entering a scene) can consume all 5 slots of one layer and push out earlier versions.
143
+ | Content | Location |
144
+ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
145
+ | MCP definitions | `~/.dsh/cordis.patch.yml` (written by the plugin; pre-write copies land in the hub's `backups/`) |
146
+ | Skill policy / custom dirs | `~/.dsh/tool-management/skills-state.json` |
147
+ | Skills / memories / personas / presets | `~/.dsh/tool-management/{skills,memories,subagents,prompts}/` |
148
+ | Subagent toggles | `~/.dsh/tool-management/subagents-index.json` |
149
+ | Recycle bin (skills / personas / presets / scenes) | `~/.dsh/tool-management/trash/{skills,subagents,prompts,scenes}-trash/` |
150
+ | Memory recycle bin | `~/.dsh/tool-management/memories-trash/` (a directory at the hub root, not under `trash/`) |
151
+ | Archive ledger / retention | `~/.dsh/tool-management/history-*.json` |
152
+ | Memory index / scenes / profiles | `~/.dsh/tool-management/memories-index.json` |
153
+ | MCP sidecars (disabled tools / known tools / notes / settings) | `~/.dsh/tool-management/mcp-*.json` |
154
+ | Injection settings (six domain switches) | `~/.dsh/tool-management/inject-settings.json` |
155
+ | Model tool table (switched-off tools + saved sets) | `~/.dsh/tool-management/tool-table.json` |
156
+ | Scene page preference (preview card before entering a scene) | `~/.dsh/tool-management/scene-settings.json` |
157
+ | Runtime log / patch backups | `~/.dsh/tool-management/tool-management.log` · `backups/` |
158
+
159
+ **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.
181
160
 
182
161
  ## Configuration & security
183
162
 
184
- | Field | Description |
185
- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
186
- | `token` | Access token. When set, **all writes + plaintext secrets** require `x-dsh-token`; **unset = plaintext endpoints closed**. Also the escape hatch for curl / LAN. If you would rather not keep the token in the config file, set the `DSH_PLUGIN_TOOL_MANAGEMENT_TOKEN` environment variable instead (when both are present `config.token` wins; `tokenDisabled: true` disables both). |
187
- | `tokenDisabled` | `true` = the token **stays in the config** but is not in effect (this is the line written by "Turn protection off"). Writes no longer require a token; plain-text reveal still does (the plain text *is* the credential). Switch it back on any time with "Turn protection on" — no need to retype it. |
188
- | `maxBodyBytes` | Request body cap, default 88 MiB. |
189
-
190
- - **Browser**: reads/writes via cookie, no token needed; but **plaintext secrets** (Reveal / export) need a token. **The token is managed under Tools → Compatibility → Access token** — a fixed entry point independent of the page. That block is one status pill + one plain sentence + three rows: the row label names the scope, the button names the verb, and the pill only answers "is protection on, is this run unlocked".
191
- - **This run**: fill once, valid until DSH exits (stored in this browser only). Once accepted, the row turns into a green **Unlocked** chip plus Clear, right where the input was (it used to collapse to a lone Clear button, which read as "what I just typed is gone"); only then does the host accept this token and the two rows below become usable. The row is not shown at all while the host has no token (there is nothing to check it against).
192
- - **Host config**: written into the profile's `cordis.patch.yml` (only those two lines change — comments and the `!!js` guard stay intact, and the previous file is backed up first); takes effect after a DSH restart. With no host token this row's button is the primary one ("Set token") and asks for the new token **twice** (a typo here means even you cannot match it). Once one is set, "Change token" opens a form with an extra **current token** field — changing it requires proving you know the current one, and being unlocked does **not** count (same rule as turning protection off): you must retype it in the form. The cursor lands in that field when the form opens; the three fields are **stacked** with Save/Clear below them (while the form is open it is the only row shown, and Enter submits), "Save new token" stays disabled until all three are filled, and a rejected token marks that field red.
193
- - **Protection switch**: clicking "Turn protection off" opens an in-place confirm box (the only row shown while it is open — same rule as "Change token") that asks you to **type the current token again** (yes, even while unlocked — the already-verified credential in the request header does *not* count for turning protection off; otherwise one click right after unlocking would drop the guard). On confirm it only writes `tokenDisabled: true`, so **the token itself stays in the config** and "Turn protection on" brings it back without retyping. While off, writes no longer require a token; **plain-text reveal still requires it** (the plain text *is* the credential). Turning it on keeps the old rule: being unlocked, or having the token in the input box, is enough — otherwise anyone who can open the GUI could switch the protection off.
194
- - **Delete token** (same row as "Change token"): removes both the `token` and `tokenDisabled` lines from the config, going back to the **never-set-up** state — writes need no token and plain-text secrets become unreadable again. It also opens an in-place confirm box asking for the current token, and the result honestly notes that "old backups still hold plaintext copies" (each backup is a full copy of the config; use "Clean old backups" in the page header to clear those too). Use the protection switch instead when you only want the gate off for now and want to keep the token.
195
- - **Status pill**: No token yet / Protection off / Protection on · locked / Protection on · unlocked. When the config has been changed but DSH has not restarted, an amber banner below says so (the pill reflects the running process).
196
- - **No "fill in the token" notice while the token is not in effect**: that notice stays only while something is actually gated (a token is in effect on the host). With the token off or unset it is dropped, and the panel's status pill tells you what is going on instead.
197
- - **Where the notice appears**: the "missing or wrong access token" sentence (with its "Fill in token" button) exists **exactly once** — inside the dialog while one is open (that is what blocks your action, so it belongs in plain sight), just below the tabs otherwise. It does not depend on which page you are on or how that page renders errors, so no entry point (dialog submits, imports, exports, bulk actions) can lose it.
163
+ | Field | What it does |
164
+ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
165
+ | `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. |
166
+ | `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. |
167
+ | `maxBodyBytes` | Request body cap, 88 MiB by default. |
168
+
169
+ **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.
170
+
171
+ - **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.
172
+ - **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.
198
173
  - **curl / scripts**: send `x-dsh-token`, or carry the browser cookie.
199
- - **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.
200
- - **The fence's degraded mode**: when the host has no `connection` service, the fence falls back to "loopback Host + same-origin" (there is no browser cookie to check), so any local process can call write ops by faking `Host: localhost`. If you forward the port to a LAN or the public internet, **configure a token** — it is the last door on that degraded path.
201
- - **`dir-list`'s exposure**: the "pick a folder" dialog walks directories through it, so any caller past the fence/token can list directories at **any absolute path** (read-only). That is by design, not a hole — but do not expose the port to an untrusted network.
202
- - **Plaintext on disk (read this)**: masking is a **display-only** measure. MCP `env` / `headers` and the plugin's own `config.token` stay in cleartext inside `~/.dsh/cordis.patch.yml` (and its per-profile copy), and every config change copies the **whole file** into `~/.dsh/tool-management/backups/` (5 copies kept **per patch file**, unencrypted, never rotated, never reclaimed on uninstall) — one secret can end up 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*. Any local process with read access to that directory can take every credential at once. **To clean up: Settings → Tools → Compatibility → "Clean old backups"** (next to Refresh — the button itself shows the current count) — pick how many to delete **per layer** (N deletes that layer's N oldest; the tiers run up to that layer's current count, default 2) — each layer has its own group, so any total is reachable, odd ones included; each row states what would be left, and the dialog asks you to confirm once more, stating the total number of files in that confirmation. Only backup files are touched, never the live config. You can also delete old files under `~/.dsh/tool-management/backups/` by hand.
203
- - **Port forwarded to public**: configure a token — prevents strangers injecting MCP commands (≈ remote code execution) and stealing secrets.
174
+ - **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.
175
+ - **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.
204
176
 
205
177
  ## FAQ
206
178
 
207
- | Symptom | Fix |
208
- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
209
- | Pages missing after install | Hard refresh; restart DSH if that fails. |
210
- | Duplicate MCP tabs | Remove the stale loader row from `cordis.patch.yml`, restart. |
211
- | Broken config, DSH won't boot | Restore the newest `.bak-<timestamp>`. |
212
- | Action stopped after DSH upgrade | Settings → Tools → **Host** for the reason; `doctor.mjs` → `host-deps.mjs --fix`. |
213
- | Still asked to confirm in `approval=never`? | No card appears — straight through with a log line; switch back to "workspace write" to get asked again. |
214
- | `subagent_manager_run` reports provider unavailable | The provider isn't registered: `spawn` (default) / `fork` (`inherit`) come from `@deepseek-ai/dsh-subagent-spawn-in-process` / `-fork-in-process` — mount and restart. |
215
- | Scene binds persona A, but 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), and `inherit` now matches fork's context inheritance. |
179
+ | Symptom | Fix |
180
+ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
+ | Pages missing after install | Hard refresh; restart DSH if that fails. |
182
+ | Duplicate MCP tabs | Remove the stale loader row from `cordis.patch.yml`, restart. |
183
+ | Broken config, DSH won't boot | Restore the newest `cordis.patch.yml.<level>.bak-<timestamp>` from `~/.dsh/tool-management/backups/` (5 per patch file). |
184
+ | Action stopped working after a DSH upgrade | Settings → Tools → **Host** for the reason; `doctor.mjs` → `host-deps.mjs --fix`. |
185
+ | 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. |
186
+ | 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. |
187
+ | `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. |
188
+ | 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. |
216
189
 
217
190
  ---
218
191
 
@@ -221,14 +194,14 @@ The plugin uses the host's own `@deepseek-ai/*` libraries at runtime — they mu
221
194
  ```bash
222
195
  npm install
223
196
  npm run build # tsc + sync client
224
- npm test # build + i18n + contract tests (pure-function invariants & host contracts; UI tests removed 2026-09-19)
197
+ npm test # build + i18n + contract tests (pure-function invariants & host contracts)
225
198
  npm run check:i18n # dictionary self-check
226
199
  npm run doctor # host compatibility check
227
200
  ```
228
201
 
229
202
  `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.
230
203
 
231
- > **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, it never blocks writes. Regular `npm install` deployments are unaffected (dependencies are installed with the package).
204
+ > **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.
232
205
 
233
206
  ## License
234
207
 
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