kimi-plugin-cc 2.4.5 → 2.5.2

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 (114) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +22 -0
  4. package/README.md +103 -29
  5. package/dist/commands/setup.js +78 -51
  6. package/dist/dsh/companion.js +177 -0
  7. package/dist/dsh/index.js +169 -0
  8. package/dist/hooks/install-paths.js +2 -2
  9. package/dist/hooks/managed-block.js +86 -20
  10. package/dist/version.js +1 -1
  11. package/docs/migration.md +4 -2
  12. package/docs/models.md +5 -1
  13. package/docs/registry-releases.md +84 -20
  14. package/docs/safety.md +25 -10
  15. package/docs/upstream-2.4.1-certification.md +6 -0
  16. package/package.json +75 -8
  17. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  18. package/plugins/kimi-codex/dist/commands/setup.js +78 -51
  19. package/plugins/kimi-codex/dist/dsh/companion.js +177 -0
  20. package/plugins/kimi-codex/dist/dsh/index.js +169 -0
  21. package/plugins/kimi-codex/dist/hooks/install-paths.js +2 -2
  22. package/plugins/kimi-codex/dist/hooks/managed-block.js +86 -20
  23. package/plugins/kimi-codex/dist/version.js +1 -1
  24. package/plugins/kimi-dsh/LICENSE +201 -0
  25. package/plugins/kimi-dsh/README.md +83 -0
  26. package/plugins/kimi-dsh/cordis.patch.yml +45 -0
  27. package/plugins/kimi-dsh/dist/background-spawn.js +216 -0
  28. package/plugins/kimi-dsh/dist/cli-cancellation.js +43 -0
  29. package/plugins/kimi-dsh/dist/cli-client.js +876 -0
  30. package/plugins/kimi-dsh/dist/commands/ask.js +336 -0
  31. package/plugins/kimi-dsh/dist/commands/cancel.js +216 -0
  32. package/plugins/kimi-dsh/dist/commands/cli-helpers.js +201 -0
  33. package/plugins/kimi-dsh/dist/commands/models.js +140 -0
  34. package/plugins/kimi-dsh/dist/commands/not-implemented.js +4 -0
  35. package/plugins/kimi-dsh/dist/commands/pursue.js +307 -0
  36. package/plugins/kimi-dsh/dist/commands/recover-swarm.js +135 -0
  37. package/plugins/kimi-dsh/dist/commands/registry.js +74 -0
  38. package/plugins/kimi-dsh/dist/commands/repair-sessions.js +160 -0
  39. package/plugins/kimi-dsh/dist/commands/replay.js +124 -0
  40. package/plugins/kimi-dsh/dist/commands/rescue.js +347 -0
  41. package/plugins/kimi-dsh/dist/commands/result.js +66 -0
  42. package/plugins/kimi-dsh/dist/commands/review-gate.js +390 -0
  43. package/plugins/kimi-dsh/dist/commands/review.js +220 -0
  44. package/plugins/kimi-dsh/dist/commands/setup.js +1074 -0
  45. package/plugins/kimi-dsh/dist/commands/status.js +27 -0
  46. package/plugins/kimi-dsh/dist/commands/swarm.js +754 -0
  47. package/plugins/kimi-dsh/dist/commands/worker.js +25 -0
  48. package/plugins/kimi-dsh/dist/companion.js +97 -0
  49. package/plugins/kimi-dsh/dist/config.js +22 -0
  50. package/plugins/kimi-dsh/dist/dsh/companion.js +177 -0
  51. package/plugins/kimi-dsh/dist/dsh/index.js +169 -0
  52. package/plugins/kimi-dsh/dist/errors.js +40 -0
  53. package/plugins/kimi-dsh/dist/git.js +224 -0
  54. package/plugins/kimi-dsh/dist/help.js +43 -0
  55. package/plugins/kimi-dsh/dist/hook-escape.js +71 -0
  56. package/plugins/kimi-dsh/dist/hooks/approval-hook.js +101 -0
  57. package/plugins/kimi-dsh/dist/hooks/approval-policy.js +211 -0
  58. package/plugins/kimi-dsh/dist/hooks/config-safety.js +653 -0
  59. package/plugins/kimi-dsh/dist/hooks/install-paths.js +428 -0
  60. package/plugins/kimi-dsh/dist/hooks/install.js +210 -0
  61. package/plugins/kimi-dsh/dist/hooks/managed-block.js +685 -0
  62. package/plugins/kimi-dsh/dist/hooks/review-gate-stop.js +19 -0
  63. package/plugins/kimi-dsh/dist/job-id.js +12 -0
  64. package/plugins/kimi-dsh/dist/job-store.js +659 -0
  65. package/plugins/kimi-dsh/dist/jobs.js +217 -0
  66. package/plugins/kimi-dsh/dist/kimi-command.js +70 -0
  67. package/plugins/kimi-dsh/dist/kimi-engine.js +632 -0
  68. package/plugins/kimi-dsh/dist/kimi-errors.js +260 -0
  69. package/plugins/kimi-dsh/dist/kimi-home.js +9 -0
  70. package/plugins/kimi-dsh/dist/kimi-timeouts.js +52 -0
  71. package/plugins/kimi-dsh/dist/kimi-version-probe.js +216 -0
  72. package/plugins/kimi-dsh/dist/logging.js +22 -0
  73. package/plugins/kimi-dsh/dist/mcp/binding.js +58 -0
  74. package/plugins/kimi-dsh/dist/mcp/job-control.js +345 -0
  75. package/plugins/kimi-dsh/dist/mcp/launcher.js +257 -0
  76. package/plugins/kimi-dsh/dist/mcp/operations.js +301 -0
  77. package/plugins/kimi-dsh/dist/mcp/protocol.js +275 -0
  78. package/plugins/kimi-dsh/dist/mcp/server.js +8 -0
  79. package/plugins/kimi-dsh/dist/mcp/stdio.js +78 -0
  80. package/plugins/kimi-dsh/dist/mcp/supervised-child.js +15 -0
  81. package/plugins/kimi-dsh/dist/mcp/tool-result.js +7 -0
  82. package/plugins/kimi-dsh/dist/mcp/workspace.js +34 -0
  83. package/plugins/kimi-dsh/dist/model-command.js +32 -0
  84. package/plugins/kimi-dsh/dist/native-v2-preflight.js +417 -0
  85. package/plugins/kimi-dsh/dist/package.json +1 -0
  86. package/plugins/kimi-dsh/dist/parsing.js +620 -0
  87. package/plugins/kimi-dsh/dist/paths.js +27 -0
  88. package/plugins/kimi-dsh/dist/plugin-data.js +98 -0
  89. package/plugins/kimi-dsh/dist/process-tree.js +479 -0
  90. package/plugins/kimi-dsh/dist/prompt-size.js +18 -0
  91. package/plugins/kimi-dsh/dist/render.js +177 -0
  92. package/plugins/kimi-dsh/dist/rescue-approval.js +865 -0
  93. package/plugins/kimi-dsh/dist/schemas/review-gate-output.js +43 -0
  94. package/plugins/kimi-dsh/dist/session-recovery.js +200 -0
  95. package/plugins/kimi-dsh/dist/session-title.js +293 -0
  96. package/plugins/kimi-dsh/dist/stream-json.js +346 -0
  97. package/plugins/kimi-dsh/dist/swarm-activation.js +834 -0
  98. package/plugins/kimi-dsh/dist/swarm-recovery.js +134 -0
  99. package/plugins/kimi-dsh/dist/types.js +1 -0
  100. package/plugins/kimi-dsh/dist/vendor/shell-quote/LICENSE +24 -0
  101. package/plugins/kimi-dsh/dist/vendor/shell-quote/index.js +1 -0
  102. package/plugins/kimi-dsh/dist/vendor/shell-quote/parse.js +229 -0
  103. package/plugins/kimi-dsh/dist/vendor/smol-toml/LICENSE +24 -0
  104. package/plugins/kimi-dsh/dist/vendor/smol-toml/date.js +156 -0
  105. package/plugins/kimi-dsh/dist/vendor/smol-toml/error.js +67 -0
  106. package/plugins/kimi-dsh/dist/vendor/smol-toml/extract.js +293 -0
  107. package/plugins/kimi-dsh/dist/vendor/smol-toml/parse.js +164 -0
  108. package/plugins/kimi-dsh/dist/vendor/smol-toml/primitive.js +151 -0
  109. package/plugins/kimi-dsh/dist/vendor/smol-toml/struct.js +156 -0
  110. package/plugins/kimi-dsh/dist/vendor/smol-toml/util.js +57 -0
  111. package/plugins/kimi-dsh/dist/version.js +7 -0
  112. package/plugins/kimi-dsh/package.json +78 -0
  113. package/plugins/kimi-dsh/scripts/companion.sh +37 -0
  114. package/plugins/kimi-dsh/scripts/review-gate-hook.sh +32 -0
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Kimi for Claude Code: code questions, reviews, fixes, parallel tasks, and MCP tasks through the local Kimi Code CLI.",
8
- "version": "2.4.5"
8
+ "version": "2.5.2"
9
9
  },
10
10
  "plugins": [
11
11
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kimi",
3
3
  "description": "Kimi for Claude Code: code questions, independent reviews, bounded fixes, autonomous goals, parallel tasks, and MCP tasks through the local Kimi Code CLI, with PreToolUse safety hooks.",
4
- "version": "2.4.5",
4
+ "version": "2.5.2",
5
5
  "homepage": "https://github.com/linxule/kimi-plugin-cc",
6
6
  "repository": "https://github.com/linxule/kimi-plugin-cc",
7
7
  "license": "Apache-2.0",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  > **Post-1.0 release history (v1.0.1 -> present) lives in [ROADMAP-TO-GA.md § Post-GA audit log](./ROADMAP-TO-GA.md#post-ga-audit-log)** and the "Version" / "Upstream compat" lines of [AGENTS.md](./AGENTS.md). Docs-only kimi-code compat checkups that don't bump the plugin version (e.g. the 0.14.2 / 0.14.3 patches) are recorded there, not here. Notable releases are summarized below; the GA entry and full pre-GA detail follow.
4
4
 
5
+ ## 2.5.2 — 2026-11-02
6
+
7
+ - Preserve TOML multiline values, line endings, and foreign hook fields during setup; repair mixed-host orphan marker comments without altering foreign hooks.
8
+ - Keep the DSH disposal grace at least 6000ms, pin a stable Node path, and expose each Kimi tool's write boundary to the model.
9
+ - Treat DSH services and its tool adapter as host-provided peers so ordinary npm consumers do not install the harness. Require patched DSH host/profile dependencies and audit both installations before the seven-tool composition and Web-boot smoke.
10
+ - Add an installed-consumer security gate to CI and publication; reject failed or incomplete audits as well as high/critical advisories. Upstream DSH pins remain vulnerable, so existing installations require the documented explicit overrides.
11
+ - Validate exact Kimi Code 2.4.1 with 24 real-binary smoke tests (167 assertions, no skips) and the 16-test exact-source audit; certification remains unchanged.
12
+
13
+ ## 2.5.1 — 2026-11-01
14
+
15
+ - Consolidate DSH distribution into the existing `kimi-plugin-cc` npm package and its established OIDC publication path. DSH users install the same root artifact with `dsh plugin --profile <name> add kimi-plugin-cc`; no second npm package or Trusted Publishing binding is required.
16
+ - Declare the root DSH bundle entry, runtime dependencies, and peer compatibility directly in `package.json`, and validate root-tarball installation, seven-tool composition, and Web boot in an isolated DSH profile.
17
+ - Pin transitive `zod` and `undici` versions in the checkout lockfile, remove the unnecessary full DSH CLI development dependency, install pnpm explicitly in CI, and exclude dependency directories from installed-script fixtures. Downstream npm consumers do not inherit those overrides; see the consumer-security release gate.
18
+
19
+ ## 2.5.0 — 2026-11-01
20
+
21
+ - Add DeepSeek Harness as a third host with native `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm`, and `kimi_swarm_write` providers. Reuse the existing companion and `kimi -p` transport, managed hooks, exact-version certification, SQLite jobs, budgets, artifacts, and process lifecycle; no ACP transport.
22
+ - Add DSH-owned setup, model inventory, host-scoped uninstall, and job controls, plus profile-level model, thinking, budget, review-base, data-root, environment, output, and teardown settings. Preserve read-only denials, real-workspace rescue/pursue writes, and patch-only swarm-write.
23
+ - Package `dsh-kimi-plugin-cc` as an independently installable npm bundle with its own pack/install/composition/Web-boot gate. Publish root and DSH tarballs together through the existing OIDC workflow, with Node 24 CI and verified composition on DSH 0.2.1-rc.5 and 0.2.2-alpha.2.
24
+ - Verify live DSH delegation: `kimi_ask` returns `4`, a managed-hook write denial creates no file, rescue writes only `DSH_OK`, and a two-target swarm-write returns a valid patch without applying it or touching the real workspace.
25
+ - Update installation, safety, model, registry, contributor, and multilingual README guidance. Reduce `AGENTS.md` from 32,704 to 21,291 bytes by keeping detailed mechanics in owning references, and enforce a 24 KB ceiling.
26
+
5
27
  ## 2.4.5 — 2026-10-31
6
28
 
7
29
  - Certify exact kimi-code 2.4.1 for all eight native-v2 operations after exact-source review, plan and swarm controls, and candidate/production smoke. See [evidence](docs/upstream-2.4.1-certification.md).
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Kimi for Claude Code and Codex
1
+ # Kimi for Claude Code, Codex, and DeepSeek Harness
2
2
 
3
3
  [![Latest release](https://img.shields.io/github/v/release/linxule/kimi-plugin-cc)](https://github.com/linxule/kimi-plugin-cc/releases/latest) [![CI](https://github.com/linxule/kimi-plugin-cc/actions/workflows/ci.yml/badge.svg)](https://github.com/linxule/kimi-plugin-cc/actions/workflows/ci.yml) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
4
4
 
5
- Use Kimi to explain code, review changes, challenge a design, or work toward a goal — an independent second opinion from a different model family, inside the Claude Code or Codex session you're already in.
5
+ Use Kimi to explain code, review changes, challenge a design, or work toward a goal — an independent second opinion from a different model family, inside the Claude Code, Codex, or DeepSeek Harness session you're already in.
6
6
 
7
7
  [![Kimi Ambassador — academia](./assets/kimi-ambassador-academia.webp)](https://www.kimi.ai/lp/kimi-ambassador)
8
8
 
@@ -27,19 +27,19 @@ Choose a language below. Each guide opens on this page.
27
27
 
28
28
  ## Use Kimi in your coding tasks
29
29
 
30
- Ask Kimi to explain code, review changes, challenge a design, or make a focused fix. You can use this plugin in Claude Code or Codex.
30
+ Ask Kimi to explain code, review changes, challenge a design, or make a focused fix. You can use this plugin in Claude Code, Codex, or DeepSeek Harness (DSH).
31
31
 
32
- The plugin runs the Kimi Code CLI on your computer. It uses your Kimi subscription or a provider you configure in Kimi. Calls count against that subscription or API account. Your Claude Code or Codex login stays unchanged. The selected provider receives the prompts and code needed for your task.
32
+ The plugin runs the Kimi Code CLI on your computer. It uses your Kimi subscription or a provider you configure in Kimi. Calls count against that subscription or API account. Your host application's login stays unchanged. The selected provider receives the prompts and code needed for your task.
33
33
 
34
- Start with a question or a review. Their tool rules allow reading files and deny file writes and shell commands. Other modes can change files. Read the [safety limits](./docs/safety.md) before asking Kimi to make changes.
34
+ Start with a question or a review. Their tool rules allow reading files and deny file writes and shell commands. Other modes can change files. DSH provides the same seven operations with the same write boundaries. Read the [safety limits](./docs/safety.md) before asking Kimi to make changes.
35
35
 
36
36
  ## Before you install
37
37
 
38
38
  You need:
39
39
 
40
40
  - macOS or Linux
41
- - Claude Code or Codex with plugin support
42
- - Node.js 22.5 or newer
41
+ - Claude Code, Codex, or DSH with plugin support
42
+ - Node.js 22.5 or newer (24.9 or newer for DSH)
43
43
  - Kimi Code CLI 2.4.1 (certified; 2.4.0, 2.3.3, 2.3.2, 2.3.1 and 2.3.0 are also certified)
44
44
 
45
45
  Windows is not currently supported. Contributors can help with the [no-model Windows diagnostics checks](./docs/windows-validation.md). Check your versions in a terminal:
@@ -95,20 +95,40 @@ The hook checks Kimi's tool calls against the plugin's rules. The `Probe:` line
95
95
  Use $kimi-ask to explain how this project's main entry point works.
96
96
  ```
97
97
 
98
- If you use both Claude Code and Codex, run setup in each. Each app has its own hook in the shared Kimi configuration.
98
+ If you use multiple hosts, run setup in each. Each app has its own hook in the shared Kimi configuration.
99
+
100
+ ## Install in DeepSeek Harness
101
+
102
+ DSH requires patched host and profile dependencies (`zod >=4.6.6`, `undici >=8.12.2`). Follow the [host installation and audit instructions](./docs/registry-releases.md#patched-dsh-host-and-profiles) before installing this plugin.
103
+
104
+ The DSH bundle provides the same seven operations as the other hosts: `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm`, and `kimi_swarm_write`. They use the same companion runtime, safety hooks, jobs, budgets, and artifacts. Rescue and pursue edit the selected workspace; swarm-write returns a patch without applying or committing it.
105
+
106
+ From this repository checkout, run:
107
+
108
+ ```sh
109
+ dsh plugin --profile web add ./plugins/kimi-dsh
110
+ ```
111
+
112
+ For the published npm bundle, run `dsh plugin --profile web add kimi-plugin-cc` instead.
113
+
114
+ For DSH Discover, enable **Custom plugin indexes** and add `https://raw.githubusercontent.com/linxule/kimi-plugin-cc/main/dsh/index.json` as an index source.
115
+
116
+ Restart DSH when requested, then run `/kimi-setup` and `/kimi-setup --check`. Ask DSH to use one of the seven tools with a self-contained task. Job controls are `/kimi-status`, `/kimi-result`, and `/kimi-cancel`. `/kimi-setup --models` lists configured models, and `/kimi-setup --uninstall` removes only the DSH hook.
117
+
118
+ DSH requires Node.js 24.9 or newer and DSH `>=0.2.1-rc.5 <0.3.0`. See the [DSH bundle guide](./plugins/kimi-dsh/README.md) for profile-level model, thinking, budget, review-base, and other configuration.
99
119
 
100
120
  ## npm distribution (optional)
101
121
 
102
122
  The existing marketplace commands above remain the default for Claude Code and
103
123
  Codex. Published releases are also available as
104
124
  [`kimi-plugin-cc` on npm](https://www.npmjs.com/package/kimi-plugin-cc), including
105
- both compiled plugin distributions. No Bun, TypeScript, or install scripts are
125
+ all three compiled plugin distributions. No Bun, TypeScript, or install scripts are
106
126
  needed to use the package. Kimi Code CLI and Node are still prerequisites.
107
127
 
108
- For an explicitly versioned local Claude Code plugin, this example pins the 2.4.5 package:
128
+ For an explicitly versioned local Claude Code plugin, this example pins the 2.5.1 package:
109
129
 
110
130
  ```sh
111
- npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.4.5
131
+ npm install --prefix ~/.local/share/kimi-plugin-cc kimi-plugin-cc@2.5.1
112
132
  claude --plugin-dir ~/.local/share/kimi-plugin-cc/node_modules/kimi-plugin-cc
113
133
  ```
114
134
 
@@ -251,19 +271,19 @@ Contributors need Bun to run `bun run check`. The detailed engineering documents
251
271
 
252
272
  ## 用 Kimi 辅助编程
253
273
 
254
- 你可以让 Kimi 解释代码、审查修改、指出设计中的问题,或完成一项范围明确的修复。本插件支持 Claude Code 和 Codex。
274
+ 你可以让 Kimi 解释代码、审查修改、指出设计中的问题,或完成一项范围明确的修复。本插件支持 Claude Code、Codex 和 DeepSeek Harness(DSH)。
255
275
 
256
- 插件在你的电脑上调用 Kimi Code CLI,使用你的 Kimi 订阅或在 Kimi 中配置的服务商。调用会消耗相应的订阅额度或产生 API 费用。Claude Code 和 Codex 的登录状态不变。任务所需的提示词和代码会发送给所选服务商。
276
+ 插件在你的电脑上调用 Kimi Code CLI,使用你的 Kimi 订阅或在 Kimi 中配置的服务商。调用会消耗相应的订阅额度或产生 API 费用。宿主应用的登录状态不变。任务所需的提示词和代码会发送给所选服务商。
257
277
 
258
- 建议先尝试提问或代码审查。这两类任务允许读取文件,禁止写入文件和执行 shell 命令。其他模式可以修改文件。让 Kimi 动手修改前,请先了解[安全限制](./docs/safety.md)。
278
+ 建议先尝试提问或代码审查。这两类任务允许读取文件,禁止写入文件和执行 shell 命令。其他模式可以修改文件;DSH 同样提供七类操作,并遵循相同的写入边界。让 Kimi 动手修改前,请先了解[安全限制](./docs/safety.md)。DSH 的安装方法见[英文说明](#install-in-deepseek-harness)。
259
279
 
260
280
  ## 安装前准备
261
281
 
262
282
  你需要:
263
283
 
264
284
  - macOS 或 Linux
265
- - 支持插件的 Claude Code 或 Codex
266
- - Node.js 22.5 或更高版本
285
+ - 支持插件的 Claude Code、Codex 或 DSH
286
+ - Node.js 22.5 或更高版本(DSH 需要 24.9 或更高版本)
267
287
  - Kimi Code CLI 2.4.1(已认证;2.4.0、2.3.3、2.3.2、2.3.1 和 2.3.0 也已认证)
268
288
 
269
289
  目前不支持 Windows。在终端检查版本:
@@ -321,9 +341,27 @@ codex plugin add kimi@kimi-marketplace
321
341
 
322
342
  如果同时使用 Claude Code 和 Codex,请分别运行 setup。两个应用各有自己的钩子,共用一份 Kimi 配置。
323
343
 
344
+ ## 在 DeepSeek Harness(DSH)中安装
345
+
346
+ DSH 宿主及各配置所用的依赖需要修补(`zod >=4.6.6`、`undici >=8.12.2`)。安装插件前,请按[宿主安装与审计说明](./docs/registry-releases.md#patched-dsh-host-and-profiles)操作。
347
+
348
+ DSH 与其他宿主一样提供七类操作:`kimi_ask`、`kimi_review`、`kimi_challenge`、`kimi_rescue`、`kimi_pursue`、`kimi_swarm` 和 `kimi_swarm_write`。它们使用同一套 companion 运行时、安全钩子、任务记录、预算和产物。Rescue 和 pursue 会修改所选工作区;swarm-write 只返回补丁,不会应用或提交它。
349
+
350
+ 从此仓库检出安装:
351
+
352
+ ```sh
353
+ dsh plugin --profile web add ./plugins/kimi-dsh
354
+ ```
355
+
356
+ 从已发布的根包安装时,改为运行 `dsh plugin --profile web add kimi-plugin-cc`。
357
+
358
+ 按提示重启 DSH,然后运行 `/kimi-setup` 和 `/kimi-setup --check`。让 DSH 使用上述任一工具,并提供独立完整的任务。任务控制命令为 `/kimi-status`、`/kimi-result` 和 `/kimi-cancel`。`/kimi-setup --models` 列出已配置模型,`/kimi-setup --uninstall` 只移除 DSH 自己的钩子。
359
+
360
+ DSH 需要 Node.js 24.9 或更高版本,以及 DSH `>=0.2.1-rc.5 <0.3.0`。配置模型、思考强度、预算、review base 等选项,请参阅 [DSH 指南](./plugins/kimi-dsh/README.md)。
361
+
324
362
  ## npm 分发(可选)
325
363
 
326
- 完整插件也已发布到 [npm,包名为 `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc),包含 Claude Code 和 Codex 的预编译运行时,无需 Bun 或构建步骤。默认仍推荐上方的插件市场安装方式。需要通过 npm 安装本地 Claude Code 插件时,请参照 [npm 安装步骤](#npm-distribution-optional)。每个应用只加载一份插件;npm 不提供 `npx kimi-plugin-cc` 命令。Codex 用户继续使用上方的 GitHub 插件市场命令。
364
+ 完整插件也已发布到 [npm,包名为 `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc),包含 Claude Code、Codex 和 DSH 的预编译运行时,无需 Bun 或构建步骤。默认仍推荐上方的插件市场安装方式。需要通过 npm 安装本地 Claude Code 插件时,请参照 [npm 安装步骤](#npm-distribution-optional)。每个应用只加载一份插件;npm 不提供 `npx kimi-plugin-cc` 命令。Codex 用户继续使用上方的 GitHub 插件市场命令。
327
365
 
328
366
  ## 选择任务
329
367
 
@@ -450,19 +488,21 @@ Ask、review、challenge 和只读 swarm 的工具规则禁止写入文件和执
450
488
 
451
489
  ## Utiliser Kimi pour vos tâches de développement
452
490
 
453
- Demandez à Kimi d'expliquer du code, de faire une revue de code, de remettre en question une approche ou de corriger un problème précis. Ce plugin fonctionne dans Claude Code et Codex.
491
+ Demandez à Kimi d'expliquer du code, de faire une revue de code, de remettre en question une approche ou de corriger un problème précis. Ce plugin fonctionne dans Claude Code, Codex et DeepSeek Harness (DSH).
454
492
 
455
- Le plugin exécute Kimi Code CLI sur votre ordinateur. Il utilise votre abonnement Kimi ou un fournisseur configuré dans Kimi. Les appels consomment votre quota d'abonnement ou sont facturés sur votre compte API. Votre connexion à Claude Code ou à Codex ne change pas. Le fournisseur choisi reçoit les prompts et le code nécessaires à la tâche.
493
+ Le plugin exécute Kimi Code CLI sur votre ordinateur. Il utilise votre abonnement Kimi ou un fournisseur configuré dans Kimi. Les appels consomment votre quota d'abonnement ou sont facturés sur votre compte API. La connexion à l'application hôte ne change pas. Le fournisseur choisi reçoit les prompts et le code nécessaires à la tâche.
456
494
 
457
- Commencez par une question ou une revue de code. Ces modes autorisent la lecture des fichiers, mais interdisent leur modification et l'exécution de commandes shell. D'autres modes peuvent modifier des fichiers. Consultez les [limites de sécurité](./docs/safety.md) avant de demander des modifications à Kimi.
495
+ Commencez par une question ou une revue de code. Ces modes autorisent la lecture des fichiers, mais interdisent leur modification et l'exécution de commandes shell. D'autres modes peuvent modifier des fichiers ; DSH nécessite des dépendances corrigées dans son installation et ses profils (`zod >=4.6.6`, `undici >=8.12.2`). Suivez les [instructions d’installation et d’audit](./docs/registry-releases.md#patched-dsh-host-and-profiles) avant d’installer ce plugin.
496
+
497
+ DSH propose les sept mêmes opérations avec les mêmes limites d'écriture. Consultez les [limites de sécurité](./docs/safety.md) avant de demander des modifications à Kimi. Voir le [guide DSH en anglais](#install-in-deepseek-harness) pour l'installation.
458
498
 
459
499
  ## Préparer l'installation
460
500
 
461
501
  Il vous faut :
462
502
 
463
503
  - macOS ou Linux
464
- - une version de Claude Code ou de Codex prenant en charge les plugins
465
- - Node.js 22.5 ou une version ultérieure
504
+ - une version de Claude Code, Codex ou DSH prenant en charge les plugins
505
+ - Node.js 22.5 ou une version ultérieure (24.9 ou ultérieure pour DSH)
466
506
  - Kimi Code CLI 2.4.1 (certifiée ; les versions 2.4.0, 2.3.3, 2.3.2, 2.3.1 et 2.3.0 sont également certifiées)
467
507
 
468
508
  Windows n'est pas pris en charge actuellement. Vérifiez les versions dans un terminal :
@@ -520,9 +560,25 @@ Utilise $kimi-ask pour expliquer le fonctionnement du point d'entrée principal
520
560
 
521
561
  Si vous utilisez Claude Code et Codex, lancez setup dans chaque application. Chacune possède son propre hook dans la configuration Kimi commune.
522
562
 
563
+ ## Installer dans DeepSeek Harness (DSH)
564
+
565
+ DSH propose les sept mêmes opérations que les autres hôtes : `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm` et `kimi_swarm_write`. Elles utilisent le même runtime companion, les mêmes hooks de sécurité, jobs, budgets et artefacts. Rescue et pursue modifient l'espace de travail sélectionné ; swarm-write renvoie un patch sans l'appliquer ni le committer.
566
+
567
+ Depuis une copie de ce dépôt, exécutez :
568
+
569
+ ```sh
570
+ dsh plugin --profile web add ./plugins/kimi-dsh
571
+ ```
572
+
573
+ Pour installer le paquet racine publié, exécutez plutôt `dsh plugin --profile web add kimi-plugin-cc`.
574
+
575
+ Redémarrez DSH si demandé, puis lancez `/kimi-setup` et `/kimi-setup --check`. Demandez à DSH d'utiliser l'un des sept outils avec une tâche autonome. Les contrôles sont `/kimi-status`, `/kimi-result` et `/kimi-cancel`. `/kimi-setup --models` liste les modèles configurés, et `/kimi-setup --uninstall` supprime uniquement le hook DSH.
576
+
577
+ DSH requiert Node.js 24.9 ou supérieur et DSH `>=0.2.1-rc.5 <0.3.0`. Consultez le [guide DSH](./plugins/kimi-dsh/README.md) pour configurer le modèle, l'effort de réflexion, le budget, la base de revue et les autres options.
578
+
523
579
  ## Distribution npm (facultative)
524
580
 
525
- Le plugin complet est aussi disponible sur [npm sous le nom `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc), avec les runtimes précompilés pour Claude Code et Codex, sans Bun ni compilation. Les marketplaces ci-dessus restent le mode d'installation par défaut. Pour installer une copie locale dans Claude Code via npm, suivez les [étapes npm](#npm-distribution-optional) (en anglais). Ne chargez qu'une copie du plugin par application ; le paquet ne fournit pas de commande `npx kimi-plugin-cc`. Pour Codex, continuez à utiliser la marketplace GitHub ci-dessus.
581
+ Le plugin complet est aussi disponible sur [npm sous le nom `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc), avec les runtimes précompilés pour Claude Code, Codex et DSH, sans Bun ni compilation. Les marketplaces ci-dessus restent le mode d'installation par défaut. Pour installer une copie locale dans Claude Code via npm, suivez les [étapes npm](#npm-distribution-optional) (en anglais). Ne chargez qu'une copie du plugin par application ; le paquet ne fournit pas de commande `npx kimi-plugin-cc`. Pour Codex, continuez à utiliser la marketplace GitHub ci-dessus.
526
582
 
527
583
  ## Choisir une tâche
528
584
 
@@ -649,19 +705,19 @@ Les contributeurs ont besoin de Bun pour exécuter `bun run check`. La documenta
649
705
 
650
706
  ## Kimi を開発に使う
651
707
 
652
- コードの説明、変更のレビュー、設計の問題点の指摘、範囲を絞った修正を Kimi に依頼できます。このプラグインは Claude Code と Codex で使えます。
708
+ コードの説明、変更のレビュー、設計の問題点の指摘、範囲を絞った修正を Kimi に依頼できます。このプラグインは Claude Code、Codex、DeepSeek Harness(DSH)で使えます。
653
709
 
654
- プラグインは、お使いのコンピューターで Kimi Code CLI を実行します。Kimi のサブスクリプション、または Kimi に設定したプロバイダーを利用します。呼び出しは、そのサブスクリプションの利用枠を消費するか、API アカウントに課金されます。Claude Code や Codex のログイン状態は変わりません。タスクに必要なプロンプトとコードは、選択したプロバイダーに送信されます。
710
+ プラグインは、お使いのコンピューターで Kimi Code CLI を実行します。Kimi のサブスクリプション、または Kimi に設定したプロバイダーを利用します。呼び出しは、そのサブスクリプションの利用枠を消費するか、API アカウントに課金されます。ホストアプリのログイン状態は変わりません。タスクに必要なプロンプトとコードは、選択したプロバイダーに送信されます。
655
711
 
656
- まずは質問かコードレビューを試してください。これらのモードではファイルの読み取りを許可し、書き込みとシェルコマンドの実行を禁止しています。ほかのモードではファイルを変更できます。変更を依頼する前に、[安全上の制限](./docs/safety.md)を確認してください。
712
+ まずは質問かコードレビューを試してください。これらのモードではファイルの読み取りを許可し、書き込みとシェルコマンドの実行を禁止しています。ほかのモードではファイルを変更できます。DSH でも同じ 7 種類の操作を同じ書き込み範囲で提供します。変更を依頼する前に、[安全上の制限](./docs/safety.md)を確認してください。DSH のインストール方法は[英語の案内](#install-in-deepseek-harness)を参照してください。
657
713
 
658
714
  ## インストール前の準備
659
715
 
660
716
  次の環境が必要です。
661
717
 
662
718
  - macOS または Linux
663
- - プラグインに対応した Claude Code または Codex
664
- - Node.js 22.5 以降
719
+ - プラグインに対応した Claude Code、Codex、または DSH
720
+ - Node.js 22.5 以降(DSH では 24.9 以降)
665
721
  - Kimi Code CLI 2.4.1(認証済み。2.4.0、2.3.3、2.3.2、2.3.1 と 2.3.0 も認証済み)
666
722
 
667
723
  現在、Windows には対応していません。ターミナルでバージョンを確認します。
@@ -719,9 +775,27 @@ $kimi-ask を使って、このプロジェクトのメインのエントリー
719
775
 
720
776
  Claude Code と Codex の両方を使う場合は、それぞれで setup を実行します。共通の Kimi 設定内に、各アプリ専用のフックが作られます。
721
777
 
778
+ ## DeepSeek Harness(DSH)にインストールする
779
+
780
+ DSH 本体とプロファイルの依存パッケージには修正版(`zod >=4.6.6`、`undici >=8.12.2`)が必要です。プラグインのインストール前に[本体のインストールと監査の手順](./docs/registry-releases.md#patched-dsh-host-and-profiles)を確認してください。
781
+
782
+ DSH でも、ほかのホストと同じ 7 種類の操作を提供します:`kimi_ask`、`kimi_review`、`kimi_challenge`、`kimi_rescue`、`kimi_pursue`、`kimi_swarm`、`kimi_swarm_write`。同じ companion ランタイム、安全フック、ジョブ、予算、成果物を使用します。Rescue と pursue は選択したワークスペースを変更し、swarm-write はパッチを返すだけで、適用やコミットは行いません。
783
+
784
+ このリポジトリのチェックアウトからインストールする場合:
785
+
786
+ ```sh
787
+ dsh plugin --profile web add ./plugins/kimi-dsh
788
+ ```
789
+
790
+ 公開されているルートパッケージからインストールする場合は、代わりに `dsh plugin --profile web add kimi-plugin-cc` を実行します。
791
+
792
+ 案内に従って DSH を再起動したら、`/kimi-setup` と `/kimi-setup --check` を実行します。独立した内容のタスクを指定して、上記のいずれかのツールを使ってください。ジョブ制御は `/kimi-status`、`/kimi-result`、`/kimi-cancel` です。`/kimi-setup --models` で設定済みモデルを一覧でき、`/kimi-setup --uninstall` は DSH のフックだけを削除します。
793
+
794
+ DSH には Node.js 24.9 以降と、DSH `>=0.2.1-rc.5 <0.3.0` が必要です。モデル、思考強度、予算、review base などの設定は [DSH ガイド](./plugins/kimi-dsh/README.md)を参照してください。
795
+
722
796
  ## npm 配布(任意)
723
797
 
724
- 完全なプラグインは [npm の `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc) としても公開されています。Claude Code と Codex 向けのコンパイル済みランタイムを含み、Bun やビルド作業は不要です。通常は上記のマーケットプレイス経由でインストールしてください。npm から Claude Code 用のローカルコピーを入れる場合は、[npm のインストール手順](#npm-distribution-optional)(英語)を参照してください。各アプリで読み込むプラグインは一つにし、重複させないでください。`npx kimi-plugin-cc` コマンドはありません。Codex では引き続き上記の GitHub マーケットプレイスを使います。
798
+ 完全なプラグインは [npm の `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc) としても公開されています。Claude Code、Codex、DSH 向けのコンパイル済みランタイムを含み、Bun やビルド作業は不要です。通常は上記のマーケットプレイス経由でインストールしてください。npm から Claude Code 用のローカルコピーを入れる場合は、[npm のインストール手順](#npm-distribution-optional)(英語)を参照してください。各アプリで読み込むプラグインは一つにし、重複させないでください。`npx kimi-plugin-cc` コマンドはありません。Codex では引き続き上記の GitHub マーケットプレイスを使います。
725
799
 
726
800
  ## タスクを選ぶ
727
801
 
@@ -9,7 +9,8 @@ import { SETUP_SUPPORTED_FLAGS } from "../parsing.js";
9
9
  // absolute Node path avoids PATH dependence under sanitized launches. Setup
10
10
  // probes the hook directly with resolveNodeBinary and through `/bin/sh -c`
11
11
  // using the exact installed command bytes. It also warns about broad permission
12
- // denies and refuses duplicate, orphaned, or malformed managed blocks.
12
+ // denies, repairs orphan marker comments, and refuses remaining
13
+ // duplicate, orphaned, or malformed managed blocks.
13
14
  //
14
15
  // Subcommands: install (default), --check, --uninstall [--all], and
15
16
  // --enable-review-gate/--disable-review-gate.
@@ -25,12 +26,13 @@ import { readPluginConfig, writePluginConfig } from "../config.js";
25
26
  import { RuntimeError } from "../errors.js";
26
27
  import { normalizeTopLevelInlineHooks, validateKimiHookSetForEnvironment, withKimiConfigLock, } from "../hooks/config-safety.js";
27
28
  import { buildHookShellCommand, hostIdFromHookCommand, resolveHookScriptPath, resolveHostId, resolveNodeBinary, } from "../hooks/install-paths.js";
28
- import { decodeManagedCommandLine, effectiveHost, evaluateInstalled, findUnmanagedApprovalHookBlocks, MARKERS, parseManagedBlock, } from "../hooks/managed-block.js";
29
+ import { decodeManagedCommandLine, effectiveHost, evaluateInstalled, findUnmanagedApprovalHookBlocks, markerCommentLines, MARKERS, parseManagedBlock, } from "../hooks/managed-block.js";
29
30
  import { formatVersionOutOfRangeWarning, probeKimiVersion, } from "../kimi-version-probe.js";
30
31
  import { isNativeV2CertifiedVersion } from "../kimi-engine.js";
31
32
  import { resolveKimiHome } from "../kimi-home.js";
32
33
  import { ensurePluginPaths, resolvePluginPaths } from "../paths.js";
33
34
  import { KIMI_PLUGIN_CC_VERSION } from "../version.js";
35
+ import { parse as parseToml } from "../vendor/smol-toml/parse.js";
34
36
  const BEGIN_MARKER_PREFIX = "# === BEGIN kimi-plugin-cc-managed";
35
37
  const DEFAULT_HOOK_TIMEOUT_S = 15;
36
38
  // Probe timing. The direct script probe is local and fast; allow 5s for
@@ -119,7 +121,17 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
119
121
  const mutation = await withKimiConfigLock(configPath, async () => {
120
122
  const original = await readConfigSafe(configPath);
121
123
  const lineEnding = detectLineEnding(original);
122
- const inlineHooks = normalizeTopLevelInlineHooks(original, lineEnding);
124
+ try {
125
+ parseToml(original);
126
+ }
127
+ catch {
128
+ await assertKimiHookSetValid(original, configPath, "setup.install", context.env);
129
+ }
130
+ const repaired = removeOrphanMarkers(original);
131
+ if (repaired.count > 0) {
132
+ warnings.push(`Removed ${repaired.count} orphaned managed marker comment(s); surrounding config was preserved.`);
133
+ }
134
+ const inlineHooks = normalizeTopLevelInlineHooks(repaired.contents, lineEnding);
123
135
  if (inlineHooks.found) {
124
136
  // Validate before normalizing so an invalid inline entry is reported
125
137
  // against the user's original bytes and the config remains untouched.
@@ -133,7 +145,7 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
133
145
  // splice line numbers. Another host's marker-less table may be its live hook
134
146
  // after a config rewrite strips comments, so it must remain untouched.
135
147
  const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
136
- const { pruned, count: prunedCount, commands: prunedCommands } = pruneOrphanApprovalHooks(inlineHooks.contents, lineEnding, hostId, expectedCommand);
148
+ const { pruned, count: prunedCount, commands: prunedCommands } = pruneOrphanApprovalHooks(inlineHooks.contents, hostId, expectedCommand);
137
149
  const existing = pruned;
138
150
  if (prunedCount > 0) {
139
151
  const readorned = prunedCommands.includes(expectedCommand);
@@ -145,7 +157,7 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
145
157
  if (state.kind === "orphan") {
146
158
  throw new RuntimeError("SETUP_ORPHAN_MARKERS", [
147
159
  `kimi-code config at ${configPath} contains an orphaned ${state.detail} marker.`,
148
- "Run `/kimi:setup --uninstall` to clean up, then re-run `/kimi:setup`.",
160
+ "Inspect the managed marker comments and re-run setup.",
149
161
  ].join(" "), "setup.install", { details: { configPath, orphan: state.detail } });
150
162
  }
151
163
  if (state.kind === "duplicate") {
@@ -393,12 +405,11 @@ async function runUninstallLocked(configPath, hookScriptPath, hostId, removeAllH
393
405
  // other hosts' blocks and live marker-less tables remain intact. `--all` is
394
406
  // the explicit every-host sweep for all of this plugin's hook tables.
395
407
  const { stripped: strippedMarkers, removedBlocks, orphansLeft } = stripManagedBlocks(existing, hostId, removeAllHosts);
396
- const lineEnding = detectLineEnding(existing);
397
408
  // Exact current command bytes also identify this host's table when a host-id
398
409
  // override disagrees with path derivation or a dev path is outside the strict
399
410
  // ownership pattern.
400
411
  const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
401
- const { pruned: stripped, count: prunedCount } = pruneOrphanApprovalHooks(strippedMarkers, lineEnding, removeAllHosts ? undefined : hostId, expectedCommand);
412
+ const { pruned: stripped, count: prunedCount } = pruneOrphanApprovalHooks(strippedMarkers, removeAllHosts ? undefined : hostId, expectedCommand);
402
413
  const changed = stripped !== existing;
403
414
  if (changed) {
404
415
  await writeConfigAtomic(configPath, stripped);
@@ -517,10 +528,10 @@ function detectLineEnding(contents) {
517
528
  return contents.includes("\r\n") ? "\r\n" : "\n";
518
529
  }
519
530
  function spliceBlock(contents, beginLine, endLine, replacement, lineEnding) {
520
- const lines = splitPreservingEnding(contents);
521
- const before = lines.slice(0, beginLine).join(lineEnding);
522
- const after = lines.slice(endLine + 1).join(lineEnding);
523
- const beforeWithSep = before.endsWith(lineEnding) || before.length === 0 ? before : `${before}${lineEnding}`;
531
+ const lines = contents.split(/(?<=\n)/);
532
+ const before = lines.slice(0, beginLine).join("");
533
+ const after = lines.slice(endLine + 1).join("");
534
+ const beforeWithSep = before.endsWith("\n") || before.length === 0 ? before : `${before}${lineEnding}`;
524
535
  const replacementWithEnding = replacement.endsWith(lineEnding) ? replacement : `${replacement}${lineEnding}`;
525
536
  return `${beforeWithSep}${replacementWithEnding}${after}`;
526
537
  }
@@ -536,14 +547,54 @@ function splitPreservingEnding(contents) {
536
547
  // pass the desired line ending).
537
548
  return contents.split("\n").map((line) => line.replace(/\r$/, ""));
538
549
  }
550
+ // A partial upstream hooks-array rewrite can leave one marker comment behind.
551
+ // Remove only unpaired marker comments, never the intervening content. The
552
+ // normal full-file validation still gates writes.
553
+ function removeOrphanMarkers(contents) {
554
+ const lines = splitPreservingEnding(contents);
555
+ const orphans = [];
556
+ const comments = markerCommentLines(contents);
557
+ let open = null;
558
+ for (let i = 0; i < lines.length; i += 1) {
559
+ if (!comments.has(i))
560
+ continue;
561
+ const line = lines[i].trim();
562
+ const begin = MARKERS.BEGIN_LINE_RE.exec(line);
563
+ if (begin !== null) {
564
+ if (open !== null)
565
+ orphans.push(open);
566
+ open = { index: i, host: begin[1]?.toLowerCase() ?? null };
567
+ }
568
+ else {
569
+ const end = MARKERS.END_LINE_RE.exec(line);
570
+ if (end === null)
571
+ continue;
572
+ const endMarker = { index: i, host: end[1]?.toLowerCase() ?? null };
573
+ if (open === null)
574
+ orphans.push(endMarker);
575
+ else {
576
+ if (open.host !== endMarker.host)
577
+ orphans.push(open, endMarker);
578
+ open = null;
579
+ }
580
+ }
581
+ }
582
+ if (open !== null)
583
+ orphans.push(open);
584
+ const remove = new Set(orphans.map((marker) => marker.index));
585
+ return remove.size === 0
586
+ ? { contents, count: 0 }
587
+ : { contents: contents.split(/(?<=\n)/).filter((_, index) => !remove.has(index)).join(""), count: remove.size };
588
+ }
539
589
  /**
540
590
  * Remove managed blocks owned by `currentHost`, or every host with `removeAll`.
541
591
  * Ownership comes from the marker suffix or a legacy command's derived host.
542
592
  * Orphan markers are removed individually so surrounding user content survives.
543
593
  */
544
594
  function stripManagedBlocks(contents, currentHost, removeAll) {
545
- const lineEnding = detectLineEnding(contents);
546
595
  const lines = splitPreservingEnding(contents);
596
+ const rawLines = contents.split(/(?<=\n)/);
597
+ const markerLines = markerCommentLines(contents);
547
598
  const result = [];
548
599
  const orphansLeft = [];
549
600
  let removedBlocks = 0;
@@ -567,7 +618,7 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
567
618
  while (i < lines.length) {
568
619
  const line = lines[i];
569
620
  const trimmed = line.trim();
570
- const begin = MARKERS.BEGIN_LINE_RE.exec(trimmed);
621
+ const begin = markerLines.has(i) ? MARKERS.BEGIN_LINE_RE.exec(trimmed) : null;
571
622
  if (begin !== null) {
572
623
  const beginHost = begin[1]?.toLowerCase() ?? null;
573
624
  // Look ahead for the matching END, capturing the block's command en route.
@@ -575,12 +626,14 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
575
626
  let blockCommand = null;
576
627
  for (let j = i + 1; j < lines.length; j += 1) {
577
628
  const nextTrimmed = lines[j].trim();
578
- if (MARKERS.BEGIN_LINE_RE.test(nextTrimmed)) {
629
+ if (markerLines.has(j) && MARKERS.BEGIN_LINE_RE.test(nextTrimmed)) {
579
630
  // Two BEGINs in a row — the outer one is an orphan.
580
631
  break;
581
632
  }
582
- if (MARKERS.END_LINE_RE.test(nextTrimmed)) {
583
- endIdx = j;
633
+ const end = markerLines.has(j) ? MARKERS.END_LINE_RE.exec(nextTrimmed) : null;
634
+ if (end !== null) {
635
+ if ((end[1]?.toLowerCase() ?? null) === beginHost)
636
+ endIdx = j;
584
637
  break;
585
638
  }
586
639
  const decoded = decodeManagedCommandLine(nextTrimmed);
@@ -594,7 +647,7 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
594
647
  i += 1;
595
648
  continue;
596
649
  }
597
- result.push(line);
650
+ result.push(rawLines[i]);
598
651
  i += 1;
599
652
  continue;
600
653
  }
@@ -606,11 +659,11 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
606
659
  }
607
660
  // Another host's block — keep it verbatim.
608
661
  for (let k = i; k <= endIdx; k += 1)
609
- result.push(lines[k]);
662
+ result.push(rawLines[k]);
610
663
  i = endIdx + 1;
611
664
  continue;
612
665
  }
613
- const end = MARKERS.END_LINE_RE.exec(trimmed);
666
+ const end = markerLines.has(i) ? MARKERS.END_LINE_RE.exec(trimmed) : null;
614
667
  if (end !== null) {
615
668
  const endHost = end[1]?.toLowerCase() ?? null;
616
669
  // Orphan END with no preceding BEGIN: drop only this line (if ours).
@@ -619,27 +672,14 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
619
672
  i += 1;
620
673
  continue;
621
674
  }
622
- result.push(line);
675
+ result.push(rawLines[i]);
623
676
  i += 1;
624
677
  continue;
625
678
  }
626
- result.push(line);
679
+ result.push(rawLines[i]);
627
680
  i += 1;
628
681
  }
629
- const collapsed = [];
630
- let blankRun = 0;
631
- for (const line of result) {
632
- if (line.length === 0) {
633
- blankRun += 1;
634
- if (blankRun <= 2)
635
- collapsed.push(line);
636
- }
637
- else {
638
- blankRun = 0;
639
- collapsed.push(line);
640
- }
641
- }
642
- return { stripped: collapsed.join(lineEnding), removedBlocks, orphansLeft };
682
+ return { stripped: result.join(""), removedBlocks, orphansLeft };
643
683
  }
644
684
  /**
645
685
  * Remove orphaned, marker-less `[[hooks]]` tables that are unambiguously this
@@ -654,7 +694,7 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
654
694
  * host — REQUIRED for scoped install/uninstall so one host never disarms the
655
695
  * other's live (marker-stripped) hook. Omit only for `--uninstall --all`.
656
696
  */
657
- function pruneOrphanApprovalHooks(contents, lineEnding, ownedBy, alsoMatchCommand) {
697
+ function pruneOrphanApprovalHooks(contents, ownedBy, alsoMatchCommand) {
658
698
  const ranges = findUnmanagedApprovalHookBlocks(contents, ownedBy, alsoMatchCommand);
659
699
  if (ranges.length === 0)
660
700
  return { pruned: contents, count: 0, commands: [] };
@@ -663,23 +703,10 @@ function pruneOrphanApprovalHooks(contents, lineEnding, ownedBy, alsoMatchComman
663
703
  for (let i = start; i < end; i += 1)
664
704
  remove.add(i);
665
705
  }
666
- const lines = splitPreservingEnding(contents);
706
+ const lines = contents.split(/(?<=\n)/);
667
707
  const kept = lines.filter((_, idx) => !remove.has(idx));
668
- const collapsed = [];
669
- let blankRun = 0;
670
- for (const line of kept) {
671
- if (line.length === 0) {
672
- blankRun += 1;
673
- if (blankRun <= 2)
674
- collapsed.push(line);
675
- }
676
- else {
677
- blankRun = 0;
678
- collapsed.push(line);
679
- }
680
- }
681
708
  return {
682
- pruned: collapsed.join(lineEnding),
709
+ pruned: kept.join(""),
683
710
  count: ranges.length,
684
711
  commands: ranges.map((range) => range.command),
685
712
  };