kimi-plugin-cc 2.4.5 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +42 -26
- package/dist/dsh/companion.js +174 -0
- package/dist/dsh/index.js +156 -0
- package/dist/hooks/install-paths.js +2 -2
- package/dist/version.js +1 -1
- package/docs/models.md +5 -1
- package/docs/registry-releases.md +20 -17
- package/docs/safety.md +22 -9
- package/docs/upstream-2.4.1-certification.md +6 -0
- package/package.json +40 -6
- package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/kimi-codex/dist/dsh/companion.js +174 -0
- package/plugins/kimi-codex/dist/dsh/index.js +156 -0
- package/plugins/kimi-codex/dist/hooks/install-paths.js +2 -2
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-dsh/LICENSE +201 -0
- package/plugins/kimi-dsh/README.md +80 -0
- package/plugins/kimi-dsh/cordis.patch.yml +45 -0
- package/plugins/kimi-dsh/dist/background-spawn.js +216 -0
- package/plugins/kimi-dsh/dist/cli-cancellation.js +43 -0
- package/plugins/kimi-dsh/dist/cli-client.js +876 -0
- package/plugins/kimi-dsh/dist/commands/ask.js +336 -0
- package/plugins/kimi-dsh/dist/commands/cancel.js +216 -0
- package/plugins/kimi-dsh/dist/commands/cli-helpers.js +201 -0
- package/plugins/kimi-dsh/dist/commands/models.js +140 -0
- package/plugins/kimi-dsh/dist/commands/not-implemented.js +4 -0
- package/plugins/kimi-dsh/dist/commands/pursue.js +307 -0
- package/plugins/kimi-dsh/dist/commands/recover-swarm.js +135 -0
- package/plugins/kimi-dsh/dist/commands/registry.js +74 -0
- package/plugins/kimi-dsh/dist/commands/repair-sessions.js +160 -0
- package/plugins/kimi-dsh/dist/commands/replay.js +124 -0
- package/plugins/kimi-dsh/dist/commands/rescue.js +347 -0
- package/plugins/kimi-dsh/dist/commands/result.js +66 -0
- package/plugins/kimi-dsh/dist/commands/review-gate.js +390 -0
- package/plugins/kimi-dsh/dist/commands/review.js +220 -0
- package/plugins/kimi-dsh/dist/commands/setup.js +1047 -0
- package/plugins/kimi-dsh/dist/commands/status.js +27 -0
- package/plugins/kimi-dsh/dist/commands/swarm.js +754 -0
- package/plugins/kimi-dsh/dist/commands/worker.js +25 -0
- package/plugins/kimi-dsh/dist/companion.js +97 -0
- package/plugins/kimi-dsh/dist/config.js +22 -0
- package/plugins/kimi-dsh/dist/dsh/companion.js +174 -0
- package/plugins/kimi-dsh/dist/dsh/index.js +156 -0
- package/plugins/kimi-dsh/dist/errors.js +40 -0
- package/plugins/kimi-dsh/dist/git.js +224 -0
- package/plugins/kimi-dsh/dist/help.js +43 -0
- package/plugins/kimi-dsh/dist/hook-escape.js +71 -0
- package/plugins/kimi-dsh/dist/hooks/approval-hook.js +101 -0
- package/plugins/kimi-dsh/dist/hooks/approval-policy.js +211 -0
- package/plugins/kimi-dsh/dist/hooks/config-safety.js +653 -0
- package/plugins/kimi-dsh/dist/hooks/install-paths.js +428 -0
- package/plugins/kimi-dsh/dist/hooks/install.js +210 -0
- package/plugins/kimi-dsh/dist/hooks/managed-block.js +619 -0
- package/plugins/kimi-dsh/dist/hooks/review-gate-stop.js +19 -0
- package/plugins/kimi-dsh/dist/job-id.js +12 -0
- package/plugins/kimi-dsh/dist/job-store.js +659 -0
- package/plugins/kimi-dsh/dist/jobs.js +217 -0
- package/plugins/kimi-dsh/dist/kimi-command.js +70 -0
- package/plugins/kimi-dsh/dist/kimi-engine.js +632 -0
- package/plugins/kimi-dsh/dist/kimi-errors.js +260 -0
- package/plugins/kimi-dsh/dist/kimi-home.js +9 -0
- package/plugins/kimi-dsh/dist/kimi-timeouts.js +52 -0
- package/plugins/kimi-dsh/dist/kimi-version-probe.js +216 -0
- package/plugins/kimi-dsh/dist/logging.js +22 -0
- package/plugins/kimi-dsh/dist/mcp/binding.js +58 -0
- package/plugins/kimi-dsh/dist/mcp/job-control.js +345 -0
- package/plugins/kimi-dsh/dist/mcp/launcher.js +257 -0
- package/plugins/kimi-dsh/dist/mcp/operations.js +301 -0
- package/plugins/kimi-dsh/dist/mcp/protocol.js +275 -0
- package/plugins/kimi-dsh/dist/mcp/server.js +8 -0
- package/plugins/kimi-dsh/dist/mcp/stdio.js +78 -0
- package/plugins/kimi-dsh/dist/mcp/supervised-child.js +15 -0
- package/plugins/kimi-dsh/dist/mcp/tool-result.js +7 -0
- package/plugins/kimi-dsh/dist/mcp/workspace.js +34 -0
- package/plugins/kimi-dsh/dist/model-command.js +32 -0
- package/plugins/kimi-dsh/dist/native-v2-preflight.js +417 -0
- package/plugins/kimi-dsh/dist/package.json +1 -0
- package/plugins/kimi-dsh/dist/parsing.js +620 -0
- package/plugins/kimi-dsh/dist/paths.js +27 -0
- package/plugins/kimi-dsh/dist/plugin-data.js +98 -0
- package/plugins/kimi-dsh/dist/process-tree.js +479 -0
- package/plugins/kimi-dsh/dist/prompt-size.js +18 -0
- package/plugins/kimi-dsh/dist/render.js +177 -0
- package/plugins/kimi-dsh/dist/rescue-approval.js +865 -0
- package/plugins/kimi-dsh/dist/schemas/review-gate-output.js +43 -0
- package/plugins/kimi-dsh/dist/session-recovery.js +200 -0
- package/plugins/kimi-dsh/dist/session-title.js +293 -0
- package/plugins/kimi-dsh/dist/stream-json.js +346 -0
- package/plugins/kimi-dsh/dist/swarm-activation.js +834 -0
- package/plugins/kimi-dsh/dist/swarm-recovery.js +134 -0
- package/plugins/kimi-dsh/dist/types.js +1 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/LICENSE +24 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/index.js +1 -0
- package/plugins/kimi-dsh/dist/vendor/shell-quote/parse.js +229 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/LICENSE +24 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/date.js +156 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/error.js +67 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/extract.js +293 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/parse.js +164 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/primitive.js +151 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/struct.js +156 -0
- package/plugins/kimi-dsh/dist/vendor/smol-toml/util.js +57 -0
- package/plugins/kimi-dsh/dist/version.js +7 -0
- package/plugins/kimi-dsh/package.json +48 -0
- package/plugins/kimi-dsh/scripts/companion.sh +37 -0
- package/plugins/kimi-dsh/scripts/review-gate-hook.sh +32 -0
package/docs/safety.md
CHANGED
|
@@ -78,20 +78,23 @@ Key constraints (verified by [`runtime/hooks/managed-block.ts`](../runtime/hooks
|
|
|
78
78
|
7. **Serialized, identity-bound setup mutations.** Install and uninstall hold a private `0o600` sibling lock across the complete read-modify-write transaction. The candidate lock is published atomically; lock reads are no-follow, nonblocking, regular-file-only, and capped at 4 KiB. Acquisition is bounded, live owners are never stolen, stale recovery is guarded by a nonrecursive recovery lease, and inode/token ownership is rechecked before config mutation or lock removal. Simultaneous Claude Code/Codex setup runs therefore cannot lose either host's block, and a replaced lock is never treated as ours. A newly created Kimi home is `0o700`; atomic config writes remain `0o600`.
|
|
79
79
|
8. **Inline hook arrays are normalized before insertion.** kimi-code accepts both `hooks = [...]` and `[[hooks]]`, but appending a managed array-of-tables block after an inline assignment can produce a conflicting document. Setup converts a valid inline hook array to canonical `[[hooks]]` tables before inserting the host block and emits an explicit warning that formatting and comments inside that assignment are normalized. Surrounding config bytes are preserved.
|
|
80
80
|
|
|
81
|
-
### Host scoping (
|
|
81
|
+
### Host scoping (all hosts share one config)
|
|
82
82
|
|
|
83
83
|
Claude Code and Codex install this plugin to **different, version-stamped
|
|
84
|
-
paths** (`~/.claude/plugins/cache/…` vs `~/.codex/plugins/cache/…`)
|
|
85
|
-
|
|
84
|
+
paths** (`~/.claude/plugins/cache/…` vs `~/.codex/plugins/cache/…`), while DSH
|
|
85
|
+
uses its own profile package path. All read the **same**
|
|
86
|
+
`~/.kimi-code/config.toml`. So the managed block is **host-scoped**:
|
|
86
87
|
its marker carries a `:<host-id>` suffix, and each host owns, verifies, and
|
|
87
88
|
uninstalls only its **own** block.
|
|
88
89
|
|
|
89
90
|
- **Host id** (`runtime/hooks/install-paths.ts::resolveHostId`) is derived
|
|
90
91
|
version-independently from the hook path: `~/.claude/…` → `claude-code`,
|
|
91
|
-
`~/.codex/…` → `codex`, else a stable `host-<sha1>` for dev checkouts.
|
|
92
|
-
plugin upgrade *refreshes* the same host's block
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
`~/.codex/…` → `codex`, else a stable `host-<sha1>` for dev checkouts. DSH
|
|
93
|
+
explicitly pins `dsh`. A plugin upgrade *refreshes* the same host's block
|
|
94
|
+
rather than adding a new one.
|
|
95
|
+
- **No clobbering.** `/kimi:setup` (Claude), `$kimi-setup` (Codex), and
|
|
96
|
+
`/kimi-setup` (DSH) each write their own block and leave the others
|
|
97
|
+
byte-identical. Before v1.7.0 there was
|
|
95
98
|
a single block exact-matched to the running host, so the two hosts overwrote
|
|
96
99
|
each other and the loser's write-commands refused with "hook path drift."
|
|
97
100
|
- **Enforcement is redundant-safe.** kimi-code fires *every* `[[hooks]]` entry
|
|
@@ -103,8 +106,8 @@ uninstalls only its **own** block.
|
|
|
103
106
|
- **Migration + cleanup.** A pre-v1.7.0 un-suffixed block is adopted and
|
|
104
107
|
converted in place by the current host on its next `/kimi:setup`. Orphaned,
|
|
105
108
|
marker-less `[[hooks]]` entries that are unambiguously our approval hook
|
|
106
|
-
(canonical command, `approval-hook.js` under a kimi-marketplace/kimi-plugin-cc
|
|
107
|
-
tree) are pruned **host-scoped** (v1.8.2): install and scoped uninstall touch
|
|
109
|
+
(canonical command, `approval-hook.js` under a kimi-marketplace/kimi-plugin-cc/
|
|
110
|
+
dsh-kimi-plugin-cc tree) are pruned **host-scoped** (v1.8.2): install and scoped uninstall touch
|
|
108
111
|
only tables whose command path derives to the *current* host. `/kimi:setup
|
|
109
112
|
--uninstall` removes only the current host's block (plus legacy + this host's
|
|
110
113
|
orphans); `/kimi:setup --uninstall --all` removes every host's block and every
|
|
@@ -123,6 +126,16 @@ uninstalls only its **own** block.
|
|
|
123
126
|
hook and is left byte-untouched (pre-v1.8.2, the host-blind prune here caused
|
|
124
127
|
each host's setup to disarm the other after any kimi login — the "seesaw").
|
|
125
128
|
|
|
129
|
+
## DeepSeek Harness providers
|
|
130
|
+
|
|
131
|
+
The DSH bundle exposes the same seven operations as the other hosts: `kimi_ask`, `kimi_review`, `kimi_challenge`, `kimi_rescue`, `kimi_pursue`, `kimi_swarm`, and `kimi_swarm_write`. Each activation starts the existing companion through DSH's managed subprocess service and uses the same `kimi -p` transport, hook verification, exact-version certification, job store, and response or safety budgets. ACP is not used.
|
|
132
|
+
|
|
133
|
+
Run `/kimi-setup` explicitly before delegating. Installing or enabling the bundle does not modify Kimi configuration. DSH uses host id `dsh` and `$DSH_HOME/plugin-data/kimi-plugin-cc` unless an absolute data-root override selects another store. Its provider-owned environment clears other-host roots/data, hook/script overrides, trusted roots, and verification bypasses instead of inheriting them.
|
|
134
|
+
|
|
135
|
+
The parent receives only complete successful stdout. stderr remains on the host, and nonzero, empty, or truncated output becomes a bounded diagnostic rather than partial prose. Cancellation terminates the managed process range and waits for quiescence. DSH activation ids are distinct from companion job ids; use `/kimi-status`, `/kimi-result`, or `/kimi-cancel` for the persisted job.
|
|
136
|
+
|
|
137
|
+
Write boundaries are identical to the companion commands: rescue and pursue edit the real selected workspace, ask/review/challenge and read-only swarm deny writes and shell commands, and swarm-write works from born HEAD in a temporary worktree and returns a patch without applying or committing it. `/kimi-setup --uninstall` removes only the DSH managed block; the all-host uninstall flag is not exposed.
|
|
138
|
+
|
|
126
139
|
## The hook's per-command policy
|
|
127
140
|
|
|
128
141
|
[`runtime/hooks/approval-policy.ts`](../runtime/hooks/approval-policy.ts) is the single source of truth:
|
|
@@ -43,6 +43,12 @@ Controls used a task-authorized dedicated Agent Key in process memory/environmen
|
|
|
43
43
|
- Codex's targeted re-review requested genuine error results, legacy-compatible turn starts, and a whole-workspace settlement assertion. A subsequent review added marker-prefix, call-derived turn-ID, and directory-entry requirements. The final production matrix (`production-prefix-smoke.log`) passed **24 tests and 167 assertions with no skips in 440.66 seconds**. All seven call-derived turn IDs had same-call Write denials with `isError: true` and the managed-message prefix, and the entire workspace was empty at settlement.
|
|
44
44
|
- Literal standard `bun run check` (`check-standard-final.log`): **1,318 pass, 30 expected opt-in skips, 0 fail, 17,319 assertions across 79 files**, 358.83 seconds. Earlier parallel attempts hit timing-sensitive lifecycle failures; the focused suites and a complete serial run passed before the literal standard gate also passed. Codex's final targeted re-review returned **APPROVE**, closing all original and subsequent findings. Final edited-byte package validation (`package-standard-final.log`) passed **233 files** and includes this record.
|
|
45
45
|
|
|
46
|
+
## Release and publication
|
|
47
|
+
|
|
48
|
+
Plugin 2.4.5 was committed as `de8c195c2986443a088b674717133b164d2e4ef2` and pushed to `main`. [Release-commit CI 41226096281](https://github.com/linxule/kimi-plugin-cc/actions/runs/41226096281) passed the full check/package and Windows spawn jobs. Annotated tag `v2.4.5` and [GitHub release](https://github.com/linxule/kimi-plugin-cc/releases/tag/v2.4.5) were created. [Publish workflow 41226438326](https://github.com/linxule/kimi-plugin-cc/actions/runs/41226438326) passed its build and npm publish jobs.
|
|
49
|
+
|
|
50
|
+
The registry tarball has SHA-1 `39f97a6de4b26ad51ac77305054682fcd5cd0164`, contains 233 files, and matches the CI artifact byte-for-byte. Fresh npm registry installations passed isolated setup/check and unsupported-version refusal for both hosts, and the compiled mirrors are byte-identical to the release checkout. Structured proof is retained at `.claude/kimi-code-research/release-2.4.5/registry-verification.json`. Installed hosts and operator CLI pins remain unchanged.
|
|
51
|
+
|
|
46
52
|
## Preserved boundaries
|
|
47
53
|
|
|
48
54
|
Certification remains exact and per-operation. Legacy-v1 ends at 0.41.x. Plan mode and unsafe experimental selectors remain refused. Hook verification, trusted-root confinement, concurrency/defaults, one print runner, memory-only web tokens, finite budgets, process/session identity checks, deactivation, and recovery ownership are unchanged. Existing Git trust and hook-process failure residuals remain documented; this release does not make the hook an OS sandbox. Publication does not install or re-pin either host.
|
package/package.json
CHANGED
|
@@ -1,30 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimi-plugin-cc",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Kimi for Claude Code and
|
|
3
|
+
"version": "2.5.1",
|
|
4
|
+
"description": "Kimi for Claude Code, Codex, and DeepSeek Harness: code questions, independent reviews, bounded fixes, autonomous goals, parallel tasks, and MCP tools through the local Kimi Code CLI.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "dist/dsh/index.js",
|
|
6
7
|
"engines": {
|
|
7
|
-
"node": ">=22.5.0"
|
|
8
|
+
"node": ">=22.5.0",
|
|
9
|
+
"dsh": ">=0.2.1-rc.5 <0.3.0"
|
|
10
|
+
},
|
|
11
|
+
"dsh": {
|
|
12
|
+
"bundle": {
|
|
13
|
+
"patch": "./plugins/kimi-dsh/cordis.patch.yml"
|
|
14
|
+
}
|
|
8
15
|
},
|
|
9
16
|
"scripts": {
|
|
10
|
-
"check": "bun run build && bun run check:contracts && bun run check:surfaces && bunx tsc --noEmit && bun test && git diff --exit-code -- dist plugins/kimi-codex && UNTRACKED=\"$(git ls-files --others --exclude-standard -- dist plugins/kimi-codex)\" && { [ -z \"$UNTRACKED\" ] || { echo \"drift gate: untracked dist/codex files (stage them and re-run check):\"; echo \"$UNTRACKED\"; exit 1; }; }",
|
|
17
|
+
"check": "bun run build && bun run check:contracts && bun run check:surfaces && bunx tsc --noEmit && bun test && git diff --exit-code -- dist plugins/kimi-codex plugins/kimi-dsh && UNTRACKED=\"$(git ls-files --others --exclude-standard -- dist plugins/kimi-codex plugins/kimi-dsh)\" && { [ -z \"$UNTRACKED\" ] || { echo \"drift gate: untracked dist/codex/dsh files (stage them and re-run check):\"; echo \"$UNTRACKED\"; exit 1; }; }",
|
|
11
18
|
"check:surfaces": "bun run scripts/generate-surfaces.ts --check",
|
|
12
19
|
"generate:surfaces": "bun run scripts/generate-surfaces.ts",
|
|
13
20
|
"build": "rm -rf dist && bunx tsc -p tsconfig.build.json && mkdir -p dist/vendor/shell-quote dist/vendor/smol-toml && cp runtime/vendor/shell-quote/LICENSE dist/vendor/shell-quote/LICENSE && cp runtime/vendor/smol-toml/*.js runtime/vendor/smol-toml/LICENSE dist/vendor/smol-toml/ && echo '{\"type\":\"module\"}' > dist/package.json",
|
|
14
21
|
"companion": "node --import tsx runtime/companion.ts",
|
|
15
22
|
"test": "bun test",
|
|
16
23
|
"smoke:real": "KIMI_PLUGIN_CC_SMOKE=1 bun test tests/runtime/real-binary-smoke.test.ts --path-ignore-patterns '**/worktrees/**'",
|
|
17
|
-
"check:package": "node scripts/check-package.mjs",
|
|
24
|
+
"check:package": "node scripts/check-package.mjs && node scripts/check-dsh-package.mjs",
|
|
18
25
|
"check:contracts": "node scripts/check-contract-size.mjs"
|
|
19
26
|
},
|
|
20
27
|
"devDependencies": {
|
|
28
|
+
"@deepseek-ai/cordis": "4.0.5",
|
|
29
|
+
"@deepseek-ai/dsh-commands": "0.2.1-rc.5",
|
|
30
|
+
"@deepseek-ai/dsh-llm": "0.2.1-rc.5",
|
|
31
|
+
"@deepseek-ai/dsh-session": "0.2.1-rc.5",
|
|
32
|
+
"@deepseek-ai/dsh-subagent": "0.2.1-rc.5",
|
|
33
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-rc.5",
|
|
34
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-rc.5",
|
|
21
35
|
"@types/bun": "^1.4.2",
|
|
22
36
|
"@types/node": "^26.5.1",
|
|
23
37
|
"smol-toml": "1.9.1",
|
|
24
38
|
"tsx": "^4.23.13",
|
|
25
39
|
"typescript": "^7.0.2"
|
|
26
40
|
},
|
|
27
|
-
"dependencies": {
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@deepseek-ai/dsh-brand": "0.2.1-rc.5",
|
|
43
|
+
"@deepseek-ai/dsh-tool-subagent": "0.2.1-rc.5",
|
|
44
|
+
"@deepseek-ai/schemastery": "3.18.5"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@deepseek-ai/cordis": "~4.0.5",
|
|
48
|
+
"@deepseek-ai/dsh-commands": ">=0.2.1-rc.5 <0.3.0",
|
|
49
|
+
"@deepseek-ai/dsh-llm": ">=0.2.1-rc.5 <0.3.0",
|
|
50
|
+
"@deepseek-ai/dsh-session": ">=0.2.1-rc.5 <0.3.0",
|
|
51
|
+
"@deepseek-ai/dsh-subagent": ">=0.2.1-rc.5 <0.3.0",
|
|
52
|
+
"@deepseek-ai/dsh-subprocess": ">=0.2.1-rc.5 <0.3.0",
|
|
53
|
+
"@deepseek-ai/dsh-working-directory": ">=0.2.1-rc.5 <0.3.0"
|
|
54
|
+
},
|
|
55
|
+
"overrides": {
|
|
56
|
+
"zod": "^4.6.6",
|
|
57
|
+
"undici": "^8.12.2"
|
|
58
|
+
},
|
|
28
59
|
"license": "Apache-2.0",
|
|
29
60
|
"author": "Xule Lin",
|
|
30
61
|
"repository": {
|
|
@@ -38,6 +69,8 @@
|
|
|
38
69
|
"keywords": [
|
|
39
70
|
"claude-code",
|
|
40
71
|
"codex",
|
|
72
|
+
"deepseek-harness",
|
|
73
|
+
"dsh",
|
|
41
74
|
"kimi",
|
|
42
75
|
"kimi-code",
|
|
43
76
|
"plugin",
|
|
@@ -58,6 +91,7 @@
|
|
|
58
91
|
"hooks/",
|
|
59
92
|
"dist/",
|
|
60
93
|
"plugins/kimi-codex/",
|
|
94
|
+
"plugins/kimi-dsh/",
|
|
61
95
|
"scripts/companion.sh",
|
|
62
96
|
"scripts/review-gate-hook.sh",
|
|
63
97
|
"docs/safety.md",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Kimi for Codex: code questions, independent reviews, bounded fixes, autonomous goals, parallel tasks, and MCP job controls through the local Kimi Code CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "linxule"
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
3
|
+
import { assertUsableCwd, settleRunResult, subprocessRunHandle, } from "@deepseek-ai/dsh-subagent";
|
|
4
|
+
export const DSH_COMPANION_OPERATIONS = [
|
|
5
|
+
"ask",
|
|
6
|
+
"review",
|
|
7
|
+
"challenge",
|
|
8
|
+
"rescue",
|
|
9
|
+
"pursue",
|
|
10
|
+
"swarm",
|
|
11
|
+
"swarm-write",
|
|
12
|
+
];
|
|
13
|
+
export function companionTextTask(prompt) {
|
|
14
|
+
if (prompt.length === 0 || prompt.some((block) => block.type !== "text")) {
|
|
15
|
+
throw new Error("kimi-plugin-cc: a delegation must contain only text blocks");
|
|
16
|
+
}
|
|
17
|
+
const text = prompt.map((block) => block.type === "text" ? block.text : "").join("\n\n");
|
|
18
|
+
if (text.trim().length === 0) {
|
|
19
|
+
throw new Error("kimi-plugin-cc: a delegation must not be empty");
|
|
20
|
+
}
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
export function companionArgv(operation, prompt, companionPath, nodeBin = process.execPath, options = {}) {
|
|
24
|
+
const command = operation === "ask" || operation === "review"
|
|
25
|
+
? [operation]
|
|
26
|
+
: operation === "swarm-write"
|
|
27
|
+
? ["task", "swarm", "--write"]
|
|
28
|
+
: ["task", operation];
|
|
29
|
+
return [
|
|
30
|
+
nodeBin,
|
|
31
|
+
companionPath,
|
|
32
|
+
...command,
|
|
33
|
+
...(operation === "ask" || operation === "rescue" ? ["--fresh"] : []),
|
|
34
|
+
...(options.model === undefined ? [] : ["-m", options.model]),
|
|
35
|
+
...(options.thinkingEffort === undefined ? [] : ["--thinking", options.thinkingEffort]),
|
|
36
|
+
...(options.budget === undefined ? [] : ["--budget", options.budget]),
|
|
37
|
+
...((operation === "review" || operation === "challenge") && options.reviewBase !== undefined
|
|
38
|
+
? ["--base", options.reviewBase]
|
|
39
|
+
: []),
|
|
40
|
+
"--",
|
|
41
|
+
prompt,
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
export function companionEnvironment(dataRoot, cwd, env, nodeBin = process.execPath) {
|
|
45
|
+
return {
|
|
46
|
+
...env,
|
|
47
|
+
KIMI_PLUGIN_CC_DATA: dataRoot,
|
|
48
|
+
KIMI_PLUGIN_CC_HOST_ID: "dsh",
|
|
49
|
+
KIMI_PLUGIN_CC_NODE_BIN: nodeBin,
|
|
50
|
+
KIMI_PLUGIN_CC_WORKSPACE_CWD: cwd,
|
|
51
|
+
CLAUDE_PLUGIN_DATA: undefined,
|
|
52
|
+
PLUGIN_DATA: undefined,
|
|
53
|
+
CLAUDE_PLUGIN_ROOT: undefined,
|
|
54
|
+
PLUGIN_ROOT: undefined,
|
|
55
|
+
KIMI_PLUGIN_CC_SHELL_LAUNCH: undefined,
|
|
56
|
+
KIMI_PLUGIN_CC_MCP_WORKSPACE: undefined,
|
|
57
|
+
KIMI_PLUGIN_CC_WORKSPACE_ROOT: undefined,
|
|
58
|
+
KIMI_PLUGIN_CC_HOOK_SCRIPT: undefined,
|
|
59
|
+
KIMI_PLUGIN_CC_SKIP_HOOK_CHECK: undefined,
|
|
60
|
+
KIMI_PLUGIN_CC_SKIP_VERSION_PROBE: undefined,
|
|
61
|
+
KIMI_CODE_TRUST_WORKSPACE: undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export async function startCompanionRun(request, operation, spec) {
|
|
65
|
+
const prompt = companionTextTask(request.prompt);
|
|
66
|
+
if (request.signal.aborted) {
|
|
67
|
+
throw new Error("kimi-plugin-cc: delegation was aborted before companion startup");
|
|
68
|
+
}
|
|
69
|
+
const cwd = assertUsableCwd("kimi-plugin-cc", "child cwd", request.cwd ?? process.cwd());
|
|
70
|
+
const termination = new AbortController();
|
|
71
|
+
let cancelled = false;
|
|
72
|
+
const requestCancel = () => {
|
|
73
|
+
cancelled = true;
|
|
74
|
+
if (!termination.signal.aborted) {
|
|
75
|
+
termination.abort(new Error("kimi-plugin-cc: delegation cancelled"));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const onAbort = () => requestCancel();
|
|
79
|
+
request.signal.addEventListener("abort", onAbort, { once: true });
|
|
80
|
+
let child;
|
|
81
|
+
try {
|
|
82
|
+
child = spec.spawn({
|
|
83
|
+
argv: companionArgv(operation, prompt, spec.companionPath, process.execPath, spec),
|
|
84
|
+
cwd,
|
|
85
|
+
stdio: {
|
|
86
|
+
stdin: "ignore",
|
|
87
|
+
stdout: { maxBytes: spec.maxOutputBytes },
|
|
88
|
+
stderr: "inherit",
|
|
89
|
+
},
|
|
90
|
+
graceMs: spec.disposeGraceMs,
|
|
91
|
+
signal: termination.signal,
|
|
92
|
+
env: companionEnvironment(spec.dataRoot, cwd, spec.env),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
request.signal.removeEventListener("abort", onAbort);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
let diagnostic;
|
|
100
|
+
const result = settleRunResult({
|
|
101
|
+
attempt: async () => {
|
|
102
|
+
let outcome;
|
|
103
|
+
try {
|
|
104
|
+
outcome = await child.done;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
diagnostic = `Kimi ${operation} companion failed before producing a terminal exit`;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
const collected = child.collected.stdout?.readFrom(0);
|
|
111
|
+
const text = collected?.text.trimEnd() ?? "";
|
|
112
|
+
if (!cancelled && outcome.exitCode === 0 && collected && !collected.lossy && text.trim().length > 0) {
|
|
113
|
+
return { output: [{ type: "text", text }], stopReason: "completed" };
|
|
114
|
+
}
|
|
115
|
+
const facts = [
|
|
116
|
+
`exit code: ${outcome.exitCode ?? "none"}`,
|
|
117
|
+
`signal: ${outcome.signal ?? "none"}`,
|
|
118
|
+
...(collected?.lossy ? ["stdout truncated"] : []),
|
|
119
|
+
...(text.trim().length === 0 ? ["stdout empty"] : []),
|
|
120
|
+
];
|
|
121
|
+
diagnostic = `Kimi ${operation} companion failed (${facts.join("; ")})`;
|
|
122
|
+
return { output: [], diagnostic, stopReason: cancelled ? "aborted" : "error" };
|
|
123
|
+
},
|
|
124
|
+
collectOutput: () => [],
|
|
125
|
+
collectDiagnostic: () => diagnostic,
|
|
126
|
+
cancelled: () => cancelled,
|
|
127
|
+
signal: request.signal,
|
|
128
|
+
onAbort,
|
|
129
|
+
});
|
|
130
|
+
return subprocessRunHandle({
|
|
131
|
+
id: brandString(randomUUID()),
|
|
132
|
+
result,
|
|
133
|
+
signal: request.signal,
|
|
134
|
+
onAbort,
|
|
135
|
+
requestCancel,
|
|
136
|
+
teardown: async () => {
|
|
137
|
+
child.terminate();
|
|
138
|
+
await child.waitForExit();
|
|
139
|
+
await child.done.catch(() => { });
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export async function runCompanionCommand(argv, cwd, spec, signal) {
|
|
144
|
+
const child = spec.spawn({
|
|
145
|
+
argv: [process.execPath, spec.companionPath, ...argv],
|
|
146
|
+
cwd,
|
|
147
|
+
stdio: {
|
|
148
|
+
stdin: "ignore",
|
|
149
|
+
stdout: { maxBytes: spec.maxOutputBytes },
|
|
150
|
+
stderr: { maxBytes: spec.maxOutputBytes },
|
|
151
|
+
},
|
|
152
|
+
graceMs: spec.disposeGraceMs,
|
|
153
|
+
signal,
|
|
154
|
+
env: companionEnvironment(spec.dataRoot, cwd, spec.env),
|
|
155
|
+
});
|
|
156
|
+
try {
|
|
157
|
+
const outcome = await child.done;
|
|
158
|
+
await child.waitForExit();
|
|
159
|
+
const stdout = child.collected.stdout?.readFrom(0);
|
|
160
|
+
const stderr = child.collected.stderr?.readFrom(0);
|
|
161
|
+
return {
|
|
162
|
+
exitCode: outcome.exitCode,
|
|
163
|
+
signal: outcome.signal,
|
|
164
|
+
stdout: stdout?.text ?? "",
|
|
165
|
+
stderr: stderr?.text ?? "",
|
|
166
|
+
truncated: Boolean(stdout?.lossy || stderr?.lossy),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
child.terminate();
|
|
171
|
+
await child.waitForExit().catch(() => false);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { CommandDefinitionId } from "@deepseek-ai/dsh-commands/brand";
|
|
5
|
+
import z from "@deepseek-ai/schemastery";
|
|
6
|
+
import { NO_START_CAPABILITIES, } from "@deepseek-ai/dsh-subagent";
|
|
7
|
+
import { DSH_COMPANION_OPERATIONS, runCompanionCommand, startCompanionRun, } from "./companion.js";
|
|
8
|
+
import { parseDurationMs } from "../parsing.js";
|
|
9
|
+
export const name = "kimi-plugin-cc";
|
|
10
|
+
export const inject = ["subagents", "subprocess"];
|
|
11
|
+
const DEFAULT_DISPOSE_GRACE_MS = 6_000;
|
|
12
|
+
const DEFAULT_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
13
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
14
|
+
const COMPANION_PATH = fileURLToPath(new URL("../../dist/companion.js", import.meta.url));
|
|
15
|
+
export const Config = z.object({
|
|
16
|
+
dataRoot: z.string().min(1),
|
|
17
|
+
env: z.dict(z.string()).default({}),
|
|
18
|
+
disposeGraceMs: z.number().default(DEFAULT_DISPOSE_GRACE_MS),
|
|
19
|
+
maxOutputBytes: z.number().default(DEFAULT_MAX_OUTPUT_BYTES),
|
|
20
|
+
model: z.string().min(1),
|
|
21
|
+
thinkingEffort: z.string().min(1),
|
|
22
|
+
budget: z.string().min(1),
|
|
23
|
+
reviewBase: z.string().min(1),
|
|
24
|
+
});
|
|
25
|
+
export function defaultDshDataRoot(env = process.env) {
|
|
26
|
+
const explicit = env.KIMI_PLUGIN_CC_DATA;
|
|
27
|
+
if (explicit !== undefined) {
|
|
28
|
+
if (!path.isAbsolute(explicit)) {
|
|
29
|
+
throw new Error("kimi-plugin-cc: KIMI_PLUGIN_CC_DATA must be absolute");
|
|
30
|
+
}
|
|
31
|
+
return path.normalize(explicit);
|
|
32
|
+
}
|
|
33
|
+
const dshHome = env.DSH_HOME ?? path.join(env.HOME || homedir(), ".dsh");
|
|
34
|
+
if (!path.isAbsolute(dshHome)) {
|
|
35
|
+
throw new Error("kimi-plugin-cc: DSH_HOME must be absolute");
|
|
36
|
+
}
|
|
37
|
+
return path.join(dshHome, "plugin-data", "kimi-plugin-cc");
|
|
38
|
+
}
|
|
39
|
+
function resolveConfig(config) {
|
|
40
|
+
const resolved = {
|
|
41
|
+
dataRoot: config.dataRoot ?? defaultDshDataRoot(),
|
|
42
|
+
env: config.env ?? {},
|
|
43
|
+
disposeGraceMs: config.disposeGraceMs ?? DEFAULT_DISPOSE_GRACE_MS,
|
|
44
|
+
maxOutputBytes: config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
45
|
+
model: config.model,
|
|
46
|
+
thinkingEffort: config.thinkingEffort,
|
|
47
|
+
budget: config.budget,
|
|
48
|
+
reviewBase: config.reviewBase,
|
|
49
|
+
};
|
|
50
|
+
if (!path.isAbsolute(resolved.dataRoot)) {
|
|
51
|
+
throw new Error("kimi-plugin-cc: dataRoot must be absolute");
|
|
52
|
+
}
|
|
53
|
+
if (!Number.isFinite(resolved.disposeGraceMs) || resolved.disposeGraceMs <= 0 || resolved.disposeGraceMs > MAX_TIMER_DELAY_MS) {
|
|
54
|
+
throw new Error(`kimi-plugin-cc: disposeGraceMs must be positive, finite, and no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
55
|
+
}
|
|
56
|
+
if (!Number.isSafeInteger(resolved.maxOutputBytes) || resolved.maxOutputBytes <= 0) {
|
|
57
|
+
throw new Error("kimi-plugin-cc: maxOutputBytes must be a positive safe integer");
|
|
58
|
+
}
|
|
59
|
+
for (const [field, value] of [["model", resolved.model], ["thinkingEffort", resolved.thinkingEffort], ["reviewBase", resolved.reviewBase]]) {
|
|
60
|
+
if (value?.startsWith("-"))
|
|
61
|
+
throw new Error(`kimi-plugin-cc: ${field} must not start with '-'`);
|
|
62
|
+
}
|
|
63
|
+
if (resolved.budget !== undefined)
|
|
64
|
+
parseDurationMs(resolved.budget, "--budget", "dsh.config", "safety");
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
class KimiCompanionProvider {
|
|
68
|
+
name;
|
|
69
|
+
operation;
|
|
70
|
+
spec;
|
|
71
|
+
capabilities = NO_START_CAPABILITIES;
|
|
72
|
+
inheritsParentContext = false;
|
|
73
|
+
constructor(name, operation, spec) {
|
|
74
|
+
this.name = name;
|
|
75
|
+
this.operation = operation;
|
|
76
|
+
this.spec = spec;
|
|
77
|
+
}
|
|
78
|
+
start(request) {
|
|
79
|
+
return startCompanionRun(request, this.operation, this.spec);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function commandArguments(rawInput) {
|
|
83
|
+
const trimmed = rawInput.trim();
|
|
84
|
+
return trimmed.length === 0 ? [] : trimmed.split(/\s+/);
|
|
85
|
+
}
|
|
86
|
+
function commandText(result) {
|
|
87
|
+
return [result.stdout.trimEnd(), result.stderr.trimEnd()]
|
|
88
|
+
.filter((text) => text.length > 0)
|
|
89
|
+
.join("\n");
|
|
90
|
+
}
|
|
91
|
+
export function apply(ctx, config) {
|
|
92
|
+
const resolved = resolveConfig(config);
|
|
93
|
+
const spec = {
|
|
94
|
+
companionPath: COMPANION_PATH,
|
|
95
|
+
dataRoot: resolved.dataRoot,
|
|
96
|
+
env: resolved.env,
|
|
97
|
+
disposeGraceMs: resolved.disposeGraceMs,
|
|
98
|
+
maxOutputBytes: resolved.maxOutputBytes,
|
|
99
|
+
model: resolved.model,
|
|
100
|
+
thinkingEffort: resolved.thinkingEffort,
|
|
101
|
+
budget: resolved.budget,
|
|
102
|
+
reviewBase: resolved.reviewBase,
|
|
103
|
+
spawn: (spawnSpec) => ctx.subprocess.spawn(spawnSpec),
|
|
104
|
+
};
|
|
105
|
+
for (const operation of DSH_COMPANION_OPERATIONS) {
|
|
106
|
+
ctx.subagents.registerProvider(new KimiCompanionProvider(`kimi-${operation}`, operation, spec));
|
|
107
|
+
}
|
|
108
|
+
ctx.inject(["commands"], (commandCtx) => {
|
|
109
|
+
const execute = async (invocation, argv) => {
|
|
110
|
+
const directories = commandCtx.get("workingDirectory");
|
|
111
|
+
if (directories === undefined) {
|
|
112
|
+
return { kind: "error", text: "Kimi commands require the DSH working-directory service." };
|
|
113
|
+
}
|
|
114
|
+
const cwd = await directories.ensure(invocation.agent, invocation.signal);
|
|
115
|
+
const result = await runCompanionCommand(argv, cwd, spec, invocation.signal);
|
|
116
|
+
const text = commandText(result);
|
|
117
|
+
if (result.truncated) {
|
|
118
|
+
return { kind: "error", text: `${text}\nKimi command output exceeded the configured byte limit.`.trim() };
|
|
119
|
+
}
|
|
120
|
+
if (result.exitCode !== 0) {
|
|
121
|
+
return {
|
|
122
|
+
kind: "error",
|
|
123
|
+
text: `${text}\nKimi command failed (exit code: ${result.exitCode ?? "none"}; signal: ${result.signal ?? "none"}).`.trim(),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { kind: "success", text: text.length > 0 ? text : "Kimi command completed." };
|
|
127
|
+
};
|
|
128
|
+
commandCtx.commands.register({
|
|
129
|
+
definitionId: CommandDefinitionId("kimi-plugin-cc/dsh/setup"),
|
|
130
|
+
name: "kimi-setup",
|
|
131
|
+
description: "Install, check, or remove the DSH-owned Kimi PreToolUse hook; list configured models",
|
|
132
|
+
input: { hint: "[--check|--uninstall|--models [--json]]" },
|
|
133
|
+
recordInput: false,
|
|
134
|
+
handler: (invocation) => {
|
|
135
|
+
const args = commandArguments(invocation.rawInput);
|
|
136
|
+
const valid = args.length === 0
|
|
137
|
+
|| (args.length === 1 && ["--check", "--uninstall", "--models"].includes(args[0]))
|
|
138
|
+
|| (args.length === 2 && args[0] === "--models" && args[1] === "--json");
|
|
139
|
+
if (!valid) {
|
|
140
|
+
return { kind: "error", text: "Usage: /kimi-setup [--check|--uninstall|--models [--json]]" };
|
|
141
|
+
}
|
|
142
|
+
return execute(invocation, ["setup", ...args]);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
for (const command of ["status", "result", "cancel"]) {
|
|
146
|
+
commandCtx.commands.register({
|
|
147
|
+
definitionId: CommandDefinitionId(`kimi-plugin-cc/dsh/${command}`),
|
|
148
|
+
name: `kimi-${command}`,
|
|
149
|
+
description: `${command === "cancel" ? "Cancel" : command === "status" ? "Inspect" : "Return"} a companion Kimi job`,
|
|
150
|
+
input: { hint: "[job-id]" },
|
|
151
|
+
recordInput: false,
|
|
152
|
+
handler: (invocation) => execute(invocation, [command, ...commandArguments(invocation.rawInput)]),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
@@ -410,7 +410,7 @@ export function hostIdFromHookCommand(command) {
|
|
|
410
410
|
}
|
|
411
411
|
/**
|
|
412
412
|
* True only for a canonical two-single-quoted-token command whose script is
|
|
413
|
-
* `approval-hook.js` under a kimi-plugin-cc/kimi-marketplace install tree.
|
|
413
|
+
* `approval-hook.js` under a kimi-plugin-cc/dsh-kimi-plugin-cc/kimi-marketplace install tree.
|
|
414
414
|
* This strict ownership test protects hand-rolled and non-canonical user hooks
|
|
415
415
|
* from orphan pruning.
|
|
416
416
|
*/
|
|
@@ -424,5 +424,5 @@ export function isOurApprovalHookCommand(decodedCommand) {
|
|
|
424
424
|
const normalized = parsed.hookScript.replace(/\\/g, "/");
|
|
425
425
|
// Require a real path segment, not an arbitrary substring, so a user hook in
|
|
426
426
|
// a similarly named wrapper directory is never misclassified and pruned.
|
|
427
|
-
return normalized.includes("/kimi-plugin-cc/") || normalized.includes("/kimi-marketplace/");
|
|
427
|
+
return normalized.includes("/kimi-plugin-cc/") || normalized.includes("/dsh-kimi-plugin-cc/") || normalized.includes("/kimi-marketplace/");
|
|
428
428
|
}
|