kimi-plugin-cc 2.5.1 → 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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +9 -1
- package/README.md +62 -4
- package/dist/commands/setup.js +78 -51
- package/dist/dsh/companion.js +9 -6
- package/dist/dsh/index.js +15 -2
- package/dist/hooks/managed-block.js +86 -20
- package/dist/version.js +1 -1
- package/docs/migration.md +4 -2
- package/docs/registry-releases.md +64 -3
- package/docs/safety.md +3 -1
- package/package.json +38 -5
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/commands/setup.js +78 -51
- package/plugins/kimi-codex/dist/dsh/companion.js +9 -6
- package/plugins/kimi-codex/dist/dsh/index.js +15 -2
- package/plugins/kimi-codex/dist/hooks/managed-block.js +86 -20
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-dsh/README.md +4 -1
- package/plugins/kimi-dsh/dist/commands/setup.js +78 -51
- package/plugins/kimi-dsh/dist/dsh/companion.js +9 -6
- package/plugins/kimi-dsh/dist/dsh/index.js +15 -2
- package/plugins/kimi-dsh/dist/hooks/managed-block.js +86 -20
- package/plugins/kimi-dsh/dist/version.js +1 -1
- package/plugins/kimi-dsh/package.json +33 -3
|
@@ -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.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,11 +2,19 @@
|
|
|
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
|
+
|
|
5
13
|
## 2.5.1 — 2026-11-01
|
|
6
14
|
|
|
7
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.
|
|
8
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.
|
|
9
|
-
- Pin
|
|
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.
|
|
10
18
|
|
|
11
19
|
## 2.5.0 — 2026-11-01
|
|
12
20
|
|
package/README.md
CHANGED
|
@@ -99,6 +99,8 @@ If you use multiple hosts, run setup in each. Each app has its own hook in the s
|
|
|
99
99
|
|
|
100
100
|
## Install in DeepSeek Harness
|
|
101
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
|
+
|
|
102
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.
|
|
103
105
|
|
|
104
106
|
From this repository checkout, run:
|
|
@@ -109,6 +111,8 @@ dsh plugin --profile web add ./plugins/kimi-dsh
|
|
|
109
111
|
|
|
110
112
|
For the published npm bundle, run `dsh plugin --profile web add kimi-plugin-cc` instead.
|
|
111
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
|
+
|
|
112
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.
|
|
113
117
|
|
|
114
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.
|
|
@@ -337,9 +341,27 @@ codex plugin add kimi@kimi-marketplace
|
|
|
337
341
|
|
|
338
342
|
如果同时使用 Claude Code 和 Codex,请分别运行 setup。两个应用各有自己的钩子,共用一份 Kimi 配置。
|
|
339
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
|
+
|
|
340
362
|
## npm 分发(可选)
|
|
341
363
|
|
|
342
|
-
完整插件也已发布到 [npm,包名为 `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc),包含 Claude Code 和
|
|
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 插件市场命令。
|
|
343
365
|
|
|
344
366
|
## 选择任务
|
|
345
367
|
|
|
@@ -470,7 +492,9 @@ Demandez à Kimi d'expliquer du code, de faire une revue de code, de remettre en
|
|
|
470
492
|
|
|
471
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.
|
|
472
494
|
|
|
473
|
-
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
|
|
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.
|
|
474
498
|
|
|
475
499
|
## Préparer l'installation
|
|
476
500
|
|
|
@@ -536,9 +560,25 @@ Utilise $kimi-ask pour expliquer le fonctionnement du point d'entrée principal
|
|
|
536
560
|
|
|
537
561
|
Si vous utilisez Claude Code et Codex, lancez setup dans chaque application. Chacune possède son propre hook dans la configuration Kimi commune.
|
|
538
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
|
+
|
|
539
579
|
## Distribution npm (facultative)
|
|
540
580
|
|
|
541
|
-
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
|
|
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.
|
|
542
582
|
|
|
543
583
|
## Choisir une tâche
|
|
544
584
|
|
|
@@ -735,9 +775,27 @@ $kimi-ask を使って、このプロジェクトのメインのエントリー
|
|
|
735
775
|
|
|
736
776
|
Claude Code と Codex の両方を使う場合は、それぞれで setup を実行します。共通の Kimi 設定内に、各アプリ専用のフックが作られます。
|
|
737
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
|
+
|
|
738
796
|
## npm 配布(任意)
|
|
739
797
|
|
|
740
|
-
完全なプラグインは [npm の `kimi-plugin-cc`](https://www.npmjs.com/package/kimi-plugin-cc) としても公開されています。Claude Code
|
|
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 マーケットプレイスを使います。
|
|
741
799
|
|
|
742
800
|
## タスクを選ぶ
|
|
743
801
|
|
package/dist/commands/setup.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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,
|
|
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
|
-
"
|
|
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,
|
|
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 =
|
|
521
|
-
const before = lines.slice(0, beginLine).join(
|
|
522
|
-
const after = lines.slice(endLine + 1).join(
|
|
523
|
-
const beforeWithSep = before.endsWith(
|
|
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
|
-
|
|
583
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
675
|
+
result.push(rawLines[i]);
|
|
623
676
|
i += 1;
|
|
624
677
|
continue;
|
|
625
678
|
}
|
|
626
|
-
result.push(
|
|
679
|
+
result.push(rawLines[i]);
|
|
627
680
|
i += 1;
|
|
628
681
|
}
|
|
629
|
-
|
|
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,
|
|
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 =
|
|
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:
|
|
709
|
+
pruned: kept.join(""),
|
|
683
710
|
count: ranges.length,
|
|
684
711
|
commands: ranges.map((range) => range.command),
|
|
685
712
|
};
|
package/dist/dsh/companion.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
3
3
|
import { assertUsableCwd, settleRunResult, subprocessRunHandle, } from "@deepseek-ai/dsh-subagent";
|
|
4
|
+
import { preferStableNodePath } from "../hooks/install-paths.js";
|
|
4
5
|
export const DSH_COMPANION_OPERATIONS = [
|
|
5
6
|
"ask",
|
|
6
7
|
"review",
|
|
@@ -20,7 +21,7 @@ export function companionTextTask(prompt) {
|
|
|
20
21
|
}
|
|
21
22
|
return text;
|
|
22
23
|
}
|
|
23
|
-
export function companionArgv(operation, prompt, companionPath, nodeBin = process.execPath, options = {}) {
|
|
24
|
+
export function companionArgv(operation, prompt, companionPath, nodeBin = preferStableNodePath(process.execPath), options = {}) {
|
|
24
25
|
const command = operation === "ask" || operation === "review"
|
|
25
26
|
? [operation]
|
|
26
27
|
: operation === "swarm-write"
|
|
@@ -41,7 +42,7 @@ export function companionArgv(operation, prompt, companionPath, nodeBin = proces
|
|
|
41
42
|
prompt,
|
|
42
43
|
];
|
|
43
44
|
}
|
|
44
|
-
export function companionEnvironment(dataRoot, cwd, env, nodeBin = process.execPath) {
|
|
45
|
+
export function companionEnvironment(dataRoot, cwd, env, nodeBin = preferStableNodePath(process.execPath)) {
|
|
45
46
|
return {
|
|
46
47
|
...env,
|
|
47
48
|
KIMI_PLUGIN_CC_DATA: dataRoot,
|
|
@@ -79,8 +80,9 @@ export async function startCompanionRun(request, operation, spec) {
|
|
|
79
80
|
request.signal.addEventListener("abort", onAbort, { once: true });
|
|
80
81
|
let child;
|
|
81
82
|
try {
|
|
83
|
+
const nodeBin = preferStableNodePath(process.execPath);
|
|
82
84
|
child = spec.spawn({
|
|
83
|
-
argv: companionArgv(operation, prompt, spec.companionPath,
|
|
85
|
+
argv: companionArgv(operation, prompt, spec.companionPath, nodeBin, spec),
|
|
84
86
|
cwd,
|
|
85
87
|
stdio: {
|
|
86
88
|
stdin: "ignore",
|
|
@@ -89,7 +91,7 @@ export async function startCompanionRun(request, operation, spec) {
|
|
|
89
91
|
},
|
|
90
92
|
graceMs: spec.disposeGraceMs,
|
|
91
93
|
signal: termination.signal,
|
|
92
|
-
env: companionEnvironment(spec.dataRoot, cwd, spec.env),
|
|
94
|
+
env: companionEnvironment(spec.dataRoot, cwd, spec.env, nodeBin),
|
|
93
95
|
});
|
|
94
96
|
}
|
|
95
97
|
catch (error) {
|
|
@@ -141,8 +143,9 @@ export async function startCompanionRun(request, operation, spec) {
|
|
|
141
143
|
});
|
|
142
144
|
}
|
|
143
145
|
export async function runCompanionCommand(argv, cwd, spec, signal) {
|
|
146
|
+
const nodeBin = preferStableNodePath(process.execPath);
|
|
144
147
|
const child = spec.spawn({
|
|
145
|
-
argv: [
|
|
148
|
+
argv: [nodeBin, spec.companionPath, ...argv],
|
|
146
149
|
cwd,
|
|
147
150
|
stdio: {
|
|
148
151
|
stdin: "ignore",
|
|
@@ -151,7 +154,7 @@ export async function runCompanionCommand(argv, cwd, spec, signal) {
|
|
|
151
154
|
},
|
|
152
155
|
graceMs: spec.disposeGraceMs,
|
|
153
156
|
signal,
|
|
154
|
-
env: companionEnvironment(spec.dataRoot, cwd, spec.env),
|
|
157
|
+
env: companionEnvironment(spec.dataRoot, cwd, spec.env, nodeBin),
|
|
155
158
|
});
|
|
156
159
|
try {
|
|
157
160
|
const outcome = await child.done;
|
package/dist/dsh/index.js
CHANGED
|
@@ -8,6 +8,12 @@ import { DSH_COMPANION_OPERATIONS, runCompanionCommand, startCompanionRun, } fro
|
|
|
8
8
|
import { parseDurationMs } from "../parsing.js";
|
|
9
9
|
export const name = "kimi-plugin-cc";
|
|
10
10
|
export const inject = ["subagents", "subprocess"];
|
|
11
|
+
export const DSH_TOOL_GUIDANCE = [
|
|
12
|
+
"Kimi delegation tools have different write boundaries:",
|
|
13
|
+
"kimi_ask, kimi_review, and kimi_challenge are read-only; kimi_swarm is a read-only parallel review.",
|
|
14
|
+
"kimi_rescue and kimi_pursue can edit the selected workspace; kimi_pursue continues toward a bounded goal.",
|
|
15
|
+
"kimi_swarm_write edits a temporary worktree and returns a patch without applying it.",
|
|
16
|
+
].join(" ");
|
|
11
17
|
const DEFAULT_DISPOSE_GRACE_MS = 6_000;
|
|
12
18
|
const DEFAULT_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
13
19
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
@@ -50,8 +56,8 @@ function resolveConfig(config) {
|
|
|
50
56
|
if (!path.isAbsolute(resolved.dataRoot)) {
|
|
51
57
|
throw new Error("kimi-plugin-cc: dataRoot must be absolute");
|
|
52
58
|
}
|
|
53
|
-
if (!Number.isFinite(resolved.disposeGraceMs) || resolved.disposeGraceMs
|
|
54
|
-
throw new Error(`kimi-plugin-cc: disposeGraceMs must be
|
|
59
|
+
if (!Number.isFinite(resolved.disposeGraceMs) || resolved.disposeGraceMs < DEFAULT_DISPOSE_GRACE_MS || resolved.disposeGraceMs > MAX_TIMER_DELAY_MS) {
|
|
60
|
+
throw new Error(`kimi-plugin-cc: disposeGraceMs must be at least ${DEFAULT_DISPOSE_GRACE_MS} and no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
55
61
|
}
|
|
56
62
|
if (!Number.isSafeInteger(resolved.maxOutputBytes) || resolved.maxOutputBytes <= 0) {
|
|
57
63
|
throw new Error("kimi-plugin-cc: maxOutputBytes must be a positive safe integer");
|
|
@@ -105,6 +111,13 @@ export function apply(ctx, config) {
|
|
|
105
111
|
for (const operation of DSH_COMPANION_OPERATIONS) {
|
|
106
112
|
ctx.subagents.registerProvider(new KimiCompanionProvider(`kimi-${operation}`, operation, spec));
|
|
107
113
|
}
|
|
114
|
+
ctx.inject(["systemPrompt"], (promptCtx) => {
|
|
115
|
+
promptCtx.systemPrompt.section({
|
|
116
|
+
name: "kimi-plugin-cc:tool-boundaries",
|
|
117
|
+
order: promptCtx.systemPrompt.getSectionOrder("TOOL_SUBAGENT"),
|
|
118
|
+
text: () => DSH_TOOL_GUIDANCE,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
108
121
|
ctx.inject(["commands"], (commandCtx) => {
|
|
109
122
|
const execute = async (invocation, argv) => {
|
|
110
123
|
const directories = commandCtx.get("workingDirectory");
|