kimi-plugin-cc 2.2.2 → 2.3.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 +2 -2
- package/CHANGELOG.md +8 -0
- package/README.md +5 -5
- package/agents/kimi-challenge.md +3 -3
- package/agents/kimi-pursue.md +1 -1
- package/agents/kimi-rescue.md +2 -2
- package/agents/kimi-review.md +3 -3
- package/commands/README.md +2 -2
- package/commands/challenge.md +1 -1
- package/commands/pursue.md +1 -1
- package/commands/review.md +1 -1
- package/dist/background-spawn.js +9 -17
- package/dist/cli-client.js +13 -62
- package/dist/commands/ask.js +24 -29
- package/dist/commands/cancel.js +7 -19
- package/dist/commands/cli-helpers.js +7 -16
- package/dist/commands/pursue.js +21 -31
- package/dist/commands/registry.js +6 -19
- package/dist/commands/rescue.js +20 -20
- package/dist/commands/review-gate.js +16 -24
- package/dist/commands/review.js +37 -43
- package/dist/commands/setup.js +85 -171
- package/dist/commands/swarm.js +103 -88
- package/dist/commands/worker.js +0 -6
- package/dist/git.js +14 -7
- package/dist/hooks/approval-hook.js +9 -20
- package/dist/hooks/approval-policy.js +36 -69
- package/dist/hooks/config-safety.js +6 -4
- package/dist/hooks/install-paths.js +62 -138
- package/dist/hooks/install.js +37 -108
- package/dist/hooks/managed-block.js +88 -214
- package/dist/job-store.js +6 -28
- package/dist/kimi-command.js +1 -3
- package/dist/kimi-engine.js +1 -1
- package/dist/kimi-timeouts.js +9 -21
- package/dist/kimi-version-probe.js +16 -1210
- package/dist/native-v2-preflight.js +1 -1
- package/dist/parsing.js +4 -18
- package/dist/rescue-approval.js +78 -164
- package/dist/session-recovery.js +7 -10
- package/dist/stream-json.js +6 -174
- package/dist/version.js +1 -1
- package/docs/migration.md +2 -6
- package/docs/registry-releases.md +18 -0
- package/docs/safety.md +14 -12
- package/package.json +2 -2
- package/plugins/kimi-codex/.codex-plugin/plugin.json +4 -4
- package/plugins/kimi-codex/dist/background-spawn.js +9 -17
- package/plugins/kimi-codex/dist/cli-client.js +13 -62
- package/plugins/kimi-codex/dist/commands/ask.js +24 -29
- package/plugins/kimi-codex/dist/commands/cancel.js +7 -19
- package/plugins/kimi-codex/dist/commands/cli-helpers.js +7 -16
- package/plugins/kimi-codex/dist/commands/pursue.js +21 -31
- package/plugins/kimi-codex/dist/commands/registry.js +6 -19
- package/plugins/kimi-codex/dist/commands/rescue.js +20 -20
- package/plugins/kimi-codex/dist/commands/review-gate.js +16 -24
- package/plugins/kimi-codex/dist/commands/review.js +37 -43
- package/plugins/kimi-codex/dist/commands/setup.js +85 -171
- package/plugins/kimi-codex/dist/commands/swarm.js +103 -88
- package/plugins/kimi-codex/dist/commands/worker.js +0 -6
- package/plugins/kimi-codex/dist/git.js +14 -7
- package/plugins/kimi-codex/dist/hooks/approval-hook.js +9 -20
- package/plugins/kimi-codex/dist/hooks/approval-policy.js +36 -69
- package/plugins/kimi-codex/dist/hooks/config-safety.js +6 -4
- package/plugins/kimi-codex/dist/hooks/install-paths.js +62 -138
- package/plugins/kimi-codex/dist/hooks/install.js +37 -108
- package/plugins/kimi-codex/dist/hooks/managed-block.js +88 -214
- package/plugins/kimi-codex/dist/job-store.js +6 -28
- package/plugins/kimi-codex/dist/kimi-command.js +1 -3
- package/plugins/kimi-codex/dist/kimi-engine.js +1 -1
- package/plugins/kimi-codex/dist/kimi-timeouts.js +9 -21
- package/plugins/kimi-codex/dist/kimi-version-probe.js +16 -1210
- package/plugins/kimi-codex/dist/native-v2-preflight.js +1 -1
- package/plugins/kimi-codex/dist/parsing.js +4 -18
- package/plugins/kimi-codex/dist/rescue-approval.js +78 -164
- package/plugins/kimi-codex/dist/session-recovery.js +7 -10
- package/plugins/kimi-codex/dist/stream-json.js +6 -174
- package/plugins/kimi-codex/dist/version.js +1 -1
- package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +2 -2
- package/plugins/kimi-codex/skills/kimi-cancel/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-replay/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-result/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -3
- package/plugins/kimi-codex/skills/kimi-setup/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-status/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-swarm/SKILL.md +1 -1
- package/plugins/kimi-codex/skills/kimi-swarm-write/SKILL.md +1 -1
package/dist/commands/setup.js
CHANGED
|
@@ -1,43 +1,21 @@
|
|
|
1
1
|
import { SETUP_SUPPORTED_FLAGS } from "../parsing.js";
|
|
2
|
-
// Managed-block installer for the kimi-code PreToolUse hook.
|
|
2
|
+
// Managed-block installer for the load-bearing kimi-code PreToolUse hook.
|
|
3
|
+
// Without it, `kimi -p` auto-approves tool calls, including from read-only
|
|
4
|
+
// commands.
|
|
3
5
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
6
|
+
// Setup writes or refreshes a host-scoped BEGIN/END block containing the
|
|
7
|
+
// canonical `'<absolute-node>' '<absolute-hook-script>'` command from
|
|
8
|
+
// resolveNodeBinary/buildHookShellCommand. Both tokens are shell-quoted and the
|
|
9
|
+
// absolute Node path avoids PATH dependence under sanitized launches. Setup
|
|
10
|
+
// probes the hook directly with resolveNodeBinary and through `/bin/sh -c`
|
|
11
|
+
// using the exact installed command bytes. It also warns about broad permission
|
|
12
|
+
// denies and refuses duplicate, orphaned, or malformed managed blocks.
|
|
10
13
|
//
|
|
11
|
-
//
|
|
14
|
+
// Subcommands: install (default), --check, --uninstall [--all], and
|
|
15
|
+
// --enable-review-gate/--disable-review-gate.
|
|
12
16
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// invokes `<process.execPath> /abs/path/to/dist/hooks/approval-hook.js`
|
|
16
|
-
// on PreToolUse. The absolute Node binary is used (not bare `node`)
|
|
17
|
-
// so the hook keeps working when kimi-code is launched from a
|
|
18
|
-
// GUI/LaunchAgent with a sanitized PATH (nvm/asdf/mise users).
|
|
19
|
-
// - Probes the installed hook in TWO ways: (a) directly via the same
|
|
20
|
-
// in-process Node binary, and (b) via `/bin/sh -c "<command>"` to
|
|
21
|
-
// mirror kimi-code's actual hook-runner spawn shape (kimi-code
|
|
22
|
-
// shells out via `/bin/sh -c` per agent-core hooks/runner.ts).
|
|
23
|
-
// - Scans the user's existing `[[permission.rules]]` for broad denies
|
|
24
|
-
// that would interfere with read-only commands; warns on stdout.
|
|
25
|
-
// - Detects and refuses to install over duplicate managed blocks,
|
|
26
|
-
// orphan markers, or malformed blocks (matcher present, wrong
|
|
27
|
-
// event, missing command). Same parser shared with PR 2's
|
|
28
|
-
// runtime/hooks/install.ts verifier.
|
|
29
|
-
//
|
|
30
|
-
// Subcommands:
|
|
31
|
-
//
|
|
32
|
-
// /kimi:setup — install (idempotent) + probe
|
|
33
|
-
// /kimi:setup --check — probe only (no write)
|
|
34
|
-
// /kimi:setup --uninstall — remove managed block (and orphan markers)
|
|
35
|
-
// /kimi:setup --enable-review-gate / --disable-review-gate
|
|
36
|
-
// — flip the plugin-side review-gate flag in
|
|
37
|
-
// CLAUDE_PLUGIN_DATA/config.json
|
|
38
|
-
//
|
|
39
|
-
// TOML manipulation is line-based, not via a TOML parser. The marker
|
|
40
|
-
// block is owned by us; the rest of the file is the user's, untouched.
|
|
17
|
+
// Managed-block splicing is line-based and preserves user bytes outside owned
|
|
18
|
+
// edits; whole-file validation and inline-hook normalization use the TOML parser.
|
|
41
19
|
import { spawn } from "node:child_process";
|
|
42
20
|
import { access, mkdir, open, readFile, rename, unlink, } from "node:fs/promises";
|
|
43
21
|
import { constants as fsConstants, existsSync } from "node:fs";
|
|
@@ -59,13 +37,10 @@ const DEFAULT_HOOK_TIMEOUT_S = 15;
|
|
|
59
37
|
// node startup on cold CI runners. The shell probe adds /bin/sh fork
|
|
60
38
|
// overhead but should still complete well within the same budget.
|
|
61
39
|
const PROBE_TIMEOUT_MS = 5_000;
|
|
62
|
-
// Reject
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// `${plugin_root}/dist/hooks/approval-hook.js` and never legitimately
|
|
67
|
-
// contains these — anyone hitting this hard error has either a hostile
|
|
68
|
-
// `KIMI_PLUGIN_CC_HOOK_SCRIPT` override or an unusual install layout.
|
|
40
|
+
// Reject the specific control characters, double quotes, and backslashes matched
|
|
41
|
+
// below, including newlines and tabs; this is not a general shell-metacharacter
|
|
42
|
+
// filter. Normal install paths do not contain them, so a match indicates an
|
|
43
|
+
// unsafe override or an unusual layout that needs an explicit path change.
|
|
69
44
|
const PATH_FORBIDDEN_RE = /[\x00-\x1f"\\\n\r\t]/;
|
|
70
45
|
export async function runSetup(argv, context) {
|
|
71
46
|
const parsed = parseArgs(argv);
|
|
@@ -86,8 +61,8 @@ export async function runSetup(argv, context) {
|
|
|
86
61
|
const configPath = resolveKimiCodeConfigPath(context.env);
|
|
87
62
|
const hookScriptPath = resolveHookScriptPath(context.env);
|
|
88
63
|
assertHookPathTomlSafe(hookScriptPath);
|
|
89
|
-
// Host id
|
|
90
|
-
// ~/.kimi-code/config.toml without clobbering each other
|
|
64
|
+
// Host id scopes each managed block so Claude Code and Codex can share one
|
|
65
|
+
// ~/.kimi-code/config.toml without clobbering each other.
|
|
91
66
|
const hostId = resolveHostId(context.env, hookScriptPath);
|
|
92
67
|
const warnings = [];
|
|
93
68
|
switch (parsed.mode) {
|
|
@@ -154,12 +129,9 @@ async function runInstall(configPath, hookScriptPath, hostId, reviewGateEnabled,
|
|
|
154
129
|
}
|
|
155
130
|
warnings.push(`Converted the top-level inline hooks array (${inlineHooks.entryCount} entr${inlineHooks.entryCount === 1 ? "y" : "ies"}) to canonical [[hooks]] tables before installing. Surrounding config bytes were preserved; formatting and comments inside that hooks assignment were normalized.`);
|
|
156
131
|
}
|
|
157
|
-
// Prune
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
// kimi-code config rewrite strips marker comments, ANOTHER host's
|
|
161
|
-
// marker-less table is that host's LIVE hook — pruning it here was the
|
|
162
|
-
// pre-v1.8.2 seesaw where each host's setup disarmed the other.
|
|
132
|
+
// Prune only this host's orphaned marker-less entries before computing
|
|
133
|
+
// splice line numbers. Another host's marker-less table may be its live hook
|
|
134
|
+
// after a config rewrite strips comments, so it must remain untouched.
|
|
163
135
|
const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
|
|
164
136
|
const { pruned, count: prunedCount, commands: prunedCommands } = pruneOrphanApprovalHooks(inlineHooks.contents, lineEnding, hostId, expectedCommand);
|
|
165
137
|
const existing = pruned;
|
|
@@ -265,9 +237,8 @@ async function runCheck(configPath, hookScriptPath, hostId, reviewGateEnabled, w
|
|
|
265
237
|
};
|
|
266
238
|
}
|
|
267
239
|
const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
|
|
268
|
-
// nodeExists
|
|
269
|
-
//
|
|
270
|
-
// hostId → check THIS host's block in the shared config (v1.7.0 scoping).
|
|
240
|
+
// nodeExists refines mismatch prose into drift classification; hostId checks
|
|
241
|
+
// only this host's block in the shared config.
|
|
271
242
|
const installedCheck = evaluateInstalled(existing, expectedCommand, {
|
|
272
243
|
hostId,
|
|
273
244
|
nodeExists: (binPath) => existsSync(binPath),
|
|
@@ -280,20 +251,15 @@ async function runCheck(configPath, hookScriptPath, hostId, reviewGateEnabled, w
|
|
|
280
251
|
if (otherHosts.length > 0) {
|
|
281
252
|
warnings.push(`Other host(s) also manage a block in this shared config: ${otherHosts.join(", ")} (coexisting, untouched).`);
|
|
282
253
|
}
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
// by the installedCheck note below, not "cruft"); ours-with-a-stale-command
|
|
287
|
-
// is prunable by this host's setup; another host's is THEIR live hook —
|
|
288
|
-
// report it informationally and leave it strictly alone.
|
|
254
|
+
// Config rewrites can strip markers while leaving each host's table enforcing.
|
|
255
|
+
// Our exact-command table is live; our stale table is prunable; another host's
|
|
256
|
+
// table is reported but left strictly alone.
|
|
289
257
|
const bareTables = findUnmanagedApprovalHookBlocks(existing);
|
|
290
258
|
const ownStale = bareTables.filter((t) => hostIdFromHookCommand(t.command) === hostId && t.command !== expectedCommand);
|
|
291
259
|
const foreignHosts = [
|
|
292
260
|
...new Set(bareTables
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
// makes its path-derived host differ from `hostId` (Opus review NIT:
|
|
296
|
-
// otherwise runCheck would mislabel the host's own table as foreign).
|
|
261
|
+
// Exact current command bytes identify this host even when a host-id
|
|
262
|
+
// override disagrees with the path-derived host.
|
|
297
263
|
.filter((t) => t.command !== expectedCommand)
|
|
298
264
|
.map((t) => hostIdFromHookCommand(t.command))
|
|
299
265
|
.filter((h) => h !== null && h !== hostId)),
|
|
@@ -383,10 +349,8 @@ async function runCheck(configPath, hookScriptPath, hostId, reviewGateEnabled, w
|
|
|
383
349
|
nextStep: !probe.ok
|
|
384
350
|
? "Run /kimi:setup to repair the managed block, or /kimi:setup --uninstall if you want to remove the integration."
|
|
385
351
|
: installedCheck.via === "bare-table"
|
|
386
|
-
// Enforcement
|
|
387
|
-
//
|
|
388
|
-
// warning tells the user to re-adorn — that contradiction confused
|
|
389
|
-
// operators (Kimi review F3). Recommend the harmless re-adorn.
|
|
352
|
+
// Enforcement remains active through the marker-less table, but recommend
|
|
353
|
+
// re-adorning rather than claiming no action is needed.
|
|
390
354
|
? "Enforcement is active. Run /kimi:setup to re-adorn this host's managed-block markers — kimi-code stripped them on its last config write (comments are not preserved). Your hook keeps working meanwhile."
|
|
391
355
|
: "No action needed.",
|
|
392
356
|
details: buildDetails({
|
|
@@ -425,21 +389,14 @@ async function runUninstallLocked(configPath, hookScriptPath, hostId, removeAllH
|
|
|
425
389
|
}),
|
|
426
390
|
};
|
|
427
391
|
}
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
// marker-less tables intact (a marker-less table is likely that host's LIVE
|
|
432
|
-
// hook after a kimi-code comment strip — v1.8.2 host scoping). `--all`:
|
|
433
|
-
// remove EVERY host's managed block and every one of our hook tables (the
|
|
434
|
-
// full nuke).
|
|
392
|
+
// Scoped uninstall removes only this host's marked and marker-less blocks;
|
|
393
|
+
// other hosts' blocks and live marker-less tables remain intact. `--all` is
|
|
394
|
+
// the explicit every-host sweep for all of this plugin's hook tables.
|
|
435
395
|
const { stripped: strippedMarkers, removedBlocks, orphansLeft } = stripManagedBlocks(existing, hostId, removeAllHosts);
|
|
436
396
|
const lineEnding = detectLineEnding(existing);
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
// path-derived host, or when the hook lives under a non-standard (dev) path
|
|
441
|
-
// that `isOurApprovalHookCommand` wouldn't recognize — the install path
|
|
442
|
-
// already does this; uninstall now matches it (Codex P2 / Opus A / Kimi F4).
|
|
397
|
+
// Exact current command bytes also identify this host's table when a host-id
|
|
398
|
+
// override disagrees with path derivation or a dev path is outside the strict
|
|
399
|
+
// ownership pattern.
|
|
443
400
|
const expectedCommand = buildHookShellCommand(hookScriptPath, context.env);
|
|
444
401
|
const { pruned: stripped, count: prunedCount } = pruneOrphanApprovalHooks(strippedMarkers, lineEnding, removeAllHosts ? undefined : hostId, expectedCommand);
|
|
445
402
|
const changed = stripped !== existing;
|
|
@@ -498,21 +455,12 @@ async function readConfigSafe(configPath) {
|
|
|
498
455
|
}
|
|
499
456
|
}
|
|
500
457
|
async function writeConfigAtomic(configPath, contents) {
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
// can race without clobbering each other's intermediate file (PR 4
|
|
504
|
-
// reviewer finding — fixed-path tmp file was a race surface).
|
|
458
|
+
// Write a unique sibling temp file and atomically rename it. O_EXCL prevents
|
|
459
|
+
// concurrent operations from sharing an intermediate path.
|
|
505
460
|
//
|
|
506
|
-
//
|
|
507
|
-
//
|
|
508
|
-
//
|
|
509
|
-
// file holds API keys and tokens; a writeFile()-then-chmod() sequence
|
|
510
|
-
// would let the temp file sit briefly at the default umask mode
|
|
511
|
-
// (typically 0o644, group/world-readable) in between — audit report 28
|
|
512
|
-
// (Codex M1) tracked the chmod-after-write half of this, but a second
|
|
513
|
-
// pass found the create-then-tighten window itself. Passing the mode to
|
|
514
|
-
// open() means the file never exists on disk at anything wider than
|
|
515
|
-
// 0o600.
|
|
461
|
+
// Pass 0o600 at creation, not in a later chmod: the config can contain
|
|
462
|
+
// credentials, so the temp file must never exist with a wider umask-derived
|
|
463
|
+
// mode.
|
|
516
464
|
await mkdir(path.dirname(configPath), { recursive: true });
|
|
517
465
|
const suffix = randomBytes(8).toString("hex");
|
|
518
466
|
const tmpPath = `${configPath}.kimi-plugin-cc.${process.pid}.${suffix}.tmp`;
|
|
@@ -589,13 +537,9 @@ function splitPreservingEnding(contents) {
|
|
|
589
537
|
return contents.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
590
538
|
}
|
|
591
539
|
/**
|
|
592
|
-
* Remove managed
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
* uninstall never removes a `~/.codex/…` legacy block another host still relies
|
|
596
|
-
* on; Kimi review). Orphan marker lines are removed individually (no
|
|
597
|
-
* destructive sweep of trailing content) so `--uninstall` on a corrupted config
|
|
598
|
-
* doesn't take user data with it.
|
|
540
|
+
* Remove managed blocks owned by `currentHost`, or every host with `removeAll`.
|
|
541
|
+
* Ownership comes from the marker suffix or a legacy command's derived host.
|
|
542
|
+
* Orphan markers are removed individually so surrounding user content survives.
|
|
599
543
|
*/
|
|
600
544
|
function stripManagedBlocks(contents, currentHost, removeAll) {
|
|
601
545
|
const lineEnding = detectLineEnding(contents);
|
|
@@ -603,13 +547,10 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
|
|
|
603
547
|
const result = [];
|
|
604
548
|
const orphansLeft = [];
|
|
605
549
|
let removedBlocks = 0;
|
|
606
|
-
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
// ownership; only `--all` removes it), so a scoped uninstall never destroys
|
|
611
|
-
// another host's non-canonical block (Codex review). A bare/orphan marker line
|
|
612
|
-
// has no command and is broken cruft, so a scoped uninstall does sweep it.
|
|
550
|
+
// Suffixed blocks belong only to the matching host. Complete legacy blocks
|
|
551
|
+
// use their command-derived host; an unattributable non-canonical command is
|
|
552
|
+
// ambiguous and only --all removes it. Orphan marker lines have no command
|
|
553
|
+
// and can be swept individually by a scoped uninstall.
|
|
613
554
|
const shouldRemove = (markerHost, commandPath, isOrphanMarker) => {
|
|
614
555
|
if (removeAll)
|
|
615
556
|
return true;
|
|
@@ -685,7 +626,6 @@ function stripManagedBlocks(contents, currentHost, removeAll) {
|
|
|
685
626
|
result.push(line);
|
|
686
627
|
i += 1;
|
|
687
628
|
}
|
|
688
|
-
// Collapse runs of >= 3 blank lines created by removal back to 2.
|
|
689
629
|
const collapsed = [];
|
|
690
630
|
let blankRun = 0;
|
|
691
631
|
for (const line of result) {
|
|
@@ -725,7 +665,6 @@ function pruneOrphanApprovalHooks(contents, lineEnding, ownedBy, alsoMatchComman
|
|
|
725
665
|
}
|
|
726
666
|
const lines = splitPreservingEnding(contents);
|
|
727
667
|
const kept = lines.filter((_, idx) => !remove.has(idx));
|
|
728
|
-
// Collapse runs of >= 3 blank lines created by removal back to 2.
|
|
729
668
|
const collapsed = [];
|
|
730
669
|
let blankRun = 0;
|
|
731
670
|
for (const line of kept) {
|
|
@@ -753,15 +692,14 @@ function buildManagedBlock(hookScriptPath, env, lineEnding = "\n", hostId = "")
|
|
|
753
692
|
return [
|
|
754
693
|
`${BEGIN_MARKER_PREFIX}${suffix} (v${KIMI_PLUGIN_CC_VERSION}) ===`,
|
|
755
694
|
`# DO NOT EDIT — managed by /kimi:setup. Run /kimi:setup --uninstall to remove.`,
|
|
756
|
-
`# Host: ${hostId.length > 0 ? hostId : "(legacy)"} —
|
|
757
|
-
`#
|
|
758
|
-
`#
|
|
695
|
+
`# Host: ${hostId.length > 0 ? hostId : "(legacy)"} — each host owns a separate block in this shared`,
|
|
696
|
+
`# ~/.kimi-code/config.toml. Scoped setup/uninstall preserves other hosts;`,
|
|
697
|
+
`# /kimi:setup --uninstall --all removes every host's block.`,
|
|
759
698
|
`# Purpose:`,
|
|
760
699
|
`# kimi-code's \`kimi -p\` mode hard-codes permission='auto' and`,
|
|
761
|
-
`# auto-approves every tool call. This hook enforces
|
|
762
|
-
`#
|
|
763
|
-
`#
|
|
764
|
-
`# Without this block the plugin's safety contract collapses.`,
|
|
700
|
+
`# auto-approves every tool call. This hook enforces each managed command's`,
|
|
701
|
+
`# read-only or workspace-bound approval policy. Without this block,`,
|
|
702
|
+
`# the plugin's safety contract collapses.`,
|
|
765
703
|
`# Matcher field is intentionally OMITTED — kimi-code compiles the`,
|
|
766
704
|
`# matcher with \`new RegExp(...)\`. An empty/missing matcher means`,
|
|
767
705
|
`# "fire for every tool". The string "*" would throw and silently`,
|
|
@@ -777,11 +715,9 @@ function buildManagedBlock(hookScriptPath, env, lineEnding = "\n", hostId = "")
|
|
|
777
715
|
].join(lineEnding);
|
|
778
716
|
}
|
|
779
717
|
/**
|
|
780
|
-
* Encode a
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
* declare `\'` as a reserved escape, and a parser-compliant TOML
|
|
784
|
-
* library (kimi-code uses smol-toml) raises an error on encounter.
|
|
718
|
+
* Encode a TOML basic string, including quotes, backslashes, and control
|
|
719
|
+
* escapes. Raw shell output must not be interpolated directly: TOML reserves
|
|
720
|
+
* `\'`, and a compliant parser rejects that escape.
|
|
785
721
|
*/
|
|
786
722
|
function tomlBasicString(value) {
|
|
787
723
|
const escaped = value
|
|
@@ -818,24 +754,18 @@ async function assertHookScriptExists(hookScriptPath) {
|
|
|
818
754
|
}
|
|
819
755
|
}
|
|
820
756
|
/**
|
|
821
|
-
* Two-layer probe
|
|
757
|
+
* Two-layer probe; both layers require exit 2 and a non-empty denial reason.
|
|
822
758
|
*
|
|
823
|
-
* 1.
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
759
|
+
* 1. Directly spawn the hook with the absolute path from resolveNodeBinary,
|
|
760
|
+
* catching missing/broken scripts and incorrect hook exit behavior without
|
|
761
|
+
* relying on PATH.
|
|
762
|
+
* 2. Execute the exact canonical command from buildHookShellCommand through
|
|
763
|
+
* `/bin/sh -c`: two shell-quoted absolute tokens for Node and the script.
|
|
764
|
+
* This catches pinned-interpreter execution and quoting failures. It does
|
|
765
|
+
* NOT test PATH discovery or compatibility of some other `node` on PATH.
|
|
829
766
|
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
* synthetic stdin. Catches: kimi-code's shell can't find the
|
|
833
|
-
* Node binary, shell-quoting in the managed block is wrong, the
|
|
834
|
-
* Node binary on PATH isn't compatible.
|
|
835
|
-
*
|
|
836
|
-
* Both must pass for the install to be considered healthy. The shell
|
|
837
|
-
* probe is skipped on platforms without `/bin/sh` (Windows pure, etc.) —
|
|
838
|
-
* but the rest of the runtime already assumes POSIX.
|
|
767
|
+
* The shell layer is skipped where `/bin/sh` is unsupported; the runtime
|
|
768
|
+
* otherwise assumes POSIX.
|
|
839
769
|
*/
|
|
840
770
|
async function probeHook(hookScriptPath, env) {
|
|
841
771
|
const directResult = await probeHookDirect(hookScriptPath, env);
|
|
@@ -905,7 +835,6 @@ function spawnProbe(command, args, env, label) {
|
|
|
905
835
|
child.kill("SIGKILL");
|
|
906
836
|
}
|
|
907
837
|
catch {
|
|
908
|
-
// best effort
|
|
909
838
|
}
|
|
910
839
|
resolve({
|
|
911
840
|
ok: false,
|
|
@@ -947,27 +876,11 @@ function truncate(value, max) {
|
|
|
947
876
|
}
|
|
948
877
|
// ----- Permission rules scan --------------------------------------------
|
|
949
878
|
/**
|
|
950
|
-
*
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
* as a known limitation in PR 4 docs.
|
|
956
|
-
*/
|
|
957
|
-
/**
|
|
958
|
-
* Probe kimi-code's version and append a warning to `warnings` if the
|
|
959
|
-
* installed kimi is outside the range kimi-plugin-cc was tested
|
|
960
|
-
* against (H6, Codex post-hotfix audit Area 8).
|
|
961
|
-
*
|
|
962
|
-
* Setup soft-fail policy: if the probe itself fails (kimi not on PATH, spawn
|
|
963
|
-
* error, unparseable output), we record nothing here — the hook probe
|
|
964
|
-
* will surface a more direct error in that case. We only loud-warn when
|
|
965
|
-
* the version is **demonstrably** out of range. Setup may still complete,
|
|
966
|
-
* but the model execution-plan gate will refuse that binary before spawn.
|
|
967
|
-
*
|
|
968
|
-
* Override: `KIMI_PLUGIN_CC_SKIP_VERSION_PROBE=1` skips the probe
|
|
969
|
-
* entirely for tests and CI environments without kimi installed. It is not a
|
|
970
|
-
* production compatibility override; model jobs persist it as test-bypass.
|
|
879
|
+
* Warn only when the installed kimi is demonstrably outside the certified
|
|
880
|
+
* range. Probe failure records nothing here because the hook probe reports the
|
|
881
|
+
* more direct problem; setup may finish, but model spawn still refuses an
|
|
882
|
+
* uncertified binary. KIMI_PLUGIN_CC_SKIP_VERSION_PROBE is a test/CI bypass,
|
|
883
|
+
* not a production compatibility override.
|
|
971
884
|
*/
|
|
972
885
|
async function collectKimiVersionWarnings(env, warnings) {
|
|
973
886
|
if (env.KIMI_PLUGIN_CC_SKIP_VERSION_PROBE === "1")
|
|
@@ -991,16 +904,12 @@ async function collectKimiVersionWarnings(env, warnings) {
|
|
|
991
904
|
warnings.push(formatVersionOutOfRangeWarning(probe, KIMI_PLUGIN_CC_VERSION));
|
|
992
905
|
}
|
|
993
906
|
/**
|
|
994
|
-
*
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
*
|
|
998
|
-
*
|
|
999
|
-
*
|
|
1000
|
-
* plugins' tools are blocked — safe, but they silently burn model turns. We
|
|
1001
|
-
* surface that expectation at setup time so a confused "kimi did nothing" report
|
|
1002
|
-
* doesn't follow. Best-effort: a missing/unreadable/malformed manifest yields no
|
|
1003
|
-
* notice. Never blocks setup; never mutates the plugin list.
|
|
907
|
+
* Non-blocking notice for kimi-code's own enabled plugins from
|
|
908
|
+
* ~/.kimi-code/plugins/installed.json. Their tools can register on every
|
|
909
|
+
* session, but read-only commands deny everything outside READ_ONLY_TOOLS
|
|
910
|
+
* (Read, Grep, Glob, ReadMediaFile, TaskList, TaskOutput), which can waste model
|
|
911
|
+
* turns. Missing, unreadable, or malformed manifests produce no notice; setup
|
|
912
|
+
* never blocks on or mutates the plugin list.
|
|
1004
913
|
*/
|
|
1005
914
|
async function collectInstalledKimiPluginsNotice(env, warnings) {
|
|
1006
915
|
const home = resolveKimiHome(env);
|
|
@@ -1041,6 +950,11 @@ async function collectInstalledKimiPluginsNotice(env, warnings) {
|
|
|
1041
950
|
" kimi-code plugins you don't need for delegated work in your kimi-code config.",
|
|
1042
951
|
].join("\n"));
|
|
1043
952
|
}
|
|
953
|
+
/**
|
|
954
|
+
* Best-effort line scan for permission rules that deny common read tools. This
|
|
955
|
+
* is not a TOML parse: it recognizes basic and literal quoted strings but misses
|
|
956
|
+
* inline-table and multiline forms.
|
|
957
|
+
*/
|
|
1044
958
|
function collectPermissionRuleWarnings(contents, warnings) {
|
|
1045
959
|
const lines = contents.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
1046
960
|
let inRule = false;
|