@misaka-net/misakanet-setup 0.8.1 → 0.9.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.
package/README.md CHANGED
@@ -1,9 +1,14 @@
1
1
  # @misaka-net/misakanet-setup
2
2
 
3
3
  One command to teach your **Claude Code**, **Codex**, **Hermes**, **OpenClaw**, **codewhale**,
4
- **Cursor**, **Gemini CLI**, **Copilot CLI**, **OpenCode**, **Kiro** or **MiniMax Code** to
5
- check MisakaNet's failure lessons before repeating a mistake — and to write up the reusable lessons
6
- this session produced, at a checkpoint.
4
+ **Cursor**, **Gemini CLI**, **Copilot CLI**, **OpenCode**, **Kiro**, **MiniMax Code** or
5
+ **DeepSeek Harness** to check MisakaNet's failure lessons before repeating a mistake — and to write
6
+ up the reusable lessons this session produced, at a checkpoint.
7
+
8
+ For **DeepSeek Harness (dsh)** that command writes the **rules half only** (`$DSH_HOME/AGENTS.md`)
9
+ and prints the one command its tools need (`dsh plugin --profile web add misakanet`); everything
10
+ else in this README applies to all twelve exactly as written. The split, and why dsh is the one
11
+ target shaped that way, is in [dsh: the rules half, written](#dsh-the-rules-half-written--the-plugin-half-printed).
7
12
 
8
13
  **What it writes:** an MCP endpoint into each agent config, rules blocks for the clients that take
9
14
  one, and a local hook for Claude Code. **What leaves this machine:** at most one anonymous request
@@ -47,60 +52,66 @@ look installed and register nothing. Its manifest also comes with the rules bloc
47
52
  (`NODE_USE_ENV_PROXY=1`) are in `docs/integrations/mcode.md`.
48
53
 
49
54
  All of them are config files written file-to-file — no subprocess is spawned, so **no token ever appears in a command line**. For Claude Code only, the five **read-only** tools are also added to `permissions.allow` — "can call it" otherwise still means a permission prompt on the very first search, so `0.5.3` pre-allows them (`--verify` reports it, `--report` prints `permissions:`); `write_lesson` is deliberately **not** pre-allowed, because a tool that writes should ask |
50
- | 2 | the agent **knows when** | rules block appended to `~/.claude/CLAUDE.md` / `~/.codex/AGENTS.md` / `~/.hermes/SOUL.md` / `~/.openclaw/workspace/AGENTS.md` / `~/.minimax/AGENTS.md` (mcode's global scope). **The five JSON-config clients are the exception: they get none**, because their rules are project-scoped (`.cursor/rules/*.mdc`, `GEMINI.md`, `.github/copilot-instructions.md`, `AGENTS.md`, `.kiro/steering/*.md`) and the installer cannot know where your projects live — the install output says so, with the project-side action for each client, instead of implying behaviour changed (issue, retry, risky-operation triggers; desensitisation rules). Wording is **imperative first** ("必须先调 misakanet_search") rather than descriptive, because a field test showed a weaker model reads a descriptive block as background and never calls the tool |
55
+ | 2 | the agent **knows when** | rules block appended to `~/.claude/CLAUDE.md` / `~/.codex/AGENTS.md` / `~/.hermes/SOUL.md` / `~/.openclaw/workspace/AGENTS.md` / `~/.minimax/AGENTS.md` (mcode's global scope) / `$DSH_HOME/AGENTS.md` (dsh, default `~/.dsh`). **The five JSON-config clients are the exception: they get none**, because their rules are project-scoped (`.cursor/rules/*.mdc`, `GEMINI.md`, `.github/copilot-instructions.md`, `AGENTS.md`, `.kiro/steering/*.md`) and the installer cannot know where your projects live — the install output says so, with the project-side action for each client, instead of implying behaviour changed (issue, retry, risky-operation triggers; desensitisation rules). Wording is **imperative first** ("必须先调 misakanet_search") rather than descriptive, because a field test showed a weaker model reads a descriptive block as background and never calls the tool |
51
56
  | 4 | *(opt-in)* you **hear** it | `--voice` adds a `PostToolUse` hook that plays the cue the server asked for (`lesson-found` / `failure-warning` / `connect-success` / `pair-success`). Off by default; mute with `MISAKANET_VOICE=0`; see `docs/integrations/mcp-voice-hooks.md` |
52
57
  | 3 | the checkpoint **fires** | a hook that counts user turns: turn 1 announces the install to the user, turn 20 (and every 10 after) injects the "write up the reusable lessons and submit them" reminder — the agent does that writing and that submitting, by hand, and the hook itself sends nothing by default; a failed tool call injects a "search before you retry" reminder built from the error text, and only with `MISAKANET_HOOK_FETCH=1` (off by default) does the hook itself send that error text as a search query. Claude Code only today — Codex's user-level hook shape is unconfirmed and OpenClaw's events are unverified, so those two work from the rules block, and `--verify` says so per target instead of implying otherwise |
53
58
 
54
59
  Without (3) a rule saying "summarise every 20 turns" never fires — agents do not keep
55
60
  counters. Without (1)/(2) the hook has nothing to call.
56
61
 
57
- ### dsh is not a file target — it is a plugin
62
+ ### dsh: the rules half, written — the plugin half, printed
58
63
 
59
64
  dsh takes its MCP servers from a **plugin bundle**, not from a JSON file. Its instruction files are a
60
65
  chain: **`$DSH_HOME/AGENTS.md`** (user-global, default `~/.dsh/AGENTS.md`, applies to every session)
61
66
  first, then the project's `AGENTS.md`/`CLAUDE.md` from the project root down to the working directory
62
67
  (`@deepseek-ai/dsh-agent-instructions`, byte-budgeted at 65,536 by default). It has no Claude-Code-style
63
- hooks.
68
+ hooks — and no plugin extension point that could inject a rule, which is why the rules are a **file**
69
+ and the tools are a **plugin**, and why the two cannot be one write.
64
70
 
65
- So dsh takes **two manual steps that do not know about each other**, and this installer does neither of
66
- them — it writes no file under `~/.dsh`:
71
+ So dsh is **one file target and one printed step**. The installer writes the half a file can hold and
72
+ names the other half instead of doing neither:
67
73
 
68
74
  ```bash
69
- dsh plugin --profile web add misakanet # ① the tools (mcp__misakanet__*) — dsh's own command
70
- # ② you paste the rules block into ~/.dsh/AGENTS.md (global) or the project's AGENTS.md
75
+ npx @misaka-net/misakanet-setup --only dsh # ① the rules block → $DSH_HOME/AGENTS.md
76
+ dsh plugin --profile web add misakanet # ② the tools (mcp__misakanet__*) — dsh's own command
71
77
  ```
72
78
 
79
+ Order does not matter; skipping ② leaves rules without tools, and the run says so with the exact
80
+ command (`! dsh:工具在插件包里,安装器不写这个文件 → 跑 \`dsh plugin --profile web add misakanet\``).
81
+ `--list-writes --only dsh` names the file and its tier (`tier2 ② 写规则块 … ~/.dsh/AGENTS.md`), and
82
+ `--mcp-only` skips it, like every other rules block.
83
+
73
84
  Measured 2026-10-17: writing `~/.dsh/AGENTS.md` is picked up as `user-global` instructions on the next
74
- message, with no restart.
85
+ message, with no restart. Measured 2026-10-17: installing the plugin takes ~1s into
86
+ `~/.dsh/profiles/web`, and neither `index.js` (the MCP client mount) nor `lib/client.js` (the web
87
+ panel) injects a rule.
75
88
 
76
- Measured 2026-10-17: installing the current `misakanet` takes ~1s into `~/.dsh/profiles/web`; the plugin's
77
- `index.js` mounts the MCP client and `lib/client.js` is the web panel — **neither injects a rule**,
78
- which is why ② is a separate, manual step.
89
+ Measured 2026-10-30, on a machine whose block had been pasted by hand: the unmarked text was adopted
90
+ into a marked block (`<!-- misakanet:start -->` … `<!-- misakanet:end -->`, header preserved,
91
+ 1,474 bytes) and the next message reloaded it — so an older hand-pasted copy is upgraded in place
92
+ rather than duplicated.
79
93
 
80
- **Upgrading the installer does not refresh that block.** Nothing rewrites text you pasted, and the
81
- installer only refreshes the files it manages (for Claude Code, `~/.claude/CLAUDE.md`). Measured
82
- 2026-10-23 (issue #3773): one machine's `~/.dsh/AGENTS.md` still carried the old block — its last line
83
- read `脱敏:密钥/凭据→<REDACTED>…` — while the same installer had just written the current one
84
- (`脱敏:已由代码自动完成…`) to `~/.claude/CLAUDE.md`. Re-paste after an upgrade, and check by comparing
85
- that line; the copy in `docs/integrations/dsh.md` can lag too.
94
+ **A sandbox home stays a sandbox.** `DSH_HOME` is a user setting, so it is honoured **only when the
95
+ installer is addressing the real home**; with `--home <dir>` every write stays under `<dir>`. That is
96
+ not a footnote: before 2026-10-30 the package's own test run wrote the *real* `~/.dsh/AGENTS.md`
97
+ (truncated it to 35 bytes) because the suite inherits `DSH_HOME` from the shell.
86
98
 
87
- **Uninstalling takes two steps, and the second one is yours.** `dsh plugin --profile web remove misakanet`
88
- removes the tools; the rules block stays in `~/.dsh/AGENTS.md` (or the project's `AGENTS.md`), and there is
89
- no `misakanet:start` / `misakanet:end` marker to lift it — the installer never put one there, because it
90
- never wrote that file. Remove the block by hand, then check:
99
+ **Uninstalling is two steps, and neither takes the other's half:**
91
100
 
92
101
  ```bash
93
- grep -c misakanet "${DSH_HOME:-$HOME/.dsh}/AGENTS.md" # expected 0; a missing file errors, which is also clean
102
+ npx @misaka-net/misakanet-setup --uninstall # ① strips the marked block
103
+ dsh plugin --profile web remove misakanet # ② removes the tools
94
104
  ```
95
105
 
96
- Until the block is removed, a new dsh session still follows it and calls `mcp__misakanet__*` tools that
97
- no longer exist.
106
+ Doing ① alone leaves a plugin nobody's rules mention; doing ② alone leaves a session following rules
107
+ that call tools which no longer exist. `--uninstall` keeps `AGENTS.md.misakanet.bak` next to the file
108
+ it emptied, and deletes the file outright when it held nothing but the block.
98
109
 
99
- **Why it is not mechanised yet.** dsh's MCP servers come from the plugin bundle and its instructions come
100
- from the `AGENTS.md` chain — two entry points this installer has no path into: it only writes the files on
101
- its own target list, and dsh's files are not on it. A mechanical fix (a marker pair around the block, plus
102
- an uninstall branch) **is not implemented; it is follow-up work**. Until then,
103
- `dsh plugin --profile web remove misakanet` does not take the block with it.
110
+ **Upgrades now refresh the block**, which is the one thing the old hand-pasted flow could not do
111
+ (measured 2026-10-23, issue #3773: a machine's block was still the old `脱敏:密钥/凭据→<REDACTED>…`
112
+ wording while the same installer had just written the current one to `~/.claude/CLAUDE.md`). Re-running
113
+ the installer updates it in place; the copy in `docs/integrations/dsh.md` can still lag, and the
114
+ installer's output is the criterion.
104
115
 
105
116
  Full guide: `docs/integrations/dsh.md`.
106
117
 
@@ -132,7 +132,7 @@ const SILENT = has('--silent');
132
132
  const REPORT_JSON = has('--report-json');
133
133
  const HOME = resolve(valueOf('--home', homedir()));
134
134
  const AGENTS = ['claude', 'codex', 'hermes', 'openclaw', 'codewhale',
135
- 'cursor', 'gemini', 'copilot', 'opencode', 'kiro', 'mcode'];
135
+ 'cursor', 'gemini', 'copilot', 'opencode', 'kiro', 'mcode', 'dsh'];
136
136
 
137
137
  /**
138
138
  * The three line lists, and what every line knows about itself.
@@ -197,7 +197,7 @@ function readText(path) {
197
197
  * except the two that name the version (measured 2026-10-22: the 0.6.0 release pull request
198
198
  * failed 9 e2e legs on exactly that).
199
199
  */
200
- const VERSION = '0.8.1' // x-release-please-version
200
+ const VERSION = '0.9.0' // x-release-please-version
201
201
 
202
202
  /**
203
203
  * The shape of a client id, in one place.
@@ -483,6 +483,8 @@ const AGENT_LABELS = {
483
483
  hermes: 'Hermes',
484
484
  openclaw: 'OpenClaw',
485
485
  codewhale: 'Codewhale',
486
+ // dsh is a plugin-bundle client: the installer writes only its rules half (see installDsh).
487
+ dsh: 'dsh',
486
488
  ...Object.fromEntries(Object.entries(MCP_ONLY_TARGETS).map(([id, spec]) => [id, spec.label])),
487
489
  // Not an MCP-only target: it takes a rules block too (see `installMcode`), so its label cannot be
488
490
  // read from that table.
@@ -566,6 +568,9 @@ const AGENT_WRITE_PATHS = {
566
568
  // carries the rules block (②). Listing both here is what makes the preview, `--list-writes` and
567
569
  // the permission pre-check agree with `installMcode`.
568
570
  mcode: (home) => [join(home, MCODE_PLUGIN_REL), join(home, MCODE_RULES_REL)],
571
+ // dsh reads its instructions from $DSH_HOME/AGENTS.md (default ~/.dsh). Its MCP servers
572
+ // come from a plugin bundle this installer cannot write, so one file is the whole manifest.
573
+ dsh: (home) => [join(dshHome(home), 'AGENTS.md')],
569
574
  openclaw: () => openclawWorkspaces().map((workspace) => join(workspace, 'AGENTS.md')),
570
575
  };
571
576
 
@@ -1704,6 +1709,9 @@ function mcpEntryPresent(agent) {
1704
1709
  }
1705
1710
 
1706
1711
  function detect(agent) {
1712
+ // dsh may live outside $HOME (DSH_HOME), so it is answered by its own resolver rather
1713
+ // than by the relative-path table below.
1714
+ if (agent === 'dsh') return existsSync(dshHome());
1707
1715
  const paths = {
1708
1716
  claude: ['.claude.json', '.claude'],
1709
1717
  codex: ['.codex'],
@@ -2332,6 +2340,43 @@ async function installMcpOnly(agent, bearer) {
2332
2340
  * `adoptExistingClientId`, so a re-run without `--client-id` keeps the header the file already
2333
2341
  * declares (#3772).
2334
2342
  */
2343
+ /**
2344
+ * Where dsh keeps its home: `DSH_HOME` wins, `<home>/.dsh` otherwise (#100.4 item 4).
2345
+ *
2346
+ * `DSH_HOME` is a *user* setting, so it may only apply when the install is addressing the real
2347
+ * home. `--home <dir>` means "pretend this directory is the home" — the test suite and the e2e
2348
+ * depend on every write staying inside it. Honouring `DSH_HOME` there wrote the user's real
2349
+ * `~/.dsh/AGENTS.md` from inside a sandbox run (measured 2026-10-30: the suite truncated it to
2350
+ * 35 bytes, and the next message reloaded the broken block).
2351
+ */
2352
+ function dshHome(home = HOME) {
2353
+ const real = homedir();
2354
+ if (process.env.DSH_HOME && resolve(home) === resolve(real)) return process.env.DSH_HOME;
2355
+ return join(home, '.dsh');
2356
+ }
2357
+
2358
+ /**
2359
+ * dsh is a plugin-bundle client, so only half of it is a file target.
2360
+ *
2361
+ * The tools (`mcp__misakanet__*`) come from the `misakanet` plugin bundle, which dsh installs with
2362
+ * its own command — there is no JSON file this installer could write. The rules, however, live in
2363
+ * `$DSH_HOME/AGENTS.md`, and writing them is exactly the manual step the README used to ask for.
2364
+ * So the installer writes the half it can and prints the half it cannot, instead of doing neither
2365
+ * and leaving the user with two steps that do not know about each other.
2366
+ */
2367
+ async function installDsh() {
2368
+ const rules = join(dshHome(), 'AGENTS.md');
2369
+ const profile = process.env.DSH_PROFILE || 'web';
2370
+ const manual = `dsh plugin --profile ${profile} add misakanet`;
2371
+ if (!MCP_ONLY) {
2372
+ ok(`dsh:规则块 ${injectBlock(rules, PROMPT_BLOCK)} → ${rules}`);
2373
+ } else {
2374
+ skip('dsh:规则块未写(--mcp-only)');
2375
+ }
2376
+ need(`dsh:工具在插件包里,安装器不写这个文件 → 跑 \`${manual}\``
2377
+ + '(不跑就只有规则、没有 mcp__misakanet__* 工具)');
2378
+ }
2379
+
2335
2380
  async function installMcode(bearer) {
2336
2381
  const plugin = mcodePluginPath(HOME);
2337
2382
  const existing = readText(plugin);
@@ -2826,6 +2871,23 @@ async function verify() {
2826
2871
  }
2827
2872
  }
2828
2873
  }
2874
+ // dsh: one file, and only half of the client — so the report says which half it can see. The
2875
+ // rules block is our footprint and is checked like every other target's; the tools live in the
2876
+ // plugin's profile, which this process cannot read, and a "complete" that quietly skipped that
2877
+ // would be the false green this whole report exists to avoid (measured 2026-10-30: with the
2878
+ // block deleted, `--verify` still said 安装完整性:完整).
2879
+ if (wantClient('dsh') && detect('dsh')) {
2880
+ const rules = join(dshHome(), 'AGENTS.md');
2881
+ if (!readText(rules).includes(`<!-- ${START} -->`)) {
2882
+ needWrite('dsh', rules,
2883
+ `dsh:规则块没装(${rules})→ 重跑安装命令`,
2884
+ `dsh:规则块装不了(${rules})→ ${unwritableAdvice()}`);
2885
+ } else {
2886
+ ok('dsh:规则块已装', 'dsh');
2887
+ skip(`dsh:工具在插件包里,--verify 看不到它 → 跑 \`dsh plugin --profile ${process.env.DSH_PROFILE || 'web'} list\` 复核`,
2888
+ 'dsh');
2889
+ }
2890
+ }
2829
2891
  // Version: what is installed, and whether the registry has moved on. Read-only here — the
2830
2892
  // hook owns the periodic nudge (it cannot be a network call in this process's hot path), and
2831
2893
  // `--verify` is an explicit user action, so one lookup is both cheap and expected.
@@ -3175,6 +3237,7 @@ function mcodePluginDirectoryOwnership() {
3175
3237
 
3176
3238
  function uninstall() {
3177
3239
  let hermesRulesRemoved = false;
3240
+ if (stripBlock(join(dshHome(), 'AGENTS.md'))) ok(`移除规则块 → ${join(dshHome(), 'AGENTS.md')}`);
3178
3241
  for (const rel of ['.claude/CLAUDE.md', '.codex/AGENTS.md', '.hermes/SOUL.md', '.minimax/AGENTS.md']) {
3179
3242
  if (stripBlock(join(HOME, rel))) {
3180
3243
  ok(`移除规则块 → ${rel}`);
@@ -3816,6 +3879,7 @@ try {
3816
3879
  else if (agent === 'openclaw') await installOpenclaw(bearer);
3817
3880
  else if (agent === 'codewhale') await installCodewhale(bearer);
3818
3881
  else if (agent === 'mcode') await installMcode(bearer);
3882
+ else if (agent === 'dsh') await installDsh();
3819
3883
  else if (MCP_ONLY_TARGETS[agent]) await installMcpOnly(agent, bearer);
3820
3884
  installed += 1;
3821
3885
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misaka-net/misakanet-setup",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "MisakaNet failure lessons for your agents: writes an MCP endpoint into each agent config, rules blocks where the client takes one, and a Claude Code hook. At most one anonymous request leaves this machine at install; then only the agent's calls — unless MISAKANET_HOOK_FETCH=1 (off by default) the hook sends the failed call's error text or command.",
5
5
  "bin": {
6
6
  "misakanet-setup": "bin/misakanet-setup.mjs"