kimi-plugin-cc 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +16 -0
  3. package/.claude-plugin/plugin.json +32 -0
  4. package/CHANGELOG.md +1096 -0
  5. package/LICENSE +201 -0
  6. package/README.md +772 -0
  7. package/agents/kimi-ask.md +82 -0
  8. package/agents/kimi-challenge.md +82 -0
  9. package/agents/kimi-pursue.md +91 -0
  10. package/agents/kimi-rescue.md +85 -0
  11. package/agents/kimi-review.md +82 -0
  12. package/agents/kimi-swarm-write.md +92 -0
  13. package/agents/kimi-swarm.md +83 -0
  14. package/commands/README.md +16 -0
  15. package/commands/ask.md +32 -0
  16. package/commands/cancel.md +17 -0
  17. package/commands/challenge.md +29 -0
  18. package/commands/pursue.md +37 -0
  19. package/commands/replay.md +13 -0
  20. package/commands/rescue.md +32 -0
  21. package/commands/result.md +13 -0
  22. package/commands/review.md +29 -0
  23. package/commands/setup.md +23 -0
  24. package/commands/status.md +13 -0
  25. package/commands/swarm.md +38 -0
  26. package/dist/background-spawn.js +224 -0
  27. package/dist/cli-cancellation.js +43 -0
  28. package/dist/cli-client.js +845 -0
  29. package/dist/commands/ask.js +327 -0
  30. package/dist/commands/cancel.js +178 -0
  31. package/dist/commands/cli-helpers.js +112 -0
  32. package/dist/commands/models.js +140 -0
  33. package/dist/commands/not-implemented.js +4 -0
  34. package/dist/commands/pursue.js +311 -0
  35. package/dist/commands/registry.js +87 -0
  36. package/dist/commands/repair-sessions.js +159 -0
  37. package/dist/commands/replay.js +123 -0
  38. package/dist/commands/rescue.js +333 -0
  39. package/dist/commands/result.js +58 -0
  40. package/dist/commands/review-gate.js +393 -0
  41. package/dist/commands/review.js +218 -0
  42. package/dist/commands/setup.js +1132 -0
  43. package/dist/commands/status.js +27 -0
  44. package/dist/commands/swarm.js +576 -0
  45. package/dist/commands/worker.js +31 -0
  46. package/dist/companion.js +114 -0
  47. package/dist/config.js +22 -0
  48. package/dist/errors.js +40 -0
  49. package/dist/git.js +215 -0
  50. package/dist/hooks/approval-hook.js +112 -0
  51. package/dist/hooks/approval-policy.js +254 -0
  52. package/dist/hooks/config-safety.js +637 -0
  53. package/dist/hooks/install-paths.js +504 -0
  54. package/dist/hooks/install.js +281 -0
  55. package/dist/hooks/managed-block.js +745 -0
  56. package/dist/hooks/review-gate-stop.js +19 -0
  57. package/dist/job-store.js +559 -0
  58. package/dist/jobs.js +202 -0
  59. package/dist/kimi-command.js +72 -0
  60. package/dist/kimi-engine.js +624 -0
  61. package/dist/kimi-errors.js +220 -0
  62. package/dist/kimi-home.js +9 -0
  63. package/dist/kimi-timeouts.js +68 -0
  64. package/dist/kimi-version-probe.js +1410 -0
  65. package/dist/logging.js +22 -0
  66. package/dist/native-v2-preflight.js +417 -0
  67. package/dist/package.json +1 -0
  68. package/dist/parsing.js +553 -0
  69. package/dist/paths.js +27 -0
  70. package/dist/plugin-data.js +98 -0
  71. package/dist/process-tree.js +448 -0
  72. package/dist/render.js +159 -0
  73. package/dist/rescue-approval.js +951 -0
  74. package/dist/schemas/review-gate-output.js +43 -0
  75. package/dist/session-title.js +293 -0
  76. package/dist/stream-json.js +498 -0
  77. package/dist/types.js +1 -0
  78. package/dist/vendor/shell-quote/LICENSE +24 -0
  79. package/dist/vendor/shell-quote/index.js +1 -0
  80. package/dist/vendor/shell-quote/parse.js +229 -0
  81. package/dist/vendor/smol-toml/LICENSE +24 -0
  82. package/dist/vendor/smol-toml/date.js +127 -0
  83. package/dist/vendor/smol-toml/error.js +63 -0
  84. package/dist/vendor/smol-toml/extract.js +100 -0
  85. package/dist/vendor/smol-toml/parse.js +148 -0
  86. package/dist/vendor/smol-toml/primitive.js +179 -0
  87. package/dist/vendor/smol-toml/struct.js +184 -0
  88. package/dist/vendor/smol-toml/util.js +113 -0
  89. package/dist/version.js +7 -0
  90. package/docs/migration.md +215 -0
  91. package/docs/models.md +82 -0
  92. package/docs/registry-releases.md +70 -0
  93. package/docs/safety.md +318 -0
  94. package/docs/session-visibility.md +32 -0
  95. package/hooks/README.md +9 -0
  96. package/hooks/hooks.json +16 -0
  97. package/package.json +67 -0
  98. package/plugins/kimi-codex/.codex-plugin/plugin.json +39 -0
  99. package/plugins/kimi-codex/dist/background-spawn.js +224 -0
  100. package/plugins/kimi-codex/dist/cli-cancellation.js +43 -0
  101. package/plugins/kimi-codex/dist/cli-client.js +845 -0
  102. package/plugins/kimi-codex/dist/commands/ask.js +327 -0
  103. package/plugins/kimi-codex/dist/commands/cancel.js +178 -0
  104. package/plugins/kimi-codex/dist/commands/cli-helpers.js +112 -0
  105. package/plugins/kimi-codex/dist/commands/models.js +140 -0
  106. package/plugins/kimi-codex/dist/commands/not-implemented.js +4 -0
  107. package/plugins/kimi-codex/dist/commands/pursue.js +311 -0
  108. package/plugins/kimi-codex/dist/commands/registry.js +87 -0
  109. package/plugins/kimi-codex/dist/commands/repair-sessions.js +159 -0
  110. package/plugins/kimi-codex/dist/commands/replay.js +123 -0
  111. package/plugins/kimi-codex/dist/commands/rescue.js +333 -0
  112. package/plugins/kimi-codex/dist/commands/result.js +58 -0
  113. package/plugins/kimi-codex/dist/commands/review-gate.js +393 -0
  114. package/plugins/kimi-codex/dist/commands/review.js +218 -0
  115. package/plugins/kimi-codex/dist/commands/setup.js +1132 -0
  116. package/plugins/kimi-codex/dist/commands/status.js +27 -0
  117. package/plugins/kimi-codex/dist/commands/swarm.js +576 -0
  118. package/plugins/kimi-codex/dist/commands/worker.js +31 -0
  119. package/plugins/kimi-codex/dist/companion.js +114 -0
  120. package/plugins/kimi-codex/dist/config.js +22 -0
  121. package/plugins/kimi-codex/dist/errors.js +40 -0
  122. package/plugins/kimi-codex/dist/git.js +215 -0
  123. package/plugins/kimi-codex/dist/hooks/approval-hook.js +112 -0
  124. package/plugins/kimi-codex/dist/hooks/approval-policy.js +254 -0
  125. package/plugins/kimi-codex/dist/hooks/config-safety.js +637 -0
  126. package/plugins/kimi-codex/dist/hooks/install-paths.js +504 -0
  127. package/plugins/kimi-codex/dist/hooks/install.js +281 -0
  128. package/plugins/kimi-codex/dist/hooks/managed-block.js +745 -0
  129. package/plugins/kimi-codex/dist/hooks/review-gate-stop.js +19 -0
  130. package/plugins/kimi-codex/dist/job-store.js +559 -0
  131. package/plugins/kimi-codex/dist/jobs.js +202 -0
  132. package/plugins/kimi-codex/dist/kimi-command.js +72 -0
  133. package/plugins/kimi-codex/dist/kimi-engine.js +624 -0
  134. package/plugins/kimi-codex/dist/kimi-errors.js +220 -0
  135. package/plugins/kimi-codex/dist/kimi-home.js +9 -0
  136. package/plugins/kimi-codex/dist/kimi-timeouts.js +68 -0
  137. package/plugins/kimi-codex/dist/kimi-version-probe.js +1410 -0
  138. package/plugins/kimi-codex/dist/logging.js +22 -0
  139. package/plugins/kimi-codex/dist/native-v2-preflight.js +417 -0
  140. package/plugins/kimi-codex/dist/package.json +1 -0
  141. package/plugins/kimi-codex/dist/parsing.js +553 -0
  142. package/plugins/kimi-codex/dist/paths.js +27 -0
  143. package/plugins/kimi-codex/dist/plugin-data.js +98 -0
  144. package/plugins/kimi-codex/dist/process-tree.js +448 -0
  145. package/plugins/kimi-codex/dist/render.js +159 -0
  146. package/plugins/kimi-codex/dist/rescue-approval.js +951 -0
  147. package/plugins/kimi-codex/dist/schemas/review-gate-output.js +43 -0
  148. package/plugins/kimi-codex/dist/session-title.js +293 -0
  149. package/plugins/kimi-codex/dist/stream-json.js +498 -0
  150. package/plugins/kimi-codex/dist/types.js +1 -0
  151. package/plugins/kimi-codex/dist/vendor/shell-quote/LICENSE +24 -0
  152. package/plugins/kimi-codex/dist/vendor/shell-quote/index.js +1 -0
  153. package/plugins/kimi-codex/dist/vendor/shell-quote/parse.js +229 -0
  154. package/plugins/kimi-codex/dist/vendor/smol-toml/LICENSE +24 -0
  155. package/plugins/kimi-codex/dist/vendor/smol-toml/date.js +127 -0
  156. package/plugins/kimi-codex/dist/vendor/smol-toml/error.js +63 -0
  157. package/plugins/kimi-codex/dist/vendor/smol-toml/extract.js +100 -0
  158. package/plugins/kimi-codex/dist/vendor/smol-toml/parse.js +148 -0
  159. package/plugins/kimi-codex/dist/vendor/smol-toml/primitive.js +179 -0
  160. package/plugins/kimi-codex/dist/vendor/smol-toml/struct.js +184 -0
  161. package/plugins/kimi-codex/dist/vendor/smol-toml/util.js +113 -0
  162. package/plugins/kimi-codex/dist/version.js +7 -0
  163. package/plugins/kimi-codex/scripts/companion.sh +37 -0
  164. package/plugins/kimi-codex/scripts/review-gate-hook.sh +32 -0
  165. package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +31 -0
  166. package/plugins/kimi-codex/skills/kimi-ask/agents/openai.yaml +6 -0
  167. package/plugins/kimi-codex/skills/kimi-cancel/SKILL.md +25 -0
  168. package/plugins/kimi-codex/skills/kimi-cancel/agents/openai.yaml +6 -0
  169. package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +31 -0
  170. package/plugins/kimi-codex/skills/kimi-challenge/agents/openai.yaml +6 -0
  171. package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +30 -0
  172. package/plugins/kimi-codex/skills/kimi-pursue/agents/openai.yaml +6 -0
  173. package/plugins/kimi-codex/skills/kimi-replay/SKILL.md +25 -0
  174. package/plugins/kimi-codex/skills/kimi-replay/agents/openai.yaml +6 -0
  175. package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +30 -0
  176. package/plugins/kimi-codex/skills/kimi-rescue/agents/openai.yaml +6 -0
  177. package/plugins/kimi-codex/skills/kimi-result/SKILL.md +25 -0
  178. package/plugins/kimi-codex/skills/kimi-result/agents/openai.yaml +6 -0
  179. package/plugins/kimi-codex/skills/kimi-review/SKILL.md +31 -0
  180. package/plugins/kimi-codex/skills/kimi-review/agents/openai.yaml +6 -0
  181. package/plugins/kimi-codex/skills/kimi-setup/SKILL.md +32 -0
  182. package/plugins/kimi-codex/skills/kimi-setup/agents/openai.yaml +6 -0
  183. package/plugins/kimi-codex/skills/kimi-status/SKILL.md +25 -0
  184. package/plugins/kimi-codex/skills/kimi-status/agents/openai.yaml +6 -0
  185. package/plugins/kimi-codex/skills/kimi-swarm/SKILL.md +31 -0
  186. package/plugins/kimi-codex/skills/kimi-swarm/agents/openai.yaml +6 -0
  187. package/plugins/kimi-codex/skills/kimi-swarm-write/SKILL.md +32 -0
  188. package/plugins/kimi-codex/skills/kimi-swarm-write/agents/openai.yaml +6 -0
  189. package/scripts/companion.sh +37 -0
  190. package/scripts/review-gate-hook.sh +32 -0
@@ -0,0 +1,281 @@
1
+ import { constants as fsConstants, existsSync } from "node:fs";
2
+ import { access, readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { validateKimiHookSetForEnvironment } from "./config-safety.js";
5
+ import { evaluateInstalled } from "./managed-block.js";
6
+ import { resolveHostId, tryBuildExpectedHookCommand } from "./install-paths.js";
7
+ import { resolveKimiHome } from "../kimi-home.js";
8
+ export async function verifyHookInstalled(env) {
9
+ const configPath = resolveKimiCodeConfigPath(env);
10
+ if (env.KIMI_PLUGIN_CC_SKIP_HOOK_CHECK === "1") {
11
+ return { installed: true, configPath };
12
+ }
13
+ // Canonical expected shell command for the current env. If this
14
+ // can't be resolved (KIMI_PLUGIN_CC_NODE_BIN not absolute,
15
+ // install-paths module can't infer plugin root, etc.) treat the hook
16
+ // as un-verifiable — installed=false with a structured reason. The
17
+ // caller's stderr warning surfaces the underlying error code.
18
+ const expected = tryBuildExpectedHookCommand(env);
19
+ if ("error" in expected) {
20
+ return {
21
+ installed: false,
22
+ reason: `unable to resolve canonical hook command for this companion: ${expected.error.message}`,
23
+ configPath,
24
+ };
25
+ }
26
+ let raw;
27
+ try {
28
+ raw = await readFile(configPath, "utf8");
29
+ }
30
+ catch (err) {
31
+ const code = err.code;
32
+ if (code === "ENOENT") {
33
+ return {
34
+ installed: false,
35
+ reason: "kimi-code config file does not exist",
36
+ configPath,
37
+ };
38
+ }
39
+ return {
40
+ installed: false,
41
+ reason: `failed to read kimi-code config: ${err.message}`,
42
+ configPath,
43
+ };
44
+ }
45
+ const hookSet = await validateKimiHookSetForEnvironment(raw, env);
46
+ if (!hookSet.valid) {
47
+ return {
48
+ installed: false,
49
+ reason: hookSet.reason ?? "configured hooks failed whole-array validation",
50
+ configPath,
51
+ };
52
+ }
53
+ // Pass `nodeExists` so a command MISMATCH is classified into an actionable
54
+ // H4 diagnosis (Node upgrade / version-manager switch vs. plugin path drift)
55
+ // instead of a raw expected-vs-got dump. Classification only refines the
56
+ // reason; it never changes the installed=false decision.
57
+ //
58
+ // `hostId` selects THIS host's block in the shared config so Claude Code and
59
+ // Codex verify their own managed block independently (v1.7.0 host scoping).
60
+ const check = evaluateInstalled(raw, expected.command, {
61
+ hostId: resolveHostId(env, expected.hookScriptPath),
62
+ nodeExists: (binPath) => existsSync(binPath),
63
+ });
64
+ if (!check.installed) {
65
+ return {
66
+ installed: false,
67
+ reason: check.reason,
68
+ configPath,
69
+ // Forwarded verbatim. Present only for a parseable path/binary mismatch;
70
+ // the earlier short-circuits above (unresolvable command, unreadable
71
+ // config, invalid hook set) return before this point and therefore never
72
+ // carry drift — those are not re-pin-recoverable.
73
+ ...(check.drift !== undefined ? { drift: check.drift } : {}),
74
+ };
75
+ }
76
+ // The command matched byte-for-byte, but that alone doesn't prove the
77
+ // hook can run — confirm the script it points at still exists and is
78
+ // readable (see point 3 above). Fail closed on any stat/access error.
79
+ try {
80
+ await access(expected.hookScriptPath, fsConstants.R_OK);
81
+ }
82
+ catch {
83
+ return {
84
+ installed: false,
85
+ reason: `hook script ${expected.hookScriptPath} is missing or unreadable — run /kimi:setup to reinstall`,
86
+ configPath,
87
+ };
88
+ }
89
+ // ...and confirm the pinned INTERPRETER is executable.
90
+ //
91
+ // SCOPE — read this before widening the claim. This is a NARROW BACKSTOP, not
92
+ // the thing that catches a dead Node. Two adversarial reviews (2026-07-25)
93
+ // rejected the original, wider rationale, and it was verified empirically:
94
+ //
95
+ // - It does NOT catch a dangling Homebrew symlink. A dangling symlink cannot
96
+ // be exec'd at all, and `command -v node` skips it, so companion.sh (which
97
+ // `exec`s the binary it resolves, scripts/companion.sh:34,50) lands on a
98
+ // DIFFERENT node. The expected command is recomputed from the live process
99
+ // on every call, so it changes, and plain byte-exact equality above
100
+ // already refuses — with a RECOVERABLE `drift` that /kimi:setup converges.
101
+ // - It does NOT catch the 2026-07-25 dead-node zombie (a config pinning
102
+ // ~/.hermes/node). That is a byte mismatch, likewise caught above.
103
+ // - On the default path it is close to self-evident: `resolveNodeBinary`
104
+ // returns the path THIS process was launched through, which therefore
105
+ // existed and was executable moments ago.
106
+ //
107
+ // THE EXACT PREDICATE it covers: config and expected command still match
108
+ // byte-for-byte, yet the Node token embedded in that command fails X_OK AT
109
+ // VERIFICATION TIME. Three reachable ways to get there:
110
+ //
111
+ // 1. An absolute `KIMI_PLUGIN_CC_NODE_BIN` naming a binary that is NOT the
112
+ // running interpreter and is already dead — e.g. a pinned `nvm` version
113
+ // later `nvm uninstall`ed — reached by a direct `node dist/companion.js`
114
+ // that bypasses companion.sh's exec+version gate. The override is
115
+ // returned verbatim, so it is never proven by the running process.
116
+ // 2. POST-LAUNCH MUTATION on the DEFAULT path (this one is easy to miss):
117
+ // the interpreter is valid at launch, so the process starts, and is then
118
+ // `chmod -x`'d. `realpathSync` still resolves it, so `preferStableNodePath`
119
+ // keeps argv0 and the bytes are unchanged — equality passes, X_OK fails.
120
+ // 3. Same, but the pathname is REMOVED after launch. `realpathSync` throws
121
+ // and we fall back to `execPath`, which in argv0===execPath layouts
122
+ // (nvm/asdf) is the identical spelling — so equality still passes while
123
+ // the path is gone.
124
+ //
125
+ // In all three the old behavior was installed=true → `/bin/sh -c` exits 127 →
126
+ // kimi-code reads any non-2 exit as ALLOW. Converting that to a refusal is
127
+ // the whole value. Cases 2 and 3 are also why setup's stronger execution
128
+ // probe is not a substitute: it runs at install/--check, whereas this reruns
129
+ // before every model spawn and so catches degradation since the last probe.
130
+ //
131
+ // It proves the exec bit ONLY — not a working Node, not the right arch, not
132
+ // >=22.5. The real proof is setup's shell probe, which EXECUTES the byte-
133
+ // identical command (setup.ts::probeHook) and now fails install nonzero. The
134
+ // verifier runs on every spawn and cannot afford a fork, so it approximates.
135
+ //
136
+ // Fail-closed on ANY errno, deliberately diverging from background-spawn.ts,
137
+ // which ignores errnos outside ENOENT/EACCES/EPERM. Spawning can afford to try
138
+ // and see; a security verifier cannot. Do not "harmonize" these.
139
+ //
140
+ // Not re-pin-recoverable: no `drift` is attached (drift only exists when the
141
+ // equality check FAILS, so it can never displace a recoverable path), and
142
+ // `hookRefusalDetails` therefore omits `retryable_after_setup`, which agent
143
+ // callers map to "surface and stop." Setup cannot conjure a working Node.
144
+ try {
145
+ await access(expected.nodeBin, fsConstants.X_OK);
146
+ }
147
+ catch {
148
+ return {
149
+ installed: false,
150
+ reason: `hook interpreter ${expected.nodeBin} is missing or not executable, so the PreToolUse hook ` +
151
+ `cannot spawn (kimi-code reads a failed hook as ALLOW). Repair the Node install, or set ` +
152
+ `KIMI_PLUGIN_CC_NODE_BIN to a valid Node >=22.5 executable and run /kimi:setup.`,
153
+ configPath,
154
+ // Machine-readable discriminator: this is the ONE refusal whose remedy is
155
+ // NOT "run /kimi:setup" (LLM-caller discipline — an agent cannot read the
156
+ // prose above, and every other refusal emits an identical `{config_path}`).
157
+ refusalKind: "node-bin-not-executable",
158
+ nodeBin: expected.nodeBin,
159
+ };
160
+ }
161
+ return { installed: true, configPath };
162
+ }
163
+ function resolveKimiCodeConfigPath(env) {
164
+ return path.join(resolveKimiHome(env), "config.toml");
165
+ }
166
+ /**
167
+ * Format a stderr-suitable warning message for the missing-hook case.
168
+ * Single source of truth so review.ts / review-gate.ts / ask.ts emit
169
+ * identical language.
170
+ */
171
+ /**
172
+ * Structured `details` payload for a hook-refusal `RuntimeError`. Single source
173
+ * of truth so all five model-spawning commands emit the same machine-readable
174
+ * shape — an LLM caller cannot see stderr, so anything it must act on has to
175
+ * ride the error's structured context (LLM-caller discipline, v0.3.6+).
176
+ *
177
+ * `drift_axis === "hook-script"` is the ONLY value a caller may treat as
178
+ * retryable (run /kimi:setup for this host, retry once). Its absence means the
179
+ * refusal is not a moved-path problem and re-running setup will not fix it.
180
+ */
181
+ /**
182
+ * Caller-facing self-heal protocol, appended to every hook-refusal message.
183
+ *
184
+ * The slash-command wrappers are deliberately thin, so the retry protocol
185
+ * rides the refusal itself — the LLM caller reads it on stdout at exactly the
186
+ * moment it applies, and one source here serves both hosts and all surfaces
187
+ * (commands, agents, Codex skills) without duplicating it in each file.
188
+ * Mirrors the fuller contract in agents/*.md: re-pin via setup, retry ONCE,
189
+ * only on `retryable_after_setup: true` AND a zero setup exit; never the
190
+ * skip env. Embeds the resolved plugin root so the command is runnable as-is.
191
+ */
192
+ export function hookRefusalRetryProtocol(env) {
193
+ const root = env.CLAUDE_PLUGIN_ROOT ?? env.PLUGIN_ROOT ?? "${CLAUDE_PLUGIN_ROOT}";
194
+ return (`If details shows retryable_after_setup:true (routine after a plugin update — the ` +
195
+ `version-stamped hook path moved), run \`${root}/scripts/companion.sh setup\` and, only ` +
196
+ `if it exits 0, retry the refused command once. Anything else — or a second refusal — ` +
197
+ `surface the refusal instead of retrying.`);
198
+ }
199
+ export function hookRefusalDetails(status) {
200
+ return {
201
+ config_path: status.configPath,
202
+ // Routes the caller to "tell the human to repair Node" instead of the
203
+ // default "run /kimi:setup and retry" reflex. Never co-occurs with `drift`:
204
+ // drift requires the equality check to FAIL, this requires it to PASS.
205
+ ...(status.refusalKind !== undefined
206
+ ? { refusal_kind: status.refusalKind, node_bin: status.nodeBin }
207
+ : {}),
208
+ ...(status.drift !== undefined
209
+ ? {
210
+ drift_axis: status.drift.axis,
211
+ drift_installed_command: status.drift.installedCommand,
212
+ drift_expected_command: status.drift.expectedCommand,
213
+ // Retryable exactly when EVERY differing token is benign:
214
+ // - the hook script moved → the install path is version-stamped, so
215
+ // setup re-pins it to the running install.
216
+ // - the node token changed spelling but names the SAME file → not
217
+ // an interpreter move (this is the v1.9.0 re-pin, which also
218
+ // moves the script, hence axis "both").
219
+ // A node token naming a DIFFERENT file is never retryable: the pinned
220
+ // interpreter may be gone, and a hook that cannot spawn exits 127,
221
+ // which kimi-code treats as ALLOW. Re-pinning would paper over a real
222
+ // enforcement gap instead of surfacing it.
223
+ retryable_after_setup: status.drift.axis === "hook-script" || status.drift.nodeInterpreterUnchanged === true,
224
+ }
225
+ : {}),
226
+ };
227
+ }
228
+ export function formatHookMissingWarning(status, commandLabel) {
229
+ return [
230
+ "",
231
+ "WARNING: kimi-plugin-cc safety hook is NOT installed (or is invalid).",
232
+ ` Command: ${commandLabel}`,
233
+ ` Config: ${status.configPath}`,
234
+ ` Reason: ${status.reason ?? "unknown"}`,
235
+ "",
236
+ " Without a valid PreToolUse hook, kimi-code's `-p` mode auto-approves",
237
+ " every tool call — including Bash, Write, Edit — even from commands",
238
+ " documented as read-only.",
239
+ "",
240
+ " This command will not start a Kimi model run until enforcement is",
241
+ " repaired (the review gate skips instead of blocking stop).",
242
+ "",
243
+ " Fix: run Claude Code `/kimi:setup` or Codex `$kimi-setup` to install",
244
+ " or repair this host's managed block in",
245
+ " ~/.kimi-code/config.toml. If you use nvm, asdf, mise, or fnm, you",
246
+ " must re-run `/kimi:setup` after any Node version switch — the",
247
+ " verifier pins the absolute Node binary path and a switch invalidates",
248
+ " the previously-installed block by design. See docs/safety.md.",
249
+ "",
250
+ " KIMI_PLUGIN_CC_SKIP_HOOK_CHECK=1 explicitly bypasses hook-verification",
251
+ " refusals and restores un-enforced `permission: auto` execution. It does",
252
+ " NOT bypass the experimental-v2 safety refusal. Reserve it for tests or",
253
+ " diagnostics where the hook risk is intentional.",
254
+ "",
255
+ ].join("\n");
256
+ }
257
+ /**
258
+ * Process-lifetime latch so commands don't spam the warning on every
259
+ * call inside a single test run.
260
+ */
261
+ let warnedThisProcess = false;
262
+ /**
263
+ * Emit the warning to stderr at most once per process.
264
+ *
265
+ * Why stderr rather than stdout: stdout is reserved for the command's
266
+ * load-bearing output (artifact prose, JSON envelopes). LLM-caller
267
+ * discipline says stderr is humans-only, and this warning is exactly
268
+ * that — a developer-facing nudge to run /kimi:setup before tagging.
269
+ */
270
+ export function maybeWarnHookMissing(status, commandLabel, stderr = process.stderr) {
271
+ if (status.installed)
272
+ return;
273
+ if (warnedThisProcess)
274
+ return;
275
+ warnedThisProcess = true;
276
+ stderr.write(formatHookMissingWarning(status, commandLabel));
277
+ }
278
+ /** Test hook — resets the once-per-process latch. */
279
+ export function __resetHookMissingWarning() {
280
+ warnedThisProcess = false;
281
+ }