cc-safety-net 1.0.2 → 1.0.4

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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Claude Code](https://img.shields.io/badge/Claude%20Code-D27656)](#claude-code-installation)
8
8
  [![Copilot CLI](https://img.shields.io/badge/Copilot%20CLI-4EA5C9)](#github-copilot-cli-installation)
9
9
  [![Gemini CLI](https://img.shields.io/badge/Gemini%20CLI-678AE3)](#gemini-cli-installation)
10
- [![Kimi CLI](https://img.shields.io/badge/Kimi%20CLI-5587FF)](#kimi-cli-installation)
10
+ [![Kimi Code](https://img.shields.io/badge/Kimi%20Code-5587FF)](#kimi-code-installation)
11
11
  [![OpenCode](https://img.shields.io/badge/OpenCode-black)](#opencode-installation)
12
12
  [![Pi](https://img.shields.io/badge/Pi%20Coding-22262E)](#pi-installation)
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-red.svg)](https://opensource.org/licenses/MIT)
@@ -31,7 +31,7 @@ A Coding Agent CLI plugin that acts as a safety net, catching destructive git an
31
31
  - [Claude Code Installation](#claude-code-installation)
32
32
  - [Gemini CLI Installation](#gemini-cli-installation)
33
33
  - [GitHub Copilot CLI Installation](#github-copilot-cli-installation)
34
- - [Kimi CLI Installation](#kimi-cli-installation)
34
+ - [Kimi Code Installation](#kimi-code-installation)
35
35
  - [OpenCode Installation](#opencode-installation)
36
36
  - [Pi Installation](#pi-installation)
37
37
  - [Status Line Integration](#status-line-integration)
@@ -218,12 +218,12 @@ gemini extensions install https://github.com/kenryu42/gemini-safety-net
218
218
 
219
219
  ---
220
220
 
221
- ### Kimi CLI Installation
221
+ ### Kimi Code Installation
222
222
 
223
- Install CC Safety Net into your Kimi CLI config:
223
+ Install CC Safety Net into your Kimi Code config:
224
224
 
225
225
  ```bash
226
- npx -y cc-safety-net hook install --kimi-cli
226
+ npx -y cc-safety-net hook install --kimi-code
227
227
  ```
228
228
 
229
229
  ---
@@ -3,13 +3,54 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
3
3
 
4
4
  // node_modules/shell-quote/quote.js
5
5
  var require_quote = __commonJS((exports, module) => {
6
+ var OPS = [
7
+ "||",
8
+ "&&",
9
+ ";;",
10
+ "|&",
11
+ "<(",
12
+ "<<<",
13
+ ">>",
14
+ ">&",
15
+ "<&",
16
+ "&",
17
+ ";",
18
+ "(",
19
+ ")",
20
+ "|",
21
+ "<",
22
+ ">"
23
+ ];
24
+ var LINE_TERMINATORS = /[\n\r\u2028\u2029]/;
25
+ var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
6
26
  module.exports = function quote(xs) {
7
27
  return xs.map(function(s) {
8
28
  if (s === "") {
9
29
  return "''";
10
30
  }
11
31
  if (s && typeof s === "object") {
12
- return s.op.replace(/(.)/g, "\\$1");
32
+ if (s.op === "glob") {
33
+ if (typeof s.pattern !== "string") {
34
+ throw new TypeError("glob token requires a string `pattern`");
35
+ }
36
+ if (LINE_TERMINATORS.test(s.pattern)) {
37
+ throw new TypeError("glob `pattern` must not contain line terminators");
38
+ }
39
+ return s.pattern.replace(GLOB_SHELL_SPECIAL, "\\$&");
40
+ }
41
+ if (typeof s.op === "string") {
42
+ if (OPS.indexOf(s.op) < 0) {
43
+ throw new TypeError("invalid `op` value: " + JSON.stringify(s.op));
44
+ }
45
+ return s.op.replace(/[\s\S]/g, "\\$&");
46
+ }
47
+ if (typeof s.comment === "string") {
48
+ if (LINE_TERMINATORS.test(s.comment)) {
49
+ throw new TypeError("`comment` must not contain line terminators");
50
+ }
51
+ return "#" + s.comment;
52
+ }
53
+ throw new TypeError("unrecognized object token shape");
13
54
  }
14
55
  if (/["\s\\]/.test(s) && !/'/.test(s)) {
15
56
  return "'" + s.replace(/(['])/g, "\\$1") + "'";
@@ -6387,8 +6428,8 @@ var CLAUDE_CODE_HOOK_EVENT = "PreToolUse";
6387
6428
  var CLAUDE_CODE_TOOL_NAME = "Bash";
6388
6429
  var GEMINI_CLI_HOOK_EVENT = "BeforeTool";
6389
6430
  var GEMINI_CLI_TOOL_NAME = "run_shell_command";
6390
- var KIMI_CLI_HOOK_EVENT = "PreToolUse";
6391
- var KIMI_CLI_TOOL_NAME = "Shell";
6431
+ var KIMI_CODE_HOOK_EVENT = "PreToolUse";
6432
+ var KIMI_CODE_TOOL_NAME = "Shell";
6392
6433
 
6393
6434
  // src/bin/hook/claude-code.ts
6394
6435
  async function runClaudeCodeHook() {
@@ -6437,8 +6478,8 @@ async function runGeminiCLIHook() {
6437
6478
  });
6438
6479
  }
6439
6480
 
6440
- // src/bin/hook/kimi-cli.ts
6441
- async function runKimiCliHook() {
6481
+ // src/bin/hook/kimi-code.ts
6482
+ async function runKimiCodeHook() {
6442
6483
  await runConfiguredHookAdapter({
6443
6484
  createDenyOutput: (message) => ({
6444
6485
  hookSpecificOutput: {
@@ -6447,7 +6488,7 @@ async function runKimiCliHook() {
6447
6488
  permissionDecisionReason: message
6448
6489
  }
6449
6490
  }),
6450
- isSupported: (input) => input.hook_event_name === KIMI_CLI_HOOK_EVENT && input.tool_name === KIMI_CLI_TOOL_NAME,
6491
+ isSupported: (input) => input.hook_event_name === KIMI_CODE_HOOK_EVENT && input.tool_name === KIMI_CODE_TOOL_NAME,
6451
6492
  getCommand: (input) => input.tool_input?.command,
6452
6493
  getCwd: (input) => input.cwd,
6453
6494
  getSessionId: (input) => input.session_id
@@ -6495,12 +6536,12 @@ var integrationMetadata = [
6495
6536
  }
6496
6537
  },
6497
6538
  {
6498
- id: "kimi-cli",
6499
- displayName: "Kimi CLI",
6539
+ id: "kimi-code",
6540
+ displayName: "Kimi Code",
6500
6541
  doctorVisible: true,
6501
6542
  runtimeHook: {
6502
- flags: ["-kc", "--kimi-cli"],
6503
- description: "Run as Kimi CLI PreToolUse hook",
6543
+ flags: ["-kc", "--kimi-code"],
6544
+ description: "Run as Kimi Code PreToolUse hook",
6504
6545
  legacyTopLevel: false,
6505
6546
  order: 4
6506
6547
  }
@@ -6533,7 +6574,7 @@ var hookRunners = {
6533
6574
  "claude-code": runClaudeCodeHook,
6534
6575
  "copilot-cli": runCopilotCliHook,
6535
6576
  "gemini-cli": runGeminiCLIHook,
6536
- "kimi-cli": runKimiCliHook
6577
+ "kimi-code": runKimiCodeHook
6537
6578
  };
6538
6579
  var hookIntegrations = runtimeHookIntegrationMetadata.map((integration) => ({
6539
6580
  ...integration,
@@ -6557,8 +6598,8 @@ var hookCommand = {
6557
6598
  description: "Run as an agent CLI hook (reads JSON from stdin)",
6558
6599
  usage: "hook <coding cli>",
6559
6600
  subcommands: [
6560
- { usage: "install --kimi-cli", description: "Install Kimi CLI hook config" },
6561
- { usage: "uninstall --kimi-cli", description: "Uninstall Kimi CLI hook config" }
6601
+ { usage: "install --kimi-code", description: "Install Kimi Code hook config" },
6602
+ { usage: "uninstall --kimi-code", description: "Uninstall Kimi Code hook config" }
6562
6603
  ],
6563
6604
  options: [
6564
6605
  ...platformOptions,
@@ -6567,7 +6608,7 @@ var hookCommand = {
6567
6608
  description: "Show this help"
6568
6609
  }
6569
6610
  ],
6570
- examples: [...platformExamples, "cc-safety-net hook install --kimi-cli"]
6611
+ examples: [...platformExamples, "cc-safety-net hook install --kimi-code"]
6571
6612
  };
6572
6613
 
6573
6614
  // src/bin/commands/rule.ts
@@ -7216,7 +7257,7 @@ function formatSystemInfoTable(system) {
7216
7257
  { label: "Codex", value: system.codexCliVersion },
7217
7258
  { label: "Copilot CLI", value: system.copilotCliVersion },
7218
7259
  { label: "Gemini CLI", value: system.geminiCliVersion },
7219
- { label: "Kimi CLI", value: system.kimiCliVersion },
7260
+ { label: "Kimi Code", value: system.kimiCodeVersion },
7220
7261
  { label: "OpenCode", value: system.openCodeVersion },
7221
7262
  { label: "Pi", value: system.piCliVersion },
7222
7263
  { label: "Node.js", value: system.nodeVersion },
@@ -7260,7 +7301,7 @@ var CLAUDE_PLUGIN_LIST_CONFIG_PATH = "claude plugin list";
7260
7301
  var CLAUDE_SAFETY_NET_PLUGIN_ID = "safety-net@cc-marketplace";
7261
7302
  var GEMINI_EXTENSIONS_LIST_CONFIG_PATH = "gemini extensions list";
7262
7303
  var GEMINI_SAFETY_NET_SOURCE = "https://github.com/kenryu42/gemini-safety-net";
7263
- var KIMI_HOOK_COMMAND_PATTERN = /cc-safety-net\s+hook\s+(?:[^\s]+\s+)*--kimi-cli(\s|["']|$)/;
7304
+ var KIMI_HOOK_COMMAND_PATTERN = /cc-safety-net\s+hook\s+(?:[^\s]+\s+)*--kimi-code(\s|["']|$)/;
7264
7305
  var CODEX_PLUGIN_HOOKS_WARNING = "Codex plugin hooks are behind a feature flag. Add `plugin_hooks = true` under [features] in $CODEX_HOME/config.toml.";
7265
7306
  var CODEX_SAFETY_NET_PLUGIN_ID = "safety-net@cc-marketplace";
7266
7307
  var SELF_TEST_CASES = [
@@ -7492,25 +7533,25 @@ function detectGeminiCLI(extensionsListOutput) {
7492
7533
  function _getKimiConfigPath(homeDir) {
7493
7534
  return join10(process.env.KIMI_SHARE_DIR || join10(homeDir, ".kimi"), "config.toml");
7494
7535
  }
7495
- function detectKimiCLI(homeDir) {
7536
+ function detectKimiCode(homeDir) {
7496
7537
  const configPath = _getKimiConfigPath(homeDir);
7497
7538
  if (!existsSync13(configPath)) {
7498
- return { platform: "kimi-cli", status: "n/a", configPath };
7539
+ return { platform: "kimi-code", status: "n/a", configPath };
7499
7540
  }
7500
7541
  try {
7501
7542
  if (!KIMI_HOOK_COMMAND_PATTERN.test(readFileSync10(configPath, "utf-8"))) {
7502
- return { platform: "kimi-cli", status: "n/a", configPath };
7543
+ return { platform: "kimi-code", status: "n/a", configPath };
7503
7544
  }
7504
7545
  } catch (e) {
7505
7546
  return {
7506
- platform: "kimi-cli",
7547
+ platform: "kimi-code",
7507
7548
  status: "n/a",
7508
7549
  configPath,
7509
7550
  errors: [`Failed to read ${configPath}: ${e instanceof Error ? e.message : String(e)}`]
7510
7551
  };
7511
7552
  }
7512
7553
  return {
7513
- platform: "kimi-cli",
7554
+ platform: "kimi-code",
7514
7555
  status: "configured",
7515
7556
  method: "hook config",
7516
7557
  configPath,
@@ -7869,8 +7910,8 @@ function detectAllHooks(cwd, options2) {
7869
7910
  return detectGeminiCLI(options2?.geminiExtensionsListOutput);
7870
7911
  case "copilot-cli":
7871
7912
  return detectCopilotCLI();
7872
- case "kimi-cli":
7873
- return detectKimiCLI(homeDir);
7913
+ case "kimi-code":
7914
+ return detectKimiCode(homeDir);
7874
7915
  case "pi":
7875
7916
  return detectPi(options2?.piSafetyNetProbe);
7876
7917
  case "codex":
@@ -7886,7 +7927,7 @@ import { existsSync as existsSync14 } from "node:fs";
7886
7927
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
7887
7928
  import { tmpdir as tmpdir4 } from "node:os";
7888
7929
  import { delimiter, extname, join as join11 } from "node:path";
7889
- var CURRENT_VERSION = "1.0.2";
7930
+ var CURRENT_VERSION = "1.0.4";
7890
7931
  var VERSION_FETCH_TIMEOUT_MS = 2000;
7891
7932
  var PI_PROBE_TIMEOUT_MS = 5000;
7892
7933
  var PI_SENTINEL_COMMAND = "cc-safety-net";
@@ -8249,7 +8290,7 @@ async function getSystemInfo(fetcher = defaultVersionFetcher, options2 = {}) {
8249
8290
  geminiCliVersion: parseVersion(geminiRaw),
8250
8291
  geminiExtensionsListOutput,
8251
8292
  copilotCliVersion: parseVersion(copilotRaw),
8252
- kimiCliVersion: parseVersion(kimiRaw),
8293
+ kimiCodeVersion: parseVersion(kimiRaw),
8253
8294
  piCliVersion: parseVersion(piRaw),
8254
8295
  nodeVersion: parseVersion(nodeRaw),
8255
8296
  npmVersion: parseVersion(npmRaw),
@@ -9395,7 +9436,7 @@ function formatTraceJson(result) {
9395
9436
  return JSON.stringify(result, null, 2);
9396
9437
  }
9397
9438
  // src/bin/help.ts
9398
- var version = "1.0.2";
9439
+ var version = "1.0.4";
9399
9440
  var INDENT = " ";
9400
9441
  var PROGRAM_NAME = "cc-safety-net";
9401
9442
  function formatOptionFlags(option) {
@@ -9502,7 +9543,7 @@ function showCommandHelp(commandName) {
9502
9543
  // src/bin/hook/install.ts
9503
9544
  import { homedir as homedir6 } from "node:os";
9504
9545
 
9505
- // src/bin/hook/install/kimi-cli.ts
9546
+ // src/bin/hook/install/kimi-code.ts
9506
9547
  import { existsSync as existsSync16, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "node:fs";
9507
9548
  import { dirname as dirname9, join as join12 } from "node:path";
9508
9549
 
@@ -9590,8 +9631,8 @@ function removeArrayRangeItem(content, item) {
9590
9631
  return `${content.slice(0, removeStart)}${content.slice(removeEnd)}`;
9591
9632
  }
9592
9633
 
9593
- // src/bin/hook/install/kimi-cli.ts
9594
- var KIMI_HOOK_COMMAND = "npx -y cc-safety-net hook --kimi-cli";
9634
+ // src/bin/hook/install/kimi-code.ts
9635
+ var KIMI_HOOK_COMMAND = "npx -y cc-safety-net hook --kimi-code";
9595
9636
  var KIMI_HOOK_BLOCK = `[[hooks]]
9596
9637
  event = "PreToolUse"
9597
9638
  matcher = "Shell"
@@ -9626,8 +9667,8 @@ function skipTomlComment(content, index) {
9626
9667
  function findTomlArrayClose(content, openIndex) {
9627
9668
  return findMatchingBracket(content, openIndex, {
9628
9669
  skipComment: skipTomlComment,
9629
- stringError: "Unterminated string in Kimi CLI config",
9630
- bracketError: "Unmatched hooks array in Kimi CLI config"
9670
+ stringError: "Unterminated string in Kimi Code config",
9671
+ bracketError: "Unmatched hooks array in Kimi Code config"
9631
9672
  });
9632
9673
  }
9633
9674
  function findTopLevelInlineHooksArray(content) {
@@ -9686,7 +9727,7 @@ function removeKimiInlineHook(content, hooksRange) {
9686
9727
  end: itemStart + KIMI_INLINE_HOOK.length
9687
9728
  });
9688
9729
  }
9689
- function installKimiCli(homeDir) {
9730
+ function installKimiCode(homeDir) {
9690
9731
  const configPath = getKimiConfigPath(homeDir);
9691
9732
  mkdirSync4(dirname9(configPath), { recursive: true });
9692
9733
  if (!existsSync16(configPath)) {
@@ -9700,7 +9741,7 @@ function installKimiCli(homeDir) {
9700
9741
  writeFileSync3(configPath, appendKimiHook(content));
9701
9742
  return { path: configPath, alreadyInstalled: false };
9702
9743
  }
9703
- function uninstallKimiCli(homeDir) {
9744
+ function uninstallKimiCode(homeDir) {
9704
9745
  const configPath = getKimiConfigPath(homeDir);
9705
9746
  if (!existsSync16(configPath))
9706
9747
  return { path: configPath, alreadyInstalled: false };
@@ -9719,21 +9760,21 @@ function getHomeDir() {
9719
9760
  return process.env.HOME ?? homedir6();
9720
9761
  }
9721
9762
  function parseInstallTarget(args, action) {
9722
- const unknownOption = args.find((arg) => arg.startsWith("-") && !["--kimi-cli"].includes(arg));
9763
+ const unknownOption = args.find((arg) => arg.startsWith("-") && !["--kimi-code"].includes(arg));
9723
9764
  if (unknownOption)
9724
9765
  throw new Error(`Unknown install option: ${unknownOption}`);
9725
9766
  const unexpectedArg = args.find((arg) => !arg.startsWith("-"));
9726
9767
  if (unexpectedArg)
9727
9768
  throw new Error(`Unexpected argument for hook ${action}: ${unexpectedArg}`);
9728
- if (!args.includes("--kimi-cli"))
9729
- throw new Error("Choose exactly one install target: --kimi-cli");
9769
+ if (!args.includes("--kimi-code"))
9770
+ throw new Error("Choose exactly one install target: --kimi-code");
9730
9771
  }
9731
9772
  function runHookInstallCommand(action, args) {
9732
9773
  try {
9733
9774
  parseInstallTarget(args, action);
9734
9775
  const homeDir = getHomeDir();
9735
- const result = action === "install" ? installKimiCli(homeDir) : uninstallKimiCli(homeDir);
9736
- const name = "Kimi CLI";
9776
+ const result = action === "install" ? installKimiCode(homeDir) : uninstallKimiCode(homeDir);
9777
+ const name = "Kimi Code";
9737
9778
  const pastTense = action === "install" ? "Installed" : "Uninstalled";
9738
9779
  console.log(action === "install" && result.alreadyInstalled ? `${name} hook already installed in ${result.path}` : action === "uninstall" && !result.alreadyInstalled ? `${name} hook not installed in ${result.path}` : `${pastTense} ${name} hook ${action === "install" ? "in" : "from"} ${result.path}`);
9739
9780
  return 0;
@@ -10751,7 +10792,7 @@ var commandParsers = {
10751
10792
  const integration = findHookIntegrationByFlag(args);
10752
10793
  if (integration)
10753
10794
  return { mode: "hook", integration };
10754
- console.error("hook requires a subcommand or integration flag. Try: cc-safety-net hook install --kimi-cli");
10795
+ console.error("hook requires a subcommand or integration flag. Try: cc-safety-net hook install --kimi-code");
10755
10796
  showCommandHelp("hook");
10756
10797
  process.exit(1);
10757
10798
  },
@@ -122,8 +122,8 @@ export interface SystemInfo {
122
122
  geminiExtensionsListOutput: string | null;
123
123
  /** Copilot CLI version (from `copilot --binary-version`, falling back to `copilot --version`) */
124
124
  copilotCliVersion: string | null;
125
- /** Kimi CLI version (from `kimi --version`) */
126
- kimiCliVersion: string | null;
125
+ /** Kimi Code version (from `kimi --version`) */
126
+ kimiCodeVersion: string | null;
127
127
  /** Pi CLI version (from `pi --version`) */
128
128
  piCliVersion: string | null;
129
129
  /** Node.js version (from `node --version`) */
@@ -2,5 +2,5 @@ export declare const CLAUDE_CODE_HOOK_EVENT = "PreToolUse";
2
2
  export declare const CLAUDE_CODE_TOOL_NAME = "Bash";
3
3
  export declare const GEMINI_CLI_HOOK_EVENT = "BeforeTool";
4
4
  export declare const GEMINI_CLI_TOOL_NAME = "run_shell_command";
5
- export declare const KIMI_CLI_HOOK_EVENT = "PreToolUse";
6
- export declare const KIMI_CLI_TOOL_NAME = "Shell";
5
+ export declare const KIMI_CODE_HOOK_EVENT = "PreToolUse";
6
+ export declare const KIMI_CODE_TOOL_NAME = "Shell";
@@ -0,0 +1,3 @@
1
+ import type { InstallResult } from '@/bin/hook/install/types';
2
+ export declare function installKimiCode(homeDir: string): InstallResult;
3
+ export declare function uninstallKimiCode(homeDir: string): InstallResult;
@@ -0,0 +1 @@
1
+ export declare function runKimiCodeHook(): Promise<void>;
@@ -33,12 +33,12 @@ declare const integrationMetadata: readonly [{
33
33
  readonly order: 3;
34
34
  };
35
35
  }, {
36
- readonly id: "kimi-cli";
37
- readonly displayName: "Kimi CLI";
36
+ readonly id: "kimi-code";
37
+ readonly displayName: "Kimi Code";
38
38
  readonly doctorVisible: true;
39
39
  readonly runtimeHook: {
40
- readonly flags: readonly ["-kc", "--kimi-cli"];
41
- readonly description: "Run as Kimi CLI PreToolUse hook";
40
+ readonly flags: readonly ["-kc", "--kimi-code"];
41
+ readonly description: "Run as Kimi Code PreToolUse hook";
42
42
  readonly legacyTopLevel: false;
43
43
  readonly order: 4;
44
44
  };
@@ -56,12 +56,12 @@ type RuntimeHookIntegrationMetadata = Extract<(typeof integrationMetadata)[numbe
56
56
  runtimeHook: object;
57
57
  }>;
58
58
  export type RuntimeHookIntegrationId = RuntimeHookIntegrationMetadata['id'];
59
- export declare const doctorIntegrationOrder: ("claude-code" | "codex" | "copilot-cli" | "gemini-cli" | "kimi-cli" | "opencode" | "pi")[];
59
+ export declare const doctorIntegrationOrder: ("claude-code" | "codex" | "copilot-cli" | "gemini-cli" | "kimi-code" | "opencode" | "pi")[];
60
60
  export declare const runtimeHookIntegrationMetadata: {
61
- id: "claude-code" | "copilot-cli" | "gemini-cli" | "kimi-cli";
62
- displayName: "Claude Code" | "Copilot CLI" | "Gemini CLI" | "Kimi CLI";
63
- flags: readonly ["-cc", "--claude-code"] | readonly ["-cp", "--copilot-cli"] | readonly ["-gc", "--gemini-cli"] | readonly ["-kc", "--kimi-cli"];
64
- description: "Run as Claude Code PreToolUse hook" | "Run as Copilot CLI PreToolUse hook" | "Run as Gemini CLI BeforeTool hook" | "Run as Kimi CLI PreToolUse hook";
61
+ id: "claude-code" | "copilot-cli" | "gemini-cli" | "kimi-code";
62
+ displayName: "Claude Code" | "Copilot CLI" | "Gemini CLI" | "Kimi Code";
63
+ flags: readonly ["-cc", "--claude-code"] | readonly ["-cp", "--copilot-cli"] | readonly ["-gc", "--gemini-cli"] | readonly ["-kc", "--kimi-code"];
64
+ description: "Run as Claude Code PreToolUse hook" | "Run as Copilot CLI PreToolUse hook" | "Run as Gemini CLI BeforeTool hook" | "Run as Kimi Code PreToolUse hook";
65
65
  legacyTopLevel: boolean;
66
66
  }[];
67
67
  export declare function getIntegrationDisplayName(id: IntegrationId): string;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,15 @@
1
- import type { Plugin } from '@opencode-ai/plugin';
2
- export declare const CCSafetyNetPlugin: Plugin;
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ type CCSafetyNetPluginInput = PluginInput & {
3
+ homeDir?: string;
4
+ };
5
+ export declare const CCSafetyNetPlugin: ({ directory, homeDir }: CCSafetyNetPluginInput) => Promise<{
6
+ config: (opencodeConfig: Record<string, unknown>) => Promise<void>;
7
+ 'tool.execute.before': (input: {
8
+ tool: string;
9
+ sessionID: string;
10
+ callID: string;
11
+ }, output: {
12
+ args: any;
13
+ }) => Promise<void>;
14
+ }>;
15
+ export {};
package/dist/index.js CHANGED
@@ -2,13 +2,54 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
2
2
 
3
3
  // node_modules/shell-quote/quote.js
4
4
  var require_quote = __commonJS((exports, module) => {
5
+ var OPS = [
6
+ "||",
7
+ "&&",
8
+ ";;",
9
+ "|&",
10
+ "<(",
11
+ "<<<",
12
+ ">>",
13
+ ">&",
14
+ "<&",
15
+ "&",
16
+ ";",
17
+ "(",
18
+ ")",
19
+ "|",
20
+ "<",
21
+ ">"
22
+ ];
23
+ var LINE_TERMINATORS = /[\n\r\u2028\u2029]/;
24
+ var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
5
25
  module.exports = function quote(xs) {
6
26
  return xs.map(function(s) {
7
27
  if (s === "") {
8
28
  return "''";
9
29
  }
10
30
  if (s && typeof s === "object") {
11
- return s.op.replace(/(.)/g, "\\$1");
31
+ if (s.op === "glob") {
32
+ if (typeof s.pattern !== "string") {
33
+ throw new TypeError("glob token requires a string `pattern`");
34
+ }
35
+ if (LINE_TERMINATORS.test(s.pattern)) {
36
+ throw new TypeError("glob `pattern` must not contain line terminators");
37
+ }
38
+ return s.pattern.replace(GLOB_SHELL_SPECIAL, "\\$&");
39
+ }
40
+ if (typeof s.op === "string") {
41
+ if (OPS.indexOf(s.op) < 0) {
42
+ throw new TypeError("invalid `op` value: " + JSON.stringify(s.op));
43
+ }
44
+ return s.op.replace(/[\s\S]/g, "\\$&");
45
+ }
46
+ if (typeof s.comment === "string") {
47
+ if (LINE_TERMINATORS.test(s.comment)) {
48
+ throw new TypeError("`comment` must not contain line terminators");
49
+ }
50
+ return "#" + s.comment;
51
+ }
52
+ throw new TypeError("unrecognized object token shape");
12
53
  }
13
54
  if (/["\s\\]/.test(s) && !/'/.test(s)) {
14
55
  return "'" + s.replace(/(['])/g, "\\$1") + "'";
@@ -6146,6 +6187,66 @@ function analyzeCommand(command2, options2 = {}) {
6146
6187
  return analyzeCommandInternal(command2, 0, { ...options2, config });
6147
6188
  }
6148
6189
 
6190
+ // src/core/audit.ts
6191
+ import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "node:fs";
6192
+ import { homedir as homedir3 } from "node:os";
6193
+ import { join as join8 } from "node:path";
6194
+ function sanitizeSessionIdForFilename(sessionId) {
6195
+ const raw = sessionId.trim();
6196
+ if (!raw) {
6197
+ return null;
6198
+ }
6199
+ let safe = raw.replace(/[^A-Za-z0-9_.-]+/g, "_");
6200
+ safe = safe.replace(/^[._-]+|[._-]+$/g, "").slice(0, 128);
6201
+ if (!safe || safe === "." || safe === "..") {
6202
+ return null;
6203
+ }
6204
+ return safe;
6205
+ }
6206
+ function writeAuditLog(sessionId, command2, segment, reason, cwd, options2 = {}) {
6207
+ const safeSessionId = sanitizeSessionIdForFilename(sessionId);
6208
+ if (!safeSessionId) {
6209
+ return;
6210
+ }
6211
+ const home = options2.homeDir ?? process.env.HOME ?? homedir3();
6212
+ const logsDir = join8(home, ".cc-safety-net", "logs");
6213
+ try {
6214
+ if (!existsSync10(logsDir)) {
6215
+ mkdirSync3(logsDir, { recursive: true });
6216
+ }
6217
+ const logFile = join8(logsDir, `${safeSessionId}.jsonl`);
6218
+ const entry = {
6219
+ ts: new Date().toISOString(),
6220
+ decision: options2.decision ?? "deny",
6221
+ command: redactSecrets(command2).slice(0, 300),
6222
+ segment: redactSecrets(segment).slice(0, 300),
6223
+ reason,
6224
+ cwd
6225
+ };
6226
+ appendFileSync(logFile, `${JSON.stringify(entry)}
6227
+ `, "utf-8");
6228
+ } catch {}
6229
+ }
6230
+ function redactSecrets(text) {
6231
+ let result = text;
6232
+ result = result.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, "<redacted>");
6233
+ result = result.replace(/\b((?:DATABASE|POSTGRES|POSTGRESQL|MYSQL|MARIADB|REDIS|MONGO(?:DB)?|DB)_URL)=([^\s]+)/gi, "$1=<redacted>");
6234
+ result = result.replace(/\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASS|KEY|CREDENTIALS)[A-Z0-9_]*)=([^\s]+)/gi, "$1=<redacted>");
6235
+ result = result.replace(/(['"]?\s*(?:authorization|cookie|x-api-key|api-key)\s*:\s*)([^'"\r\n]+)(['"]?)/gi, "$1<redacted>$3");
6236
+ result = result.replace(/(['"]?\s*authorization\s*:\s*)([^'"]+)(['"]?)/gi, "$1<redacted>$3");
6237
+ result = result.replace(/(authorization\s*:\s*)([^\s"']+)(\s+[^\s"']+)?/gi, "$1<redacted>");
6238
+ result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)([^\s/:@]+):([^\s@/]+)@/gi, "$1<redacted>:<redacted>@");
6239
+ result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)([^\s/@:]+)@/gi, "$1<redacted>@");
6240
+ result = result.replace(/\bgh[pousr]_[A-Za-z0-9]{20,}\b/g, "<redacted>");
6241
+ result = result.replace(/\bxoxb-[A-Za-z0-9-]{20,}\b/g, "<redacted>");
6242
+ result = result.replace(/\bnpm_[A-Za-z0-9_]{20,}\b/g, "<redacted>");
6243
+ result = result.replace(/\b[rs]k_(?:live|test)_[A-Za-z0-9_]{20,}\b/g, "<redacted>");
6244
+ result = result.replace(/\bpypi-[A-Za-z0-9_-]{20,}\b/g, "<redacted>");
6245
+ result = result.replace(/\b[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}\b/g, "<redacted>");
6246
+ result = result.replace(/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g, "<redacted>");
6247
+ return result;
6248
+ }
6249
+
6149
6250
  // src/core/format.ts
6150
6251
  function formatBlockedMessage(input) {
6151
6252
  const { reason, command: command2, segment } = input;
@@ -6235,7 +6336,7 @@ function loadBuiltinCommands(disabledCommands) {
6235
6336
  }
6236
6337
  // src/index.ts
6237
6338
  var REASON_SAFETY_NET_FAILED_CLOSED = "CC Safety Net failed closed because command analysis failed unexpectedly.";
6238
- var CCSafetyNetPlugin = async ({ directory }) => {
6339
+ var CCSafetyNetPlugin = async ({ directory, homeDir }) => {
6239
6340
  const modes = getCCSafetyNetEnvModes();
6240
6341
  return {
6241
6342
  config: async (opencodeConfig) => {
@@ -6267,6 +6368,11 @@ var CCSafetyNetPlugin = async ({ directory }) => {
6267
6368
  }));
6268
6369
  }
6269
6370
  if (result) {
6371
+ if (input.sessionID) {
6372
+ writeAuditLog(input.sessionID, command2, result.segment, result.reason, directory, {
6373
+ homeDir
6374
+ });
6375
+ }
6270
6376
  const message = formatBlockedMessage({
6271
6377
  reason: result.reason,
6272
6378
  command: command2,
package/dist/pi/index.js CHANGED
@@ -2,13 +2,54 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
2
2
 
3
3
  // node_modules/shell-quote/quote.js
4
4
  var require_quote = __commonJS((exports, module) => {
5
+ var OPS = [
6
+ "||",
7
+ "&&",
8
+ ";;",
9
+ "|&",
10
+ "<(",
11
+ "<<<",
12
+ ">>",
13
+ ">&",
14
+ "<&",
15
+ "&",
16
+ ";",
17
+ "(",
18
+ ")",
19
+ "|",
20
+ "<",
21
+ ">"
22
+ ];
23
+ var LINE_TERMINATORS = /[\n\r\u2028\u2029]/;
24
+ var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
5
25
  module.exports = function quote(xs) {
6
26
  return xs.map(function(s) {
7
27
  if (s === "") {
8
28
  return "''";
9
29
  }
10
30
  if (s && typeof s === "object") {
11
- return s.op.replace(/(.)/g, "\\$1");
31
+ if (s.op === "glob") {
32
+ if (typeof s.pattern !== "string") {
33
+ throw new TypeError("glob token requires a string `pattern`");
34
+ }
35
+ if (LINE_TERMINATORS.test(s.pattern)) {
36
+ throw new TypeError("glob `pattern` must not contain line terminators");
37
+ }
38
+ return s.pattern.replace(GLOB_SHELL_SPECIAL, "\\$&");
39
+ }
40
+ if (typeof s.op === "string") {
41
+ if (OPS.indexOf(s.op) < 0) {
42
+ throw new TypeError("invalid `op` value: " + JSON.stringify(s.op));
43
+ }
44
+ return s.op.replace(/[\s\S]/g, "\\$&");
45
+ }
46
+ if (typeof s.comment === "string") {
47
+ if (LINE_TERMINATORS.test(s.comment)) {
48
+ throw new TypeError("`comment` must not contain line terminators");
49
+ }
50
+ return "#" + s.comment;
51
+ }
52
+ throw new TypeError("unrecognized object token shape");
12
53
  }
13
54
  if (/["\s\\]/.test(s) && !/'/.test(s)) {
14
55
  return "'" + s.replace(/(['])/g, "\\$1") + "'";
package/dist/types.d.ts CHANGED
@@ -83,8 +83,8 @@ export interface GeminiHookOutput {
83
83
  stopReason?: string;
84
84
  suppressOutput?: boolean;
85
85
  }
86
- /** Kimi CLI hook input format */
87
- export interface KimiCliHookInput {
86
+ /** Kimi Code hook input format */
87
+ export interface KimiCodeHookInput {
88
88
  session_id?: string;
89
89
  cwd?: string;
90
90
  hook_event_name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safety-net",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A coding agent CLI hook - block destructive git and filesystem commands before execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -64,7 +64,7 @@
64
64
  ]
65
65
  },
66
66
  "dependencies": {
67
- "shell-quote": "^1.8.3"
67
+ "shell-quote": "^1.8.4"
68
68
  },
69
69
  "trustedDependencies": [
70
70
  "@ast-grep/cli"
@@ -1,3 +0,0 @@
1
- import type { InstallResult } from '@/bin/hook/install/types';
2
- export declare function installKimiCli(homeDir: string): InstallResult;
3
- export declare function uninstallKimiCli(homeDir: string): InstallResult;
@@ -1 +0,0 @@
1
- export declare function runKimiCliHook(): Promise<void>;