@cleocode/adapters 2026.10.1 → 2026.10.3

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.
@@ -4,10 +4,13 @@
4
4
  * Handles CLEO installation into Claude Code environments:
5
5
  * - Ensures CLAUDE.md has CLEO @-references
6
6
  * - Manages plugin registration in ~/.claude/settings.json
7
+ * - Installs the heavy-command `PreToolUse` hook in the PROJECT's
8
+ * `.claude/settings.local.json` (T12983)
7
9
  *
8
10
  * Migrated from src/core/install/claude-plugin.ts
9
11
  *
10
12
  * @task T5240
13
+ * @task T12983
11
14
  */
12
15
  import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
13
16
  /**
@@ -17,6 +20,9 @@ import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cle
17
20
  * 1. Ensuring CLAUDE.md contains @-references to CLEO instruction files
18
21
  * 2. Installing adapter-provided commands to .claude/commands/
19
22
  * 3. Registering the brain observation plugin in ~/.claude/settings.json
23
+ * 4. Installing PreCompact hook templates
24
+ * 5. Installing the heavy-command `PreToolUse` hook in the project's
25
+ * `.claude/settings.local.json`, when `options.heavyCommandHook` is set
20
26
  *
21
27
  * @remarks
22
28
  * Installation is idempotent -- running install multiple times on the same
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-code/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAyBjG;;;;;;;;;;;;;GAaG;AACH,qBAAa,yBAA0B,YAAW,sBAAsB;IACtE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAwD9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBrC;;;;;;;OAOG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAsBvB;;;;OAIG;YACW,cAAc;IAwB5B;;;;;;;;;;;;;;;;;;;OAmBG;YACW,oBAAoB;IAgClC;;;;;;;;;;;;;OAaG;YACW,sBAAsB;CAmBrC"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-code/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA6BjG;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,yBAA0B,YAAW,sBAAsB;IACtE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA0E9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBrC;;;;;;;OAOG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAsBvB;;;;OAIG;YACW,cAAc;IAwB5B;;;;;;;;;;;;;;;;;;;OAmBG;YACW,oBAAoB;IAgClC;;;;;;;;;;;;;OAaG;YACW,sBAAsB;CAmBrC"}
@@ -47,82 +47,5 @@ export declare class ClaudeCodePathProvider implements AdapterPathProvider {
47
47
  * ```
48
48
  */
49
49
  export declare function claudeSettingsPath(): string;
50
- /**
51
- * Whether a parsed settings value is a plain JSON object.
52
- *
53
- * @param value - Any parsed JSON value
54
- * @returns `true` for a non-null, non-array object
55
- *
56
- * @example
57
- * ```typescript
58
- * isPlainObject(settings.enabledPlugins);
59
- * ```
60
- */
61
- export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
62
- /**
63
- * Whether a settings.json hook entry carries a CLEO-owned command.
64
- *
65
- * @param entry - One element of a `hooks.<Event>` array
66
- * @param commandIncludes - Extra substring the CLEO command must contain
67
- * @returns `true` when one of the entry's commands has the `# cleo-hook` marker
68
- *
69
- * @example
70
- * ```typescript
71
- * isCleoHookEntry({ hooks: [{ type: "command", command: "x # cleo-hook" }] }); // true
72
- * ```
73
- */
74
- export declare function isCleoHookEntry(entry: unknown, commandIncludes?: string): boolean;
75
- /**
76
- * Whether a `hooks.<Event>` value holds a CLEO-owned entry.
77
- *
78
- * @param entries - The event's value (non-arrays hold none)
79
- * @param commandIncludes - Extra substring the CLEO command must contain
80
- * @returns `true` when a CLEO entry is present
81
- *
82
- * @example
83
- * ```typescript
84
- * hasCleoHook(hookMap(settings).Stop);
85
- * ```
86
- */
87
- export declare function hasCleoHook(entries: unknown, commandIncludes?: string): boolean;
88
- /**
89
- * Return the settings object's `hooks` map, creating an empty one if absent.
90
- *
91
- * @param settings - Parsed settings.json
92
- * @returns The live `hooks` object (mutations apply to `settings`)
93
- * @throws Error when `hooks` exists but is not an object — the caller's write
94
- * is aborted and the file left untouched
95
- *
96
- * @example
97
- * ```typescript
98
- * const hooks = hookMap(settings);
99
- * ```
100
- */
101
- export declare function hookMap(settings: Record<string, unknown>): Record<string, unknown>;
102
- /**
103
- * Append a hook entry to `hooks[event]`, preserving existing entries.
104
- *
105
- * @param hooks - The settings `hooks` map
106
- * @param event - Native event name (e.g. `Stop`, `PreCompact`)
107
- * @param entry - Entry to append
108
- * @throws Error when `hooks[event]` exists but is not an array
109
- *
110
- * @example
111
- * ```typescript
112
- * appendHookEntry(hookMap(settings), "Stop", { matcher: "", hooks: [] });
113
- * ```
114
- */
115
- export declare function appendHookEntry(hooks: Record<string, unknown>, event: string, entry: Record<string, unknown>): void;
116
- /**
117
- * Remove every CLEO-owned entry from every event array in `hooks`.
118
- *
119
- * @param hooks - The settings `hooks` map
120
- * @returns Whether anything was removed
121
- *
122
- * @example
123
- * ```typescript
124
- * const changed = removeCleoHookEntries(hookMap(settings));
125
- * ```
126
- */
127
- export declare function removeCleoHookEntries(hooks: Record<string, unknown>): boolean;
50
+ export { appendHookEntry, CLEO_HOOK_MARKER, hasCleoHook, hookMap, isCleoHookEntry, isPlainObject, removeCleoHookEntries, } from '../shared/hook-config.js';
128
51
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-code/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,uDAAuD;IACvD,cAAc,IAAI,MAAM;IAIxB,4EAA4E;IAC5E,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,4EAA4E;IAC5E,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAInC,8EAA8E;IAC9E,eAAe,IAAI,MAAM,GAAG,IAAI;CAGjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CASjF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWlF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAMN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAW7E"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-code/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,uDAAuD;IACvD,cAAc,IAAI,MAAM;IAIxB,4EAA4E;IAC5E,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,4EAA4E;IAC5E,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAInC,8EAA8E;IAC9E,eAAe,IAAI,MAAM,GAAG,IAAI;CAGjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,OAAO,EACP,eAAe,EACf,aAAa,EACb,qBAAqB,GACtB,MAAM,0BAA0B,CAAC"}
@@ -3,9 +3,12 @@
3
3
  *
4
4
  * Handles CLEO installation into Codex CLI environments:
5
5
  * - Ensures AGENTS.md has CLEO @-references via CAAMP
6
+ * - Installs the heavy-command `PreToolUse` hook in the project's
7
+ * `.codex/hooks.json` (T12983)
6
8
  *
7
9
  * @task T162
8
10
  * @task T9019
11
+ * @task T12983
9
12
  * @epic T134
10
13
  */
11
14
  import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
@@ -15,11 +18,14 @@ import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cle
15
18
  * Manages CLEO's integration with Codex CLI by:
16
19
  * 1. Ensuring AGENTS.md contains @-references to CLEO instruction files
17
20
  * (delegated to CAAMP's canonical {@link ensureProviderInstructionFile}).
21
+ * 2. Installing the heavy-command `PreToolUse` hook (matcher `Bash`) in the
22
+ * project's `.codex/hooks.json`, when `options.heavyCommandHook` is set.
23
+ * Codex asks the user to review and trust a new project hook (`/hooks`)
24
+ * before it runs it.
18
25
  *
19
26
  * @remarks
20
27
  * Installation is idempotent — running install multiple times on the same
21
- * project produces the same result. Only AGENTS.md is managed; Codex CLI
22
- * does not have an MCP or plugin registration mechanism.
28
+ * project produces the same result.
23
29
  *
24
30
  * @task T162
25
31
  * @epic T134
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/codex/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,oBAAqB,YAAW,sBAAsB;IACjE;;;;;;OAMG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAwB9D;;;;;OAKG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAerC;;;;;;;OAOG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrE"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/codex/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIjG;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAqB,YAAW,sBAAsB;IACjE;;;;;;OAMG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAyC9D;;;;;OAKG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAerC;;;;;;;OAOG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrE"}
@@ -21,6 +21,21 @@ import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cle
21
21
  * project produces the same result. Only AGENTS.md is managed; Kimi does
22
22
  * not have an MCP or plugin registration mechanism.
23
23
  *
24
+ * The heavy-command hook (T12983) is NOT installed for Kimi: Kimi reads
25
+ * `[[hooks]]` only from the user-global `~/.kimi/config.toml` (no project
26
+ * config, and CLEO installs this hook per project only), and its
27
+ * `PreToolUse` answer cannot rewrite the command. `cleo hook heavy-command
28
+ * --provider kimi` speaks Kimi's protocol (deny with the governed command as
29
+ * the reason) for a manual entry:
30
+ *
31
+ * ```toml
32
+ * [[hooks]]
33
+ * event = "PreToolUse"
34
+ * matcher = "Shell"
35
+ * command = "cleo hook heavy-command --provider kimi"
36
+ * timeout = 20
37
+ * ```
38
+ *
24
39
  * @task T163
25
40
  * @epic T134
26
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/kimi/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAChE;;;;;;OAMG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAuB9D;;;;;OAKG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiBrC;;;;;;;OAOG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrE"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/kimi/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAChE;;;;;;OAMG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAuB9D;;;;;OAKG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiBrC;;;;;;;OAOG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrE"}
@@ -4,10 +4,12 @@
4
4
  * Handles CLEO installation into OpenCode environments:
5
5
  * - Ensures AGENTS.md has CLEO @-references via CAAMP
6
6
  * - Installs PreCompact hook shell shims + a JS plugin wrapper (T1013)
7
+ * - Installs the heavy-command `tool.execute.before` plugin (T12983)
7
8
  *
8
9
  * @task T5240
9
10
  * @task T1013
10
11
  * @task T9019
12
+ * @task T12983
11
13
  */
12
14
  import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
13
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/opencode/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOjG;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAwB,YAAW,sBAAsB;IACpE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAgC9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAerC;;;;;;OAMG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpE;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,oBAAoB;IAiC5B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,qBAAqB;CA0C9B"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/opencode/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWjG;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAwB,YAAW,sBAAsB;IACpE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAiD9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAerC;;;;;;OAMG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpE;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,oBAAoB;IAiC5B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,qBAAqB;CA0C9B"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Install CLEO's heavy-command hook into a project's provider configs (T12983).
3
+ *
4
+ * The hook (`cleo hook heavy-command`, in `@cleocode/cleo`) runs before every
5
+ * shell command an agent issues and routes heavy work (tests, builds,
6
+ * typechecks, installs) through `cleo run`, the machine-wide resource budget.
7
+ * Delivery per provider:
8
+ *
9
+ * - Claude Code: a `PreToolUse` / `Bash` hook in
10
+ * `<project>/.claude/settings.local.json`: project-level but per machine,
11
+ * which fits a per-machine budget. When git does not ignore that file yet,
12
+ * a marked block in the repository's `info/exclude` keeps it out of git.
13
+ * - Codex: the same hook in `<project>/.codex/hooks.json` (Codex asks the
14
+ * user to review and trust a new project hook in `/hooks` before it runs).
15
+ * - opencode: a `tool.execute.before` / `tool.execute.after` plugin at
16
+ * `<project>/.opencode/plugins/cleo-heavy-command.js`.
17
+ * - Kimi: not installed. Kimi reads hooks only from the global
18
+ * `~/.kimi/config.toml` and cannot rewrite input; `cleo hook heavy-command
19
+ * --provider kimi` speaks its protocol for a manual `[[hooks]]` entry.
20
+ *
21
+ * Project-level only: CLEO never writes this hook into a user-global config,
22
+ * and never into the home directory's provider configs. The hook command
23
+ * decides cheaply before it starts `cleo` (a tool name must appear as a whole
24
+ * word), guards against a missing or older `cleo`, and always exits 0. Only
25
+ * CLEO's own hook object is ever added, replaced or removed: a user hook that
26
+ * shares its matcher group stays. Mode `off` removes it again.
27
+ *
28
+ * @task T12983
29
+ * @epic T12978
30
+ */
31
+ import type { HeavyCommandHookMode } from '@cleocode/contracts';
32
+ /** Identifies the heavy-command hook among CLEO's hook commands. */
33
+ export declare const HEAVY_COMMAND_HOOK_ID = "cleo hook heavy-command";
34
+ /** Seconds a harness waits for the hook before it runs the command unchanged. */
35
+ export declare const HEAVY_COMMAND_HOOK_TIMEOUT_SEC = 20;
36
+ /** File name of the generated opencode plugin. */
37
+ export declare const OPENCODE_HEAVY_COMMAND_PLUGIN = "cleo-heavy-command.js";
38
+ /**
39
+ * Tool names that send a shell call on to `cleo hook`: a superset of the
40
+ * command words `run-class` treats as heavy (test runners, compilers,
41
+ * bundlers, linters, package managers and their exec runners, cargo, go).
42
+ * Matched as whole words only, so `cat test-notes.md` never starts `cleo`;
43
+ * recognition proper stays in `run-class` inside `cleo hook`.
44
+ */
45
+ export declare const HEAVY_TOOL_WORDS: readonly string[];
46
+ /**
47
+ * What a sync did to one config file. `skipped`: the project is the user's
48
+ * home directory, whose provider configs are the user-global ones.
49
+ */
50
+ export type HeavyHookSyncResult = 'installed' | 'updated' | 'removed' | 'unchanged' | 'skipped';
51
+ /**
52
+ * Whether `projectDir` is the user's home directory. There,
53
+ * `<home>/.claude/settings.local.json`, `<home>/.codex/hooks.json` and
54
+ * `<home>/.opencode/` act as user-global configs, and CLEO installs this
55
+ * hook per project only, so the installers skip it.
56
+ *
57
+ * @param projectDir - the project root an installer was given.
58
+ * @param home - the home directory (injectable for tests).
59
+ */
60
+ export declare function isUserHomeDir(projectDir: string, home?: string): boolean;
61
+ /**
62
+ * The context line an older CLEO gets: its `cleo` has no `cleo hook` (and so
63
+ * no `cleo run`), so the command runs unchanged and ungoverned. A
64
+ * Claude-style `PreToolUse` answer; no single quotes (it is inlined into a
65
+ * single-quoted `sh` word).
66
+ */
67
+ export declare const OLDER_CLEO_HOOK_ANSWER: string;
68
+ /** File-name prefix of the "this cleo has no `cleo hook`" markers in `$TMPDIR`. */
69
+ export declare const OLDER_CLEO_MARKER_PREFIX = "cleo-hook-unsupported-";
70
+ /**
71
+ * Delete this user's older-CLEO markers, so a CLEO that has just installed the
72
+ * hook is asked again at once instead of after the marker lapses.
73
+ *
74
+ * @param dir - the directory the hook command keeps markers in
75
+ * (`$XDG_RUNTIME_DIR`, else `$TMPDIR`).
76
+ * @returns how many markers were removed.
77
+ */
78
+ export declare function clearOlderCleoMarkers(dir?: string): number;
79
+ /**
80
+ * The hook command line for a JSON hook config, ending in the `# cleo-hook`
81
+ * marker. Claude Code runs hook commands through `sh -c`, so its line is the
82
+ * guard script itself. Codex runs them through the user's login shell
83
+ * (`$SHELL -lc`, which may be fish), so its line hands the script to
84
+ * `/bin/sh -c` as one quoted word.
85
+ *
86
+ * @param provider - the harness protocol to answer in.
87
+ * @returns the command line for the provider's config.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * heavyCommandHookCommand('claude-code');
92
+ * // command -v cleo >/dev/null 2>&1 || exit 0; in="$(cat)"; set -f; …; exit 0 # cleo-hook
93
+ * heavyCommandHookCommand('codex');
94
+ * // /bin/sh -c 'command -v cleo >/dev/null 2>&1 || exit 0; …' # cleo-hook
95
+ * ```
96
+ */
97
+ export declare function heavyCommandHookCommand(provider: 'claude-code' | 'codex'): string;
98
+ /**
99
+ * CLEO's hook object (one element of a matcher group's `hooks` array).
100
+ *
101
+ * @param provider - `claude-code` or `codex`.
102
+ */
103
+ export declare function heavyCommandHookObject(provider: 'claude-code' | 'codex'): Record<string, unknown>;
104
+ /**
105
+ * The `hooks.PreToolUse` matcher group CLEO adds when none of the user's
106
+ * groups holds its hook yet (Claude Code and Codex both match `Bash`).
107
+ *
108
+ * @param provider - `claude-code` or `codex`.
109
+ */
110
+ export declare function heavyCommandHookEntry(provider: 'claude-code' | 'codex'): Record<string, unknown>;
111
+ /**
112
+ * Add, refresh or (mode `off`) remove CLEO's heavy-command hook in a JSON hook
113
+ * config, through CAAMP's locked atomic writer. Only CLEO's own hook object is
114
+ * touched; a malformed file throws and is left as it is.
115
+ *
116
+ * @param configPath - the config file (created when missing, unless `off`).
117
+ * @param provider - `claude-code` or `codex`.
118
+ * @param mode - the resolved hook mode.
119
+ * @returns what changed.
120
+ */
121
+ export declare function syncJsonHeavyCommandHook(configPath: string, provider: 'claude-code' | 'codex', mode: HeavyCommandHookMode): Promise<HeavyHookSyncResult>;
122
+ /** First line of the `info/exclude` block CLEO adds (and alone may remove). */
123
+ export declare const LOCAL_SETTINGS_EXCLUDE_MARKER = "# cleo-hook: keep the per-machine heavy-command hook settings out of git (T12983)";
124
+ /**
125
+ * Keep `.claude/settings.local.json` out of git when nothing ignores it yet
126
+ * (Claude Code ignores the file only when it creates it itself). Adds a marked
127
+ * block naming this project's file to the repository's `info/exclude`, never
128
+ * to a tracked `.gitignore`. Keyed on the exact path line, so a second CLEO
129
+ * project in a subdirectory of the same repository gets its own line.
130
+ * Idempotent; does nothing outside a git work tree.
131
+ *
132
+ * @param projectDir - the project root.
133
+ * @returns whether a block was added.
134
+ */
135
+ export declare function excludeLocalSettingsFromGit(projectDir: string): boolean;
136
+ /**
137
+ * Remove the block {@link excludeLocalSettingsFromGit} added for this
138
+ * project, and only that: the exact path line and the marker right above it.
139
+ *
140
+ * @param projectDir - the project root.
141
+ * @returns whether a block was removed.
142
+ */
143
+ export declare function unexcludeLocalSettingsFromGit(projectDir: string): boolean;
144
+ /**
145
+ * Claude Code: sync the hook in `<project>/.claude/settings.local.json`, keep
146
+ * that file out of git, and remove a hook an earlier build put in the shared
147
+ * `<project>/.claude/settings.json`.
148
+ *
149
+ * @param projectDir - the project root.
150
+ * @param mode - the resolved hook mode.
151
+ * @returns what changed in `settings.local.json`.
152
+ */
153
+ export declare function syncClaudeCodeHeavyCommandHook(projectDir: string, mode: HeavyCommandHookMode): Promise<HeavyHookSyncResult>;
154
+ /**
155
+ * Source of the opencode plugin. opencode runs plugins in-process:
156
+ * `tool.execute.before` may replace `output.args`, and
157
+ * `tool.execute.after` may extend the tool's output. The plugin skips calls
158
+ * with no {@link HEAVY_TOOL_WORDS} word, asks
159
+ * `cleo hook heavy-command --provider opencode` for the rest, applies a
160
+ * rewrite and its longer timeout, and appends the hook's context line to the
161
+ * tool's output (opencode has no other channel back to the agent).
162
+ */
163
+ export declare function opencodeHeavyCommandPluginSource(): string;
164
+ /**
165
+ * Write (or, mode `off`, delete) the opencode plugin in
166
+ * `<projectDir>/.opencode/plugins/`. Rewritten only when its content changed.
167
+ *
168
+ * @param projectDir - the project root.
169
+ * @param mode - the resolved hook mode.
170
+ * @returns what changed.
171
+ */
172
+ export declare function syncOpencodeHeavyCommandPlugin(projectDir: string, mode: HeavyCommandHookMode): HeavyHookSyncResult;
173
+ //# sourceMappingURL=heavy-command-hook-install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heavy-command-hook-install.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/heavy-command-hook-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAeH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,oEAAoE;AACpE,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAE/D,iFAAiF;AACjF,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,kDAAkD;AAClD,eAAO,MAAM,6BAA6B,0BAA0B,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EA+B5C,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAUhG;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,OAAO,CAEnF;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAOjC,CAAC;AAEH,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,2BAA2B,CAAC;AAEjE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,GAAE,MAAgD,GACpD,MAAM,CAkBR;AAqDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,GAAG,MAAM,CAIjF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMjG;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEhG;AA4DD;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,aAAa,GAAG,OAAO,EACjC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CA4B9B;AAKD,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,sFAC2C,CAAC;AA6BtF;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAWvE;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CASzE;AAED;;;;;;;;GAQG;AACH,wBAAsB,8BAA8B,CAClD,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAc9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,IAAI,MAAM,CAmDzD;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,oBAAoB,GACzB,mBAAmB,CAarB"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Helpers for JSON hook configs shaped like Claude Code's `settings.json`
3
+ * `hooks` map: `{ "<Event>": [{ "matcher": "...", "hooks": [{ "type":
4
+ * "command", "command": "..." }] }] }`. Codex's `hooks.json` uses the same
5
+ * shape. Every command CLEO owns carries the `# cleo-hook` marker so CLEO can
6
+ * find (and remove) its own entries without touching user-authored ones.
7
+ *
8
+ * Moved from `claude-code/paths.ts` (T12385) so the Codex installer can share
9
+ * them (T12983); `claude-code/paths.ts` re-exports them.
10
+ *
11
+ * @task T12385
12
+ * @task T12983
13
+ */
14
+ /** Marker CLEO puts in every hook command it owns. */
15
+ export declare const CLEO_HOOK_MARKER = "# cleo-hook";
16
+ /**
17
+ * Whether a parsed settings value is a plain JSON object.
18
+ *
19
+ * @param value - Any parsed JSON value
20
+ * @returns `true` for a non-null, non-array object
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * isPlainObject(settings.enabledPlugins);
25
+ * ```
26
+ */
27
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
28
+ /**
29
+ * Whether a settings.json hook entry carries a CLEO-owned command.
30
+ *
31
+ * @param entry - One element of a `hooks.<Event>` array
32
+ * @param commandIncludes - Extra substring the CLEO command must contain
33
+ * @returns `true` when one of the entry's commands has the `# cleo-hook` marker
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * isCleoHookEntry({ hooks: [{ type: "command", command: "x # cleo-hook" }] }); // true
38
+ * ```
39
+ */
40
+ export declare function isCleoHookEntry(entry: unknown, commandIncludes?: string): boolean;
41
+ /**
42
+ * Whether a `hooks.<Event>` value holds a CLEO-owned entry.
43
+ *
44
+ * @param entries - The event's value (non-arrays hold none)
45
+ * @param commandIncludes - Extra substring the CLEO command must contain
46
+ * @returns `true` when a CLEO entry is present
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * hasCleoHook(hookMap(settings).Stop);
51
+ * ```
52
+ */
53
+ export declare function hasCleoHook(entries: unknown, commandIncludes?: string): boolean;
54
+ /**
55
+ * Return the settings object's `hooks` map, creating an empty one if absent.
56
+ *
57
+ * @param settings - Parsed settings.json
58
+ * @returns The live `hooks` object (mutations apply to `settings`)
59
+ * @throws Error when `hooks` exists but is not an object — the caller's write
60
+ * is aborted and the file left untouched
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const hooks = hookMap(settings);
65
+ * ```
66
+ */
67
+ export declare function hookMap(settings: Record<string, unknown>): Record<string, unknown>;
68
+ /**
69
+ * Append a hook entry to `hooks[event]`, preserving existing entries.
70
+ *
71
+ * @param hooks - The settings `hooks` map
72
+ * @param event - Native event name (e.g. `Stop`, `PreCompact`)
73
+ * @param entry - Entry to append
74
+ * @throws Error when `hooks[event]` exists but is not an array
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * appendHookEntry(hookMap(settings), "Stop", { matcher: "", hooks: [] });
79
+ * ```
80
+ */
81
+ export declare function appendHookEntry(hooks: Record<string, unknown>, event: string, entry: Record<string, unknown>): void;
82
+ /**
83
+ * Remove every CLEO-owned entry from every event array in `hooks`.
84
+ *
85
+ * @param hooks - The settings `hooks` map
86
+ * @returns Whether anything was removed
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const changed = removeCleoHookEntries(hookMap(settings));
91
+ * ```
92
+ */
93
+ export declare function removeCleoHookEntries(hooks: Record<string, unknown>): boolean;
94
+ //# sourceMappingURL=hook-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-config.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/hook-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CASjF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWlF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAMN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAW7E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/adapters",
3
- "version": "2026.10.1",
3
+ "version": "2026.10.3",
4
4
  "description": "Unified provider adapters for CLEO (Claude Code, OpenCode, Cursor)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,9 +15,9 @@
15
15
  "@ai-sdk/anthropic": "^3.0.69",
16
16
  "@ai-sdk/openai": "^2.0.53",
17
17
  "ai": "^6.0.168",
18
- "@cleocode/caamp": "2026.10.1",
19
- "@cleocode/contracts": "2026.10.1",
20
- "@cleocode/paths": "2026.10.1"
18
+ "@cleocode/caamp": "2026.10.3",
19
+ "@cleocode/contracts": "2026.10.3",
20
+ "@cleocode/paths": "2026.10.3"
21
21
  },
22
22
  "license": "MIT",
23
23
  "engines": {
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@types/node": "^24.3.0",
32
32
  "vitest": "^4.1.4",
33
- "@cleocode/playbooks": "2026.10.1"
33
+ "@cleocode/playbooks": "2026.10.3"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",
@@ -4,18 +4,25 @@
4
4
  * Handles CLEO installation into Claude Code environments:
5
5
  * - Ensures CLAUDE.md has CLEO @-references
6
6
  * - Manages plugin registration in ~/.claude/settings.json
7
+ * - Installs the heavy-command `PreToolUse` hook in the PROJECT's
8
+ * `.claude/settings.local.json` (T12983)
7
9
  *
8
10
  * Migrated from src/core/install/claude-plugin.ts
9
11
  *
10
12
  * @task T5240
13
+ * @task T12983
11
14
  */
12
15
 
13
16
  import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from 'node:fs';
14
17
  import { homedir } from 'node:os';
15
- import { dirname, join } from 'node:path';
18
+ import { dirname, join, resolve } from 'node:path';
16
19
  import { fileURLToPath } from 'node:url';
17
20
  import { ensureProviderInstructionFile, updateJsonConfigFile } from '@cleocode/caamp';
18
21
  import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
22
+ import {
23
+ isUserHomeDir,
24
+ syncClaudeCodeHeavyCommandHook,
25
+ } from '../shared/heavy-command-hook-install.js';
19
26
  import {
20
27
  type InstallHookTemplatesResult,
21
28
  installProviderHookTemplates,
@@ -47,6 +54,9 @@ function getAdapterCommandsDir(): string {
47
54
  * 1. Ensuring CLAUDE.md contains @-references to CLEO instruction files
48
55
  * 2. Installing adapter-provided commands to .claude/commands/
49
56
  * 3. Registering the brain observation plugin in ~/.claude/settings.json
57
+ * 4. Installing PreCompact hook templates
58
+ * 5. Installing the heavy-command `PreToolUse` hook in the project's
59
+ * `.claude/settings.local.json`, when `options.heavyCommandHook` is set
50
60
  *
51
61
  * @remarks
52
62
  * Installation is idempotent -- running install multiple times on the same
@@ -105,6 +115,24 @@ export class ClaudeCodeInstallProvider implements AdapterInstallProvider {
105
115
  settingsErrors.push(err instanceof Error ? err.message : String(err));
106
116
  }
107
117
 
118
+ // Step 5 (T12983): route heavy shell commands through `cleo run`.
119
+ // Project-level settings only; the user's ~/.claude is never touched.
120
+ if (options.heavyCommandHook !== undefined) {
121
+ const projectSettings = join(projectDir, '.claude', 'settings.json');
122
+ if (isUserHomeDir(projectDir) || resolve(projectSettings) === resolve(claudeSettingsPath())) {
123
+ details.heavyCommandHook = 'skipped';
124
+ } else {
125
+ try {
126
+ details.heavyCommandHook = await syncClaudeCodeHeavyCommandHook(
127
+ projectDir,
128
+ options.heavyCommandHook,
129
+ );
130
+ } catch (err) {
131
+ settingsErrors.push(err instanceof Error ? err.message : String(err));
132
+ }
133
+ }
134
+ }
135
+
108
136
  if (settingsErrors.length > 0) {
109
137
  details.settingsErrors = settingsErrors;
110
138
  }