@cleocode/adapters 2026.4.101 → 2026.4.102
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.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1221 -16720
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code/install.d.ts +36 -0
- package/dist/providers/claude-code/install.d.ts.map +1 -1
- package/dist/providers/cursor/install.d.ts +36 -0
- package/dist/providers/cursor/install.d.ts.map +1 -1
- package/dist/providers/opencode/install.d.ts +47 -2
- package/dist/providers/opencode/install.d.ts.map +1 -1
- package/dist/providers/shared/hook-template-installer.d.ts +109 -0
- package/dist/providers/shared/hook-template-installer.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +11 -0
- package/src/providers/README.md +137 -0
- package/src/providers/claude-code/__tests__/hooks-install.test.ts +113 -0
- package/src/providers/claude-code/install.ts +129 -0
- package/src/providers/claude-code/templates/hooks/precompact-safestop.sh +52 -0
- package/src/providers/cursor/__tests__/hooks-install.test.ts +88 -0
- package/src/providers/cursor/install.ts +117 -0
- package/src/providers/cursor/templates/hooks/precompact.sh +47 -0
- package/src/providers/gemini-cli/templates/hooks/precompact.sh +47 -0
- package/src/providers/opencode/__tests__/hooks-install.test.ts +87 -0
- package/src/providers/opencode/install.ts +134 -3
- package/src/providers/opencode/templates/hooks/precompact.sh +42 -0
- package/src/providers/pi/templates/hooks/README.md +40 -0
- package/src/providers/shared/hook-template-installer.ts +268 -0
- package/src/providers/shared/templates/hooks/cleo-precompact-core.sh +128 -0
|
@@ -74,5 +74,41 @@ export declare class ClaudeCodeInstallProvider implements AdapterInstallProvider
|
|
|
74
74
|
* @returns Description of what was registered, or null if no change needed
|
|
75
75
|
*/
|
|
76
76
|
private registerPlugin;
|
|
77
|
+
/**
|
|
78
|
+
* Install the CLEO PreCompact hook templates for Claude Code (T1013).
|
|
79
|
+
*
|
|
80
|
+
* Writes two files to `~/.claude/hooks/`:
|
|
81
|
+
* 1. `cleo-precompact-core.sh` — universal CLEO safestop helper (shared
|
|
82
|
+
* across all providers; sourced by the provider-specific shim).
|
|
83
|
+
* 2. `precompact-safestop.sh` — Claude-Code-flavoured wrapper that invokes
|
|
84
|
+
* `cleo memory precompact-flush` and `cleo safestop`.
|
|
85
|
+
*
|
|
86
|
+
* Also registers a `PreCompact` entry in `~/.claude/settings.json` so Claude
|
|
87
|
+
* Code runs the hook when auto-compact fires (at 95% context).
|
|
88
|
+
*
|
|
89
|
+
* Idempotent: subsequent installs skip unchanged files and do not duplicate
|
|
90
|
+
* the settings.json hook entry.
|
|
91
|
+
*
|
|
92
|
+
* @returns Install summary (paths written + config change description), or
|
|
93
|
+
* `null` when no change was required.
|
|
94
|
+
*
|
|
95
|
+
* @task T1013
|
|
96
|
+
*/
|
|
97
|
+
private installHookTemplates;
|
|
98
|
+
/**
|
|
99
|
+
* Register the PreCompact hook command in `~/.claude/settings.json`.
|
|
100
|
+
*
|
|
101
|
+
* The Claude Code native event name for the canonical `PreCompact` event is
|
|
102
|
+
* `PreCompact` (identity mapping — see `hook-mappings.json`). The entry is
|
|
103
|
+
* tagged with `# cleo-hook` so the uninstall flow can identify and remove
|
|
104
|
+
* our additions without touching user-authored hooks.
|
|
105
|
+
*
|
|
106
|
+
* @param shimPath - Absolute path to the installed `precompact-safestop.sh`.
|
|
107
|
+
* @returns `true` when a new hook entry was written, `false` when an
|
|
108
|
+
* equivalent entry was already present.
|
|
109
|
+
*
|
|
110
|
+
* @task T1013
|
|
111
|
+
*/
|
|
112
|
+
private registerPreCompactHook;
|
|
77
113
|
}
|
|
78
114
|
//# sourceMappingURL=install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-code/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-code/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAwBjG;;;;;;;;;;;;;GAaG;AACH,qBAAa,yBAA0B,YAAW,sBAAsB;IACtE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAuC9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBrC;;;;;;OAMG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IA+B7B;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAsBvB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAkCtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,oBAAoB;IAgC5B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;CAmD/B"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Handles CLEO installation into Cursor environments:
|
|
5
5
|
* - Ensures .cursorrules has CLEO @-references (legacy format)
|
|
6
6
|
* - Creates .cursor/rules/cleo.mdc with CLEO references (modern format)
|
|
7
|
+
* - Installs PreCompact hook shell shims + wires them into .cursor/hooks.json (T1013)
|
|
7
8
|
*
|
|
8
9
|
* Cursor supports two instruction file formats:
|
|
9
10
|
* 1. Legacy: .cursorrules (flat file, project root)
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
* This provider writes to both for maximum compatibility.
|
|
13
14
|
*
|
|
14
15
|
* @task T5240
|
|
16
|
+
* @task T1013
|
|
15
17
|
*/
|
|
16
18
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
17
19
|
/**
|
|
@@ -20,6 +22,7 @@ import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cle
|
|
|
20
22
|
* Manages CLEO's integration with Cursor by:
|
|
21
23
|
* 1. Creating/updating .cursorrules with @-references (legacy)
|
|
22
24
|
* 2. Creating .cursor/rules/cleo.mdc with @-references (modern)
|
|
25
|
+
* 3. Installing the PreCompact hook shim + registering it in .cursor/hooks.json (T1013)
|
|
23
26
|
*
|
|
24
27
|
* @remarks
|
|
25
28
|
* Installation is idempotent and writes to both instruction file formats
|
|
@@ -83,5 +86,38 @@ export declare class CursorInstallProvider implements AdapterInstallProvider {
|
|
|
83
86
|
* Get list of instruction files that were updated.
|
|
84
87
|
*/
|
|
85
88
|
private getUpdatedFileList;
|
|
89
|
+
/**
|
|
90
|
+
* Install the CLEO PreCompact hook templates for Cursor (T1013).
|
|
91
|
+
*
|
|
92
|
+
* Writes two files to `<projectDir>/.cursor/hooks/`:
|
|
93
|
+
* 1. `cleo-precompact-core.sh` — universal CLEO safestop helper.
|
|
94
|
+
* 2. `precompact.sh` — Cursor-flavoured wrapper.
|
|
95
|
+
*
|
|
96
|
+
* Also registers a `preCompact` entry in `.cursor/hooks.json`. The native
|
|
97
|
+
* event name `preCompact` comes from CAAMP's `hook-mappings.json` SSoT.
|
|
98
|
+
*
|
|
99
|
+
* Idempotent: re-running install skips unchanged files and avoids
|
|
100
|
+
* duplicating the hooks.json entry.
|
|
101
|
+
*
|
|
102
|
+
* @param projectDir - Project root directory.
|
|
103
|
+
* @returns Install summary, or `null` when no change was required.
|
|
104
|
+
*
|
|
105
|
+
* @task T1013
|
|
106
|
+
*/
|
|
107
|
+
private installHookTemplates;
|
|
108
|
+
/**
|
|
109
|
+
* Register the PreCompact hook command in `.cursor/hooks.json`.
|
|
110
|
+
*
|
|
111
|
+
* Cursor's native event name for the canonical `PreCompact` is `preCompact`
|
|
112
|
+
* (camelCase — see CAAMP `hook-mappings.json`). Entries are tagged with a
|
|
113
|
+
* `# cleo-hook` comment so they can be cleanly removed on uninstall.
|
|
114
|
+
*
|
|
115
|
+
* @param projectDir - Project root directory.
|
|
116
|
+
* @param shimPath - Absolute path to the installed `precompact.sh`.
|
|
117
|
+
* @returns `true` when a new entry was written, `false` when already wired.
|
|
118
|
+
*
|
|
119
|
+
* @task T1013
|
|
120
|
+
*/
|
|
121
|
+
private registerPreCompactHook;
|
|
86
122
|
}
|
|
87
123
|
//# sourceMappingURL=install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/cursor/install.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/cursor/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAiBjG;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,sBAAsB;IAClE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA2B9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAqBrC;;;;;;OAMG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAmBzB;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,oBAAoB;IA8B5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,sBAAsB;CAyC/B"}
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles CLEO installation into OpenCode environments:
|
|
5
5
|
* - Ensures AGENTS.md has CLEO @-references
|
|
6
|
+
* - Installs PreCompact hook shell shims + a JS plugin wrapper (T1013)
|
|
6
7
|
*
|
|
7
8
|
* @task T5240
|
|
9
|
+
* @task T1013
|
|
8
10
|
*/
|
|
9
11
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
10
12
|
/**
|
|
@@ -12,11 +14,17 @@ import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cle
|
|
|
12
14
|
*
|
|
13
15
|
* Manages CLEO's integration with OpenCode by:
|
|
14
16
|
* 1. Ensuring AGENTS.md contains @-references to CLEO instruction files
|
|
17
|
+
* 2. Installing PreCompact hook shell templates + generating the JS plugin
|
|
18
|
+
* wrapper that spawns the shim on `experimental.session.compacting` (T1013).
|
|
15
19
|
*
|
|
16
20
|
* @remarks
|
|
17
21
|
* Installation is idempotent -- running install multiple times on the same
|
|
18
|
-
* project produces the same result.
|
|
19
|
-
*
|
|
22
|
+
* project produces the same result. OpenCode's plugin system is the native
|
|
23
|
+
* hook surface (OpenCode has no config-file hook registry like Claude Code or
|
|
24
|
+
* Cursor), so the installer writes a JS plugin that subscribes to the native
|
|
25
|
+
* event and spawns the shell shim as a child process. This keeps the DRY
|
|
26
|
+
* contract: all providers funnel through the shared `cleo-precompact-core.sh`
|
|
27
|
+
* helper and end up in the `cleo` CLI.
|
|
20
28
|
*/
|
|
21
29
|
export declare class OpenCodeInstallProvider implements AdapterInstallProvider {
|
|
22
30
|
/**
|
|
@@ -52,5 +60,42 @@ export declare class OpenCodeInstallProvider implements AdapterInstallProvider {
|
|
|
52
60
|
* @returns true if the file was created or modified
|
|
53
61
|
*/
|
|
54
62
|
private updateInstructionFile;
|
|
63
|
+
/**
|
|
64
|
+
* Install the CLEO PreCompact hook templates for OpenCode (T1013).
|
|
65
|
+
*
|
|
66
|
+
* OpenCode uses a JavaScript plugin system, not config-based hooks. The
|
|
67
|
+
* installer:
|
|
68
|
+
*
|
|
69
|
+
* 1. Writes the shared bash helper and OpenCode-flavoured `precompact.sh`
|
|
70
|
+
* to `<projectDir>/.opencode/plugins/hooks/` so the shim can be spawned
|
|
71
|
+
* as a child process.
|
|
72
|
+
* 2. Generates an OpenCode plugin `.opencode/plugins/cleo-precompact.js`
|
|
73
|
+
* that subscribes to `experimental.session.compacting` (CAAMP native
|
|
74
|
+
* event for the canonical `PreCompact`) and spawns the shim.
|
|
75
|
+
*
|
|
76
|
+
* Idempotent.
|
|
77
|
+
*
|
|
78
|
+
* @param projectDir - Project root directory.
|
|
79
|
+
* @returns Install summary, or `null` when no change was required.
|
|
80
|
+
*
|
|
81
|
+
* @task T1013
|
|
82
|
+
*/
|
|
83
|
+
private installHookTemplates;
|
|
84
|
+
/**
|
|
85
|
+
* Write an OpenCode JavaScript plugin that spawns `precompact.sh` when the
|
|
86
|
+
* canonical `PreCompact` event fires. OpenCode exposes the event natively as
|
|
87
|
+
* `experimental.session.compacting` (see CAAMP `hook-mappings.json`).
|
|
88
|
+
*
|
|
89
|
+
* The generated file is idempotent — overwritten only when its content
|
|
90
|
+
* differs from the target on disk. Uses `child_process.spawn` so the bash
|
|
91
|
+
* shim runs in a separate process and does not block the compaction path.
|
|
92
|
+
*
|
|
93
|
+
* @param pluginsDir - Absolute path to `.opencode/plugins/`.
|
|
94
|
+
* @param shimPath - Absolute path to the installed `precompact.sh`.
|
|
95
|
+
* @returns `true` when the plugin file was written, `false` when unchanged.
|
|
96
|
+
*
|
|
97
|
+
* @task T1013
|
|
98
|
+
*/
|
|
99
|
+
private writePrecompactPlugin;
|
|
55
100
|
}
|
|
56
101
|
//# sourceMappingURL=install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/opencode/install.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/providers/opencode/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAiBjG;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,YAAW,sBAAsB;IACpE;;;;;OAKG;IACG,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA4B9D;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEhC;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBrC;;;;;;OAMG;IACG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IA+B7B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,oBAAoB;IAiC5B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,qBAAqB;CA0C9B"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hook-template installer for provider adapters.
|
|
3
|
+
*
|
|
4
|
+
* Each provider ships its own PreCompact shell shim under
|
|
5
|
+
* `packages/adapters/src/providers/<provider>/templates/hooks/` which sources
|
|
6
|
+
* the universal helper at
|
|
7
|
+
* `packages/adapters/src/providers/shared/templates/hooks/cleo-precompact-core.sh`.
|
|
8
|
+
*
|
|
9
|
+
* This module wires the templates into the provider's hooks directory at
|
|
10
|
+
* install time. Provider-specific {@link AdapterInstallProvider} implementations
|
|
11
|
+
* call {@link installProviderHookTemplates} with their own provider id, and the
|
|
12
|
+
* installer consults CAAMP's `hook-mappings.json` SSoT to verify the provider
|
|
13
|
+
* supports the required canonical event and handler type before writing.
|
|
14
|
+
*
|
|
15
|
+
* DRY invariant: all shims source the same core helper — adapter-specific
|
|
16
|
+
* shims only add provider-flavoured banners and `$CLEO_PRECOMPACT_*` env
|
|
17
|
+
* handling.
|
|
18
|
+
*
|
|
19
|
+
* @task T1013
|
|
20
|
+
* @epic T1000
|
|
21
|
+
*/
|
|
22
|
+
/** Identifiers for providers that ship bash hook templates. */
|
|
23
|
+
export type HookTemplateProviderId = 'claude-code' | 'cursor' | 'opencode' | 'gemini-cli';
|
|
24
|
+
/**
|
|
25
|
+
* Result returned by {@link installProviderHookTemplates}.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Paths are absolute and point at the filesystem locations the installer
|
|
29
|
+
* actually wrote to. When no template files needed copying (e.g. because the
|
|
30
|
+
* destination already contained identical files), `installedFiles` is empty
|
|
31
|
+
* and `skipped` carries the reason-keyed paths instead.
|
|
32
|
+
*/
|
|
33
|
+
export interface InstallHookTemplatesResult {
|
|
34
|
+
/** Provider identifier the templates were installed for. */
|
|
35
|
+
provider: HookTemplateProviderId;
|
|
36
|
+
/** Absolute path to the hooks directory that received the templates. */
|
|
37
|
+
targetDir: string;
|
|
38
|
+
/** Absolute paths to files written during this install invocation. */
|
|
39
|
+
installedFiles: string[];
|
|
40
|
+
/** Files that were not written (already present and identical). */
|
|
41
|
+
skipped: string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for {@link installProviderHookTemplates}.
|
|
45
|
+
*/
|
|
46
|
+
export interface InstallHookTemplatesOptions {
|
|
47
|
+
/** Provider to install hook templates for. */
|
|
48
|
+
provider: HookTemplateProviderId;
|
|
49
|
+
/** Absolute path to the hooks directory that should receive the shims. */
|
|
50
|
+
targetDir: string;
|
|
51
|
+
/**
|
|
52
|
+
* When `true`, overwrite existing files even if their contents match.
|
|
53
|
+
*
|
|
54
|
+
* @defaultValue `false`
|
|
55
|
+
*/
|
|
56
|
+
force?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Install the CLEO PreCompact hook templates for a provider.
|
|
60
|
+
*
|
|
61
|
+
* Writes two files into `targetDir`:
|
|
62
|
+
*
|
|
63
|
+
* 1. `cleo-precompact-core.sh` — universal helper (shared across all providers)
|
|
64
|
+
* 2. `<provider-shim>.sh` — provider-flavoured shim that sources the helper
|
|
65
|
+
*
|
|
66
|
+
* The shim invokes only the universal CLEO CLI (`cleo memory precompact-flush`
|
|
67
|
+
* and `cleo safestop …`) — adapters never reach into core internals.
|
|
68
|
+
*
|
|
69
|
+
* @param options - Installation target and provider id.
|
|
70
|
+
* @returns Paths written, paths skipped, and the resolved target directory.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { homedir } from 'node:os';
|
|
75
|
+
* import { join } from 'node:path';
|
|
76
|
+
* import { installProviderHookTemplates } from '@cleocode/adapters';
|
|
77
|
+
*
|
|
78
|
+
* const result = installProviderHookTemplates({
|
|
79
|
+
* provider: 'claude-code',
|
|
80
|
+
* targetDir: join(homedir(), '.claude', 'hooks'),
|
|
81
|
+
* });
|
|
82
|
+
* // result.installedFiles includes both scripts on first run.
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @task T1013
|
|
86
|
+
* @public
|
|
87
|
+
*/
|
|
88
|
+
export declare function installProviderHookTemplates(options: InstallHookTemplatesOptions): InstallHookTemplatesResult;
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the source-side path of a provider's hook template for inspection
|
|
91
|
+
* and testing. Returns the absolute path where the installer will read from.
|
|
92
|
+
*
|
|
93
|
+
* @param provider - Provider identifier.
|
|
94
|
+
* @returns Absolute path to the provider's shim template file.
|
|
95
|
+
*
|
|
96
|
+
* @task T1013
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
export declare function getProviderHookTemplatePath(provider: HookTemplateProviderId): string;
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the source-side path of the shared universal helper.
|
|
102
|
+
*
|
|
103
|
+
* @returns Absolute path to `cleo-precompact-core.sh` in the adapter package.
|
|
104
|
+
*
|
|
105
|
+
* @task T1013
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
export declare function getSharedHookCorePath(): string;
|
|
109
|
+
//# sourceMappingURL=hook-template-installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-template-installer.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/hook-template-installer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,+DAA+D;AAC/D,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;AAE1F;;;;;;;;GAQG;AACH,MAAM,WAAW,0BAA0B;IACzC,4DAA4D;IAC5D,QAAQ,EAAE,sBAAsB,CAAC;IACjC,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,8CAA8C;IAC9C,QAAQ,EAAE,sBAAsB,CAAC;IACjC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAqHD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,2BAA2B,GACnC,0BAA0B,CAyB5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,CAEpF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/adapters",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.102",
|
|
4
4
|
"description": "Unified provider adapters for CLEO (Claude Code, OpenCode, Cursor)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,8 +15,8 @@
|
|
|
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.4.
|
|
19
|
-
"@cleocode/contracts": "2026.4.
|
|
18
|
+
"@cleocode/caamp": "2026.4.102",
|
|
19
|
+
"@cleocode/contracts": "2026.4.102"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"engines": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^22.19.15",
|
|
31
31
|
"vitest": "^4.1.4",
|
|
32
|
-
"@cleocode/playbooks": "2026.4.
|
|
32
|
+
"@cleocode/playbooks": "2026.4.102"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
package/src/index.ts
CHANGED
|
@@ -84,5 +84,16 @@ export {
|
|
|
84
84
|
OpenCodeInstallProvider,
|
|
85
85
|
OpenCodeSpawnProvider,
|
|
86
86
|
} from './providers/opencode/index.js';
|
|
87
|
+
export type {
|
|
88
|
+
HookTemplateProviderId,
|
|
89
|
+
InstallHookTemplatesOptions,
|
|
90
|
+
InstallHookTemplatesResult,
|
|
91
|
+
} from './providers/shared/hook-template-installer.js';
|
|
92
|
+
// T1013 — shared PreCompact hook template installer (DRY across providers).
|
|
93
|
+
export {
|
|
94
|
+
getProviderHookTemplatePath,
|
|
95
|
+
getSharedHookCorePath,
|
|
96
|
+
installProviderHookTemplates,
|
|
97
|
+
} from './providers/shared/hook-template-installer.js';
|
|
87
98
|
export type { AdapterManifest } from './registry.js';
|
|
88
99
|
export { discoverProviders, getProviderManifests } from './registry.js';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# CLEO Provider Adapters
|
|
2
|
+
|
|
3
|
+
This directory contains the per-provider adapters that integrate CLEO with
|
|
4
|
+
individual AI coding harnesses (Claude Code, Cursor, OpenCode, Gemini CLI,
|
|
5
|
+
Pi, etc.). Each adapter implements the contracts defined in
|
|
6
|
+
`@cleocode/contracts` and delegates universal behaviour to CLEO core through
|
|
7
|
+
the `cleo` CLI — adapters never reach into `@cleocode/core` internals at
|
|
8
|
+
runtime.
|
|
9
|
+
|
|
10
|
+
## Directory layout
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
providers/
|
|
14
|
+
├── claude-code/
|
|
15
|
+
│ ├── adapter.ts # CLEOProviderAdapter implementation
|
|
16
|
+
│ ├── hooks.ts # AdapterHookProvider (event-name translation)
|
|
17
|
+
│ ├── install.ts # AdapterInstallProvider (filesystem wiring)
|
|
18
|
+
│ ├── spawn.ts # AdapterSpawnProvider
|
|
19
|
+
│ └── templates/
|
|
20
|
+
│ └── hooks/ # Provider-specific shell shims (see below)
|
|
21
|
+
├── cursor/
|
|
22
|
+
│ ├── ...
|
|
23
|
+
│ └── templates/hooks/
|
|
24
|
+
├── opencode/
|
|
25
|
+
│ ├── ...
|
|
26
|
+
│ └── templates/hooks/
|
|
27
|
+
├── gemini-cli/
|
|
28
|
+
│ ├── ...
|
|
29
|
+
│ └── templates/hooks/
|
|
30
|
+
├── pi/
|
|
31
|
+
│ ├── ...
|
|
32
|
+
│ └── templates/hooks/ # README only — Pi uses TS extensions, not shell
|
|
33
|
+
└── shared/
|
|
34
|
+
├── hook-template-installer.ts # DRY installer for all providers
|
|
35
|
+
├── paths.ts
|
|
36
|
+
├── transcript-reader.ts
|
|
37
|
+
└── templates/hooks/
|
|
38
|
+
└── cleo-precompact-core.sh # Shared universal helper (single SSoT)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Hook Template Architecture (T1013)
|
|
42
|
+
|
|
43
|
+
The provider adapters own all harness-specific hook templates. The core
|
|
44
|
+
package (`@cleocode/core`) ships only the universal business logic
|
|
45
|
+
(`src/memory/precompact-flush.ts`, `src/hooks/handlers/*`) and the `cleo`
|
|
46
|
+
CLI — never bash.
|
|
47
|
+
|
|
48
|
+
### Layered design
|
|
49
|
+
|
|
50
|
+
1. **Universal layer** — `shared/templates/hooks/cleo-precompact-core.sh`
|
|
51
|
+
contains the one-and-only implementation of the pre-compact flush +
|
|
52
|
+
safestop sequence. It invokes the CLEO CLI (`cleo memory precompact-flush`
|
|
53
|
+
and `cleo safestop …`). No provider knows any CLEO internals; every
|
|
54
|
+
provider ends up at the same CLI surface.
|
|
55
|
+
|
|
56
|
+
2. **Provider shim layer** — each harness-specific directory ships a tiny
|
|
57
|
+
wrapper that sources the universal helper and adds only provider-flavoured
|
|
58
|
+
banners / env handling. Per-provider filenames match the harness's native
|
|
59
|
+
event vocabulary:
|
|
60
|
+
|
|
61
|
+
| Provider | Shim | Canonical event | Native event |
|
|
62
|
+
|--------------|-----------------------------------------|-----------------|---------------------------|
|
|
63
|
+
| claude-code | `precompact-safestop.sh` | `PreCompact` | `PreCompact` |
|
|
64
|
+
| cursor | `precompact.sh` | `PreCompact` | `preCompact` |
|
|
65
|
+
| opencode | `precompact.sh` + `cleo-precompact.js` | `PreCompact` | `experimental.session.compacting` |
|
|
66
|
+
| gemini-cli | `precompact.sh` | `PreCompact` | `PreCompress` |
|
|
67
|
+
| pi | *README-only* (uses TS extension) | `PreCompact` | `context` |
|
|
68
|
+
|
|
69
|
+
The event mappings are sourced from `packages/caamp/providers/hook-mappings.json`
|
|
70
|
+
(the CAAMP SSoT). Provider adapters import the translation via
|
|
71
|
+
`@cleocode/caamp`'s `toNative()` / `getProviderHookProfile()` APIs rather
|
|
72
|
+
than hardcoding names.
|
|
73
|
+
|
|
74
|
+
3. **Installer layer** — `shared/hook-template-installer.ts` exposes a single
|
|
75
|
+
`installProviderHookTemplates({ provider, targetDir })` function that:
|
|
76
|
+
1. Resolves the provider's shim + the shared helper from this package.
|
|
77
|
+
2. Copies both into the provided target directory (idempotent).
|
|
78
|
+
3. Returns an install summary for reporting.
|
|
79
|
+
|
|
80
|
+
Each provider's `AdapterInstallProvider.install(...)` method calls this
|
|
81
|
+
installer and then wires the shim into the harness's configuration
|
|
82
|
+
surface:
|
|
83
|
+
|
|
84
|
+
- Claude Code: append a `PreCompact` entry to `~/.claude/settings.json`.
|
|
85
|
+
- Cursor: append a `preCompact` entry to `.cursor/hooks.json`.
|
|
86
|
+
- OpenCode: generate a JS plugin at
|
|
87
|
+
`.opencode/plugins/cleo-precompact.js` that `spawn()`s the shim.
|
|
88
|
+
- Gemini CLI: (planned) append a `PreCompress` entry to
|
|
89
|
+
`~/.gemini/settings.json`.
|
|
90
|
+
- Pi: (planned) write a TS extension that calls the core handler.
|
|
91
|
+
|
|
92
|
+
### Why this split?
|
|
93
|
+
|
|
94
|
+
Before T1013 the pre-compact hook lived in
|
|
95
|
+
`packages/core/templates/hooks/precompact-safestop.sh` — Claude-Code-specific
|
|
96
|
+
bash buried inside the provider-neutral core package. Moving the file into
|
|
97
|
+
`packages/adapters/src/providers/claude-code/templates/hooks/` and adding
|
|
98
|
+
equivalent templates under the other provider directories restores the
|
|
99
|
+
architectural boundary:
|
|
100
|
+
|
|
101
|
+
- **Core** owns universal CLEO logic (`precompact-flush.ts`,
|
|
102
|
+
`memory-sqlite.ts`, session handling).
|
|
103
|
+
- **Adapters** own harness-specific wiring (shell shims, config fragments,
|
|
104
|
+
plugin generators).
|
|
105
|
+
- **CAAMP** owns the event-name translation SSoT (`hook-mappings.json`).
|
|
106
|
+
|
|
107
|
+
Provider adapters are the only place that knows whether a harness uses
|
|
108
|
+
`.claude/settings.json` versus `.cursor/hooks.json` versus a JS plugin —
|
|
109
|
+
and they all funnel execution back through the same `cleo` CLI.
|
|
110
|
+
|
|
111
|
+
### Adding a new harness
|
|
112
|
+
|
|
113
|
+
1. Add the provider's mapping to `packages/caamp/providers/hook-mappings.json`
|
|
114
|
+
(the SSoT). At minimum populate `hookSystem`, `hookConfigPath`,
|
|
115
|
+
`handlerTypes`, and the `PreCompact` entry under `mappings`.
|
|
116
|
+
2. Create `<provider>/templates/hooks/<shim>.sh` that sources the shared
|
|
117
|
+
helper. Keep the shim small — only provider-flavoured echo / exit handling
|
|
118
|
+
goes here.
|
|
119
|
+
3. Teach `shared/hook-template-installer.ts` about the new provider id by
|
|
120
|
+
adding it to `HookTemplateProviderId` and `PROVIDER_SHIM`.
|
|
121
|
+
4. Add the provider's `AdapterInstallProvider` method that calls
|
|
122
|
+
`installProviderHookTemplates` + writes the harness-specific config
|
|
123
|
+
fragment.
|
|
124
|
+
5. Ship tests under `<provider>/__tests__/hooks-install.test.ts`.
|
|
125
|
+
|
|
126
|
+
### Constraints
|
|
127
|
+
|
|
128
|
+
- **Shims MUST invoke CLEO only through the CLI.** No `require('@cleocode/core')`,
|
|
129
|
+
no reaching into core internals from bash.
|
|
130
|
+
- **Shims MUST be idempotent on install and uninstall.** Re-running the
|
|
131
|
+
installer must not duplicate config entries.
|
|
132
|
+
- **The shared helper is the one-and-only contract.** Duplicating its logic
|
|
133
|
+
into provider shims is a DRY violation — add provider-specific behaviour
|
|
134
|
+
only to the shim wrapper.
|
|
135
|
+
- **Hook failures MUST NOT block the harness.** All CLEO invocations in the
|
|
136
|
+
shim tolerate non-zero exits via `|| true` so the host compaction path is
|
|
137
|
+
never interrupted.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Claude Code PreCompact hook-template installer (T1013).
|
|
3
|
+
*
|
|
4
|
+
* Validates that:
|
|
5
|
+
* - Shared `cleo-precompact-core.sh` and provider-specific
|
|
6
|
+
* `precompact-safestop.sh` are copied into `~/.claude/hooks/`.
|
|
7
|
+
* - `~/.claude/settings.json` gains a `PreCompact` entry pointing at the
|
|
8
|
+
* installed shim, tagged with the `# cleo-hook` sentinel for clean uninstall.
|
|
9
|
+
* - Repeat invocations are idempotent (no duplicate settings entries).
|
|
10
|
+
* - The source templates contain the universal CLEO CLI invocations so the
|
|
11
|
+
* bash contract remains DRY across providers.
|
|
12
|
+
*
|
|
13
|
+
* The tests use real filesystem writes under a scoped tmp directory that
|
|
14
|
+
* impersonates `$HOME` via the `HOME` env var, so no user config is touched.
|
|
15
|
+
*
|
|
16
|
+
* @task T1013
|
|
17
|
+
* @epic T1000
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
21
|
+
import { tmpdir } from 'node:os';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
24
|
+
import { ClaudeCodeInstallProvider } from '../install.js';
|
|
25
|
+
|
|
26
|
+
describe('ClaudeCodeInstallProvider — PreCompact hook templates', () => {
|
|
27
|
+
let fakeHome: string;
|
|
28
|
+
let realHome: string | undefined;
|
|
29
|
+
let projectDir: string;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
realHome = process.env.HOME;
|
|
33
|
+
fakeHome = mkdtempSync(join(tmpdir(), 'cleo-claude-install-'));
|
|
34
|
+
projectDir = mkdtempSync(join(tmpdir(), 'cleo-claude-project-'));
|
|
35
|
+
process.env.HOME = fakeHome;
|
|
36
|
+
// Suppress unused-var lint on Windows (USERPROFILE pathway not exercised here).
|
|
37
|
+
void fakeHome;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
if (realHome !== undefined) {
|
|
42
|
+
process.env.HOME = realHome;
|
|
43
|
+
} else {
|
|
44
|
+
delete process.env.HOME;
|
|
45
|
+
}
|
|
46
|
+
rmSync(fakeHome, { recursive: true, force: true });
|
|
47
|
+
rmSync(projectDir, { recursive: true, force: true });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('installs both bash templates into $HOME/.claude/hooks/', async () => {
|
|
51
|
+
const provider = new ClaudeCodeInstallProvider();
|
|
52
|
+
|
|
53
|
+
const result = await provider.install({ projectDir });
|
|
54
|
+
expect(result.success).toBe(true);
|
|
55
|
+
|
|
56
|
+
const hookTemplates = (result.details?.hookTemplates ?? null) as {
|
|
57
|
+
templates: { installedFiles: string[]; targetDir: string };
|
|
58
|
+
settingsEntryAdded: boolean;
|
|
59
|
+
} | null;
|
|
60
|
+
|
|
61
|
+
expect(hookTemplates).not.toBeNull();
|
|
62
|
+
expect(hookTemplates?.templates.targetDir).toBe(join(fakeHome, '.claude', 'hooks'));
|
|
63
|
+
const installed = hookTemplates?.templates.installedFiles ?? [];
|
|
64
|
+
expect(installed.some((p) => p.endsWith('cleo-precompact-core.sh'))).toBe(true);
|
|
65
|
+
expect(installed.some((p) => p.endsWith('precompact-safestop.sh'))).toBe(true);
|
|
66
|
+
|
|
67
|
+
// Installed shim sources the shared helper.
|
|
68
|
+
const shim = readFileSync(
|
|
69
|
+
join(fakeHome, '.claude', 'hooks', 'precompact-safestop.sh'),
|
|
70
|
+
'utf-8',
|
|
71
|
+
);
|
|
72
|
+
expect(shim).toContain('cleo-precompact-core.sh');
|
|
73
|
+
// Shared helper invokes the universal CLEO CLI.
|
|
74
|
+
const core = readFileSync(
|
|
75
|
+
join(fakeHome, '.claude', 'hooks', 'cleo-precompact-core.sh'),
|
|
76
|
+
'utf-8',
|
|
77
|
+
);
|
|
78
|
+
expect(core).toContain('cleo memory precompact-flush');
|
|
79
|
+
expect(core).toContain('cleo');
|
|
80
|
+
expect(core).toMatch(/cleo_cmd.*safestop/);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('writes a PreCompact entry into $HOME/.claude/settings.json tagged # cleo-hook', async () => {
|
|
84
|
+
const provider = new ClaudeCodeInstallProvider();
|
|
85
|
+
await provider.install({ projectDir });
|
|
86
|
+
|
|
87
|
+
const settingsPath = join(fakeHome, '.claude', 'settings.json');
|
|
88
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8')) as {
|
|
89
|
+
hooks?: Record<string, Array<{ hooks?: Array<{ command?: string; type?: string }> }>>;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const preCompact = settings.hooks?.PreCompact ?? [];
|
|
93
|
+
expect(preCompact.length).toBeGreaterThan(0);
|
|
94
|
+
const firstEntry = preCompact[0];
|
|
95
|
+
expect(firstEntry).toBeDefined();
|
|
96
|
+
const firstHook = firstEntry?.hooks?.[0];
|
|
97
|
+
expect(firstHook?.type).toBe('command');
|
|
98
|
+
expect(firstHook?.command).toContain('precompact-safestop.sh');
|
|
99
|
+
expect(firstHook?.command).toContain('# cleo-hook');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('is idempotent — re-running install does not duplicate the PreCompact entry', async () => {
|
|
103
|
+
const provider = new ClaudeCodeInstallProvider();
|
|
104
|
+
await provider.install({ projectDir });
|
|
105
|
+
await provider.install({ projectDir });
|
|
106
|
+
|
|
107
|
+
const settingsPath = join(fakeHome, '.claude', 'settings.json');
|
|
108
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8')) as {
|
|
109
|
+
hooks?: { PreCompact?: unknown[] };
|
|
110
|
+
};
|
|
111
|
+
expect((settings.hooks?.PreCompact ?? []).length).toBe(1);
|
|
112
|
+
});
|
|
113
|
+
});
|