@cleocode/adapters 2026.10.1 → 2026.10.2
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/dist/index.js +1073 -262
- package/dist/index.js.map +3 -3
- package/dist/providers/claude-code/install.d.ts +6 -0
- package/dist/providers/claude-code/install.d.ts.map +1 -1
- package/dist/providers/claude-code/paths.d.ts +1 -78
- package/dist/providers/claude-code/paths.d.ts.map +1 -1
- package/dist/providers/codex/install.d.ts +8 -2
- package/dist/providers/codex/install.d.ts.map +1 -1
- package/dist/providers/kimi/install.d.ts +15 -0
- package/dist/providers/kimi/install.d.ts.map +1 -1
- package/dist/providers/opencode/install.d.ts +2 -0
- package/dist/providers/opencode/install.d.ts.map +1 -1
- package/dist/providers/shared/heavy-command-hook-install.d.ts +173 -0
- package/dist/providers/shared/heavy-command-hook-install.d.ts.map +1 -0
- package/dist/providers/shared/hook-config.d.ts +94 -0
- package/dist/providers/shared/hook-config.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/providers/claude-code/install.ts +29 -1
- package/src/providers/claude-code/paths.ts +9 -131
- package/src/providers/codex/install.ts +27 -3
- package/src/providers/kimi/install.ts +15 -0
- package/src/providers/opencode/install.ts +24 -1
- package/src/providers/shared/__tests__/heavy-command-hook-install.test.ts +621 -0
- package/src/providers/shared/heavy-command-hook-install.ts +562 -0
- package/src/providers/shared/hook-config.ts +145 -0
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
|
|
15
|
+
/** Marker CLEO puts in every hook command it owns. */
|
|
16
|
+
export const CLEO_HOOK_MARKER = '# cleo-hook';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether a parsed settings value is a plain JSON object.
|
|
20
|
+
*
|
|
21
|
+
* @param value - Any parsed JSON value
|
|
22
|
+
* @returns `true` for a non-null, non-array object
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* isPlainObject(settings.enabledPlugins);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
30
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether a settings.json hook entry carries a CLEO-owned command.
|
|
35
|
+
*
|
|
36
|
+
* @param entry - One element of a `hooks.<Event>` array
|
|
37
|
+
* @param commandIncludes - Extra substring the CLEO command must contain
|
|
38
|
+
* @returns `true` when one of the entry's commands has the `# cleo-hook` marker
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* isCleoHookEntry({ hooks: [{ type: "command", command: "x # cleo-hook" }] }); // true
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function isCleoHookEntry(entry: unknown, commandIncludes?: string): boolean {
|
|
46
|
+
if (!isPlainObject(entry) || !Array.isArray(entry.hooks)) return false;
|
|
47
|
+
return entry.hooks.some(
|
|
48
|
+
(hook) =>
|
|
49
|
+
isPlainObject(hook) &&
|
|
50
|
+
typeof hook.command === 'string' &&
|
|
51
|
+
hook.command.includes(CLEO_HOOK_MARKER) &&
|
|
52
|
+
(commandIncludes === undefined || hook.command.includes(commandIncludes)),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Whether a `hooks.<Event>` value holds a CLEO-owned entry.
|
|
58
|
+
*
|
|
59
|
+
* @param entries - The event's value (non-arrays hold none)
|
|
60
|
+
* @param commandIncludes - Extra substring the CLEO command must contain
|
|
61
|
+
* @returns `true` when a CLEO entry is present
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* hasCleoHook(hookMap(settings).Stop);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function hasCleoHook(entries: unknown, commandIncludes?: string): boolean {
|
|
69
|
+
return Array.isArray(entries) && entries.some((e) => isCleoHookEntry(e, commandIncludes));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Return the settings object's `hooks` map, creating an empty one if absent.
|
|
74
|
+
*
|
|
75
|
+
* @param settings - Parsed settings.json
|
|
76
|
+
* @returns The live `hooks` object (mutations apply to `settings`)
|
|
77
|
+
* @throws Error when `hooks` exists but is not an object — the caller's write
|
|
78
|
+
* is aborted and the file left untouched
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const hooks = hookMap(settings);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function hookMap(settings: Record<string, unknown>): Record<string, unknown> {
|
|
86
|
+
const existing = settings.hooks;
|
|
87
|
+
if (existing === undefined) {
|
|
88
|
+
const created: Record<string, unknown> = {};
|
|
89
|
+
settings.hooks = created;
|
|
90
|
+
return created;
|
|
91
|
+
}
|
|
92
|
+
if (!isPlainObject(existing)) {
|
|
93
|
+
throw new Error('settings.json "hooks" is not an object; not modifying it');
|
|
94
|
+
}
|
|
95
|
+
return existing;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Append a hook entry to `hooks[event]`, preserving existing entries.
|
|
100
|
+
*
|
|
101
|
+
* @param hooks - The settings `hooks` map
|
|
102
|
+
* @param event - Native event name (e.g. `Stop`, `PreCompact`)
|
|
103
|
+
* @param entry - Entry to append
|
|
104
|
+
* @throws Error when `hooks[event]` exists but is not an array
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* appendHookEntry(hookMap(settings), "Stop", { matcher: "", hooks: [] });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function appendHookEntry(
|
|
112
|
+
hooks: Record<string, unknown>,
|
|
113
|
+
event: string,
|
|
114
|
+
entry: Record<string, unknown>,
|
|
115
|
+
): void {
|
|
116
|
+
const existing = hooks[event];
|
|
117
|
+
if (existing !== undefined && !Array.isArray(existing)) {
|
|
118
|
+
throw new Error(`settings.json "hooks.${event}" is not an array; not modifying it`);
|
|
119
|
+
}
|
|
120
|
+
hooks[event] = [...(existing ?? []), entry];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Remove every CLEO-owned entry from every event array in `hooks`.
|
|
125
|
+
*
|
|
126
|
+
* @param hooks - The settings `hooks` map
|
|
127
|
+
* @returns Whether anything was removed
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const changed = removeCleoHookEntries(hookMap(settings));
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function removeCleoHookEntries(hooks: Record<string, unknown>): boolean {
|
|
135
|
+
let changed = false;
|
|
136
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
137
|
+
if (!Array.isArray(entries)) continue;
|
|
138
|
+
const kept = entries.filter((e) => !isCleoHookEntry(e));
|
|
139
|
+
if (kept.length !== entries.length) {
|
|
140
|
+
hooks[event] = kept;
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return changed;
|
|
145
|
+
}
|