@cleocode/adapters 2026.4.31 → 2026.4.36
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 +641 -5
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code/adapter.js +184 -0
- package/dist/providers/claude-code/adapter.js.map +1 -0
- package/dist/providers/claude-code/context-monitor.js +159 -0
- package/dist/providers/claude-code/context-monitor.js.map +1 -0
- package/dist/providers/claude-code/hooks.js +286 -0
- package/dist/providers/claude-code/hooks.js.map +1 -0
- package/dist/providers/claude-code/index.js +41 -0
- package/dist/providers/claude-code/index.js.map +1 -0
- package/dist/providers/claude-code/install.js +199 -0
- package/dist/providers/claude-code/install.js.map +1 -0
- package/dist/providers/claude-code/paths.js +41 -0
- package/dist/providers/claude-code/paths.js.map +1 -0
- package/dist/providers/claude-code/spawn.js +171 -0
- package/dist/providers/claude-code/spawn.js.map +1 -0
- package/dist/providers/claude-code/statusline.js +130 -0
- package/dist/providers/claude-code/statusline.js.map +1 -0
- package/dist/providers/claude-code/task-sync.js +119 -0
- package/dist/providers/claude-code/task-sync.js.map +1 -0
- package/dist/providers/claude-code/transport.js +29 -0
- package/dist/providers/claude-code/transport.js.map +1 -0
- package/dist/providers/codex/adapter.js +146 -0
- package/dist/providers/codex/adapter.js.map +1 -0
- package/dist/providers/codex/hooks.js +113 -0
- package/dist/providers/codex/hooks.js.map +1 -0
- package/dist/providers/codex/index.js +39 -0
- package/dist/providers/codex/index.js.map +1 -0
- package/dist/providers/codex/install.js +124 -0
- package/dist/providers/codex/install.js.map +1 -0
- package/dist/providers/cursor/adapter.js +151 -0
- package/dist/providers/cursor/adapter.js.map +1 -0
- package/dist/providers/cursor/hooks.js +208 -0
- package/dist/providers/cursor/hooks.js.map +1 -0
- package/dist/providers/cursor/index.js +36 -0
- package/dist/providers/cursor/index.js.map +1 -0
- package/dist/providers/cursor/install.js +180 -0
- package/dist/providers/cursor/install.js.map +1 -0
- package/dist/providers/cursor/spawn.js +59 -0
- package/dist/providers/cursor/spawn.js.map +1 -0
- package/dist/providers/gemini-cli/adapter.js +158 -0
- package/dist/providers/gemini-cli/adapter.js.map +1 -0
- package/dist/providers/gemini-cli/hooks.js +128 -0
- package/dist/providers/gemini-cli/hooks.js.map +1 -0
- package/dist/providers/gemini-cli/index.js +39 -0
- package/dist/providers/gemini-cli/index.js.map +1 -0
- package/dist/providers/gemini-cli/install.js +124 -0
- package/dist/providers/gemini-cli/install.js.map +1 -0
- package/dist/providers/kimi/adapter.js +145 -0
- package/dist/providers/kimi/adapter.js.map +1 -0
- package/dist/providers/kimi/hooks.js +79 -0
- package/dist/providers/kimi/hooks.js.map +1 -0
- package/dist/providers/kimi/index.js +39 -0
- package/dist/providers/kimi/index.js.map +1 -0
- package/dist/providers/kimi/install.js +124 -0
- package/dist/providers/kimi/install.js.map +1 -0
- package/dist/providers/opencode/adapter.js +166 -0
- package/dist/providers/opencode/adapter.js.map +1 -0
- package/dist/providers/opencode/hooks.js +206 -0
- package/dist/providers/opencode/hooks.js.map +1 -0
- package/dist/providers/opencode/index.js +37 -0
- package/dist/providers/opencode/index.js.map +1 -0
- package/dist/providers/opencode/install.js +115 -0
- package/dist/providers/opencode/install.js.map +1 -0
- package/dist/providers/opencode/spawn.js +241 -0
- package/dist/providers/opencode/spawn.js.map +1 -0
- package/dist/providers/pi/adapter.d.ts +102 -0
- package/dist/providers/pi/adapter.d.ts.map +1 -0
- package/dist/providers/pi/adapter.js +220 -0
- package/dist/providers/pi/adapter.js.map +1 -0
- package/dist/providers/pi/hooks.d.ts +149 -0
- package/dist/providers/pi/hooks.d.ts.map +1 -0
- package/dist/providers/pi/hooks.js +223 -0
- package/dist/providers/pi/hooks.js.map +1 -0
- package/dist/providers/pi/index.d.ts +36 -0
- package/dist/providers/pi/index.d.ts.map +1 -0
- package/dist/providers/pi/index.js +38 -0
- package/dist/providers/pi/index.js.map +1 -0
- package/dist/providers/pi/install.d.ts +68 -0
- package/dist/providers/pi/install.d.ts.map +1 -0
- package/dist/providers/pi/install.js +175 -0
- package/dist/providers/pi/install.js.map +1 -0
- package/dist/providers/pi/spawn.d.ts +68 -0
- package/dist/providers/pi/spawn.d.ts.map +1 -0
- package/dist/providers/pi/spawn.js +187 -0
- package/dist/providers/pi/spawn.js.map +1 -0
- package/dist/providers/shared/transcript-reader.js +124 -0
- package/dist/providers/shared/transcript-reader.js.map +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +92 -0
- package/dist/registry.js.map +1 -0
- package/package.json +3 -3
- package/src/providers/pi/adapter.ts +240 -0
- package/src/providers/pi/hooks.ts +232 -0
- package/src/providers/pi/index.ts +41 -0
- package/src/providers/pi/install.ts +189 -0
- package/src/providers/pi/manifest.json +40 -0
- package/src/providers/pi/spawn.ts +207 -0
- package/src/registry.ts +6 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared transcript-reading utility for provider hook adapters.
|
|
3
|
+
*
|
|
4
|
+
* Several providers (Gemini CLI, Codex CLI) store session data in a
|
|
5
|
+
* flat directory of JSON/JSONL files using the same role/content schema.
|
|
6
|
+
* This module centralises the "find most-recent file, parse turns"
|
|
7
|
+
* logic to avoid duplicating it in each hook provider.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { readLatestTranscript } from '../shared/transcript-reader.js';
|
|
12
|
+
*
|
|
13
|
+
* async getTranscript(_sessionId: string, _projectDir: string) {
|
|
14
|
+
* return readLatestTranscript(join(homedir(), '.gemini'));
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @task T161
|
|
19
|
+
* @epic T134
|
|
20
|
+
*/
|
|
21
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Parse a raw JSONL or JSON session file into an array of transcript turns.
|
|
28
|
+
*
|
|
29
|
+
* Lines that are not valid JSON, or that lack a string `role` and string
|
|
30
|
+
* `content`, are silently skipped.
|
|
31
|
+
*
|
|
32
|
+
* @param raw - Raw file contents (UTF-8 string).
|
|
33
|
+
* @returns Array of `{ role, content }` pairs, in file order.
|
|
34
|
+
*/
|
|
35
|
+
function parseTranscriptLines(raw) {
|
|
36
|
+
const turns = [];
|
|
37
|
+
const lines = raw.split('\n').filter((l) => l.trim());
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
try {
|
|
40
|
+
const entry = JSON.parse(line);
|
|
41
|
+
const role = entry.role;
|
|
42
|
+
const content = entry.content;
|
|
43
|
+
if (typeof role === 'string' && typeof content === 'string') {
|
|
44
|
+
turns.push({ role, content });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Skip malformed lines
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return turns;
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Public API
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
/**
|
|
57
|
+
* Read the most recent JSON or JSONL session file from `providerDir` and
|
|
58
|
+
* return its contents as a flat transcript string.
|
|
59
|
+
*
|
|
60
|
+
* Files are sorted in descending order by filename — this works naturally
|
|
61
|
+
* for providers that embed timestamps in filenames. The most recently named
|
|
62
|
+
* file is read first.
|
|
63
|
+
*
|
|
64
|
+
* Returns `null` when:
|
|
65
|
+
* - `providerDir` does not exist or cannot be read
|
|
66
|
+
* - No JSON/JSONL files are present
|
|
67
|
+
* - The most recent file contains no parseable turns
|
|
68
|
+
*
|
|
69
|
+
* @remarks
|
|
70
|
+
* This utility is shared by Gemini CLI and Codex CLI hook providers which
|
|
71
|
+
* both store session data in the same flat JSON/JSONL format. Only the
|
|
72
|
+
* most recent file is read to keep memory usage bounded.
|
|
73
|
+
*
|
|
74
|
+
* @param providerDir - Absolute path to the provider's session directory
|
|
75
|
+
* (e.g. `~/.gemini` or `~/.codex`).
|
|
76
|
+
* @returns A plain-text transcript with lines of the form `role: content`,
|
|
77
|
+
* or `null` if no transcript could be extracted.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* import { readLatestTranscript } from '../shared/transcript-reader.js';
|
|
82
|
+
*
|
|
83
|
+
* const transcript = await readLatestTranscript('/home/user/.gemini');
|
|
84
|
+
* if (transcript) {
|
|
85
|
+
* console.log(transcript);
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @task T161
|
|
90
|
+
* @epic T134
|
|
91
|
+
*/
|
|
92
|
+
export async function readLatestTranscript(providerDir) {
|
|
93
|
+
let allFiles = [];
|
|
94
|
+
try {
|
|
95
|
+
const entries = await readdir(providerDir, { withFileTypes: true });
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
if (!entry.isFile())
|
|
98
|
+
continue;
|
|
99
|
+
const name = entry.name;
|
|
100
|
+
if (name.endsWith('.json') || name.endsWith('.jsonl')) {
|
|
101
|
+
allFiles.push(join(providerDir, name));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (allFiles.length === 0)
|
|
109
|
+
return null;
|
|
110
|
+
// Sort descending — timestamps in filenames sort naturally
|
|
111
|
+
allFiles = allFiles.sort((a, b) => b.localeCompare(a));
|
|
112
|
+
const mostRecent = allFiles[0];
|
|
113
|
+
if (!mostRecent)
|
|
114
|
+
return null;
|
|
115
|
+
try {
|
|
116
|
+
const raw = await readFile(mostRecent, 'utf-8');
|
|
117
|
+
const turns = parseTranscriptLines(raw);
|
|
118
|
+
return turns.length > 0 ? turns.map((t) => `${t.role}: ${t.content}`).join('\n') : null;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=transcript-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-reader.js","sourceRoot":"","sources":["../../../src/providers/shared/transcript-reader.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,8DAA8D;IAC9D,iBAAiB,EAAE,KAAK,CAAC;QACvB,oDAAoD;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,sCAAsC;QACtC,OAAO,EAAE,MAAM,CAAC;QAChB,yDAAyD;QACzD,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,IAAI,eAAe,EAAE,CAexD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,8DAA8D;IAC9D,iBAAiB,EAAE,KAAK,CAAC;QACvB,oDAAoD;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,sCAAsC;QACtC,OAAO,EAAE,MAAM,CAAC;QAChB,yDAAyD;QACzD,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,IAAI,eAAe,EAAE,CAexD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAwBtF"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter registry — discovers and provides access to provider manifests.
|
|
3
|
+
*
|
|
4
|
+
* Scans the providers/ directory for manifest.json files and returns
|
|
5
|
+
* the discovered adapter manifests for use by AdapterManager.
|
|
6
|
+
*
|
|
7
|
+
* @task T5240
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { dirname, join, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
/** Known provider IDs bundled with @cleocode/adapters. */
|
|
13
|
+
const PROVIDER_IDS = ['claude-code', 'opencode', 'cursor', 'pi'];
|
|
14
|
+
/**
|
|
15
|
+
* Get the manifests for all bundled provider adapters.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Scans the known provider directories for `manifest.json` files.
|
|
19
|
+
* Providers whose manifests cannot be loaded (missing or malformed)
|
|
20
|
+
* are silently skipped.
|
|
21
|
+
*
|
|
22
|
+
* @returns Array of adapter manifests for successfully loaded providers
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { getProviderManifests } from '@cleocode/adapters';
|
|
27
|
+
*
|
|
28
|
+
* const manifests = getProviderManifests();
|
|
29
|
+
* for (const m of manifests) {
|
|
30
|
+
* console.log(`${m.id}: ${m.name} v${m.version}`);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function getProviderManifests() {
|
|
35
|
+
const manifests = [];
|
|
36
|
+
const baseDir = resolve(dirname(fileURLToPath(import.meta.url)), 'providers');
|
|
37
|
+
for (const providerId of PROVIDER_IDS) {
|
|
38
|
+
try {
|
|
39
|
+
const manifestPath = join(baseDir, providerId, 'manifest.json');
|
|
40
|
+
const raw = readFileSync(manifestPath, 'utf-8');
|
|
41
|
+
manifests.push(JSON.parse(raw));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Skip providers whose manifests cannot be loaded
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return manifests;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Discover all available provider adapters.
|
|
51
|
+
*
|
|
52
|
+
* Returns a map of provider ID to adapter factory function.
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* Each factory lazily imports the provider module and constructs a new
|
|
56
|
+
* adapter instance. This avoids loading all provider code upfront and
|
|
57
|
+
* keeps startup fast.
|
|
58
|
+
*
|
|
59
|
+
* @returns Map of provider ID to async factory function that creates an adapter instance
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import { discoverProviders } from '@cleocode/adapters';
|
|
64
|
+
*
|
|
65
|
+
* const providers = await discoverProviders();
|
|
66
|
+
* const factory = providers.get('claude-code');
|
|
67
|
+
* if (factory) {
|
|
68
|
+
* const adapter = await factory();
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export async function discoverProviders() {
|
|
73
|
+
const providers = new Map();
|
|
74
|
+
providers.set('claude-code', async () => {
|
|
75
|
+
const { ClaudeCodeAdapter } = await import('./providers/claude-code/index.js');
|
|
76
|
+
return new ClaudeCodeAdapter();
|
|
77
|
+
});
|
|
78
|
+
providers.set('opencode', async () => {
|
|
79
|
+
const { OpenCodeAdapter } = await import('./providers/opencode/index.js');
|
|
80
|
+
return new OpenCodeAdapter();
|
|
81
|
+
});
|
|
82
|
+
providers.set('cursor', async () => {
|
|
83
|
+
const { CursorAdapter } = await import('./providers/cursor/index.js');
|
|
84
|
+
return new CursorAdapter();
|
|
85
|
+
});
|
|
86
|
+
providers.set('pi', async () => {
|
|
87
|
+
const { PiAdapter } = await import('./providers/pi/index.js');
|
|
88
|
+
return new PiAdapter();
|
|
89
|
+
});
|
|
90
|
+
return providers;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAqCzC,0DAA0D;AAC1D,MAAM,YAAY,GAAG,CAAC,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAU,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAE9E,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;YAChE,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YAChD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkC,CAAC;IAE5D,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;QAC/E,OAAO,IAAI,iBAAiB,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,+BAA+B,CAAC,CAAC;QAC1E,OAAO,IAAI,eAAe,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACjC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QACtE,OAAO,IAAI,aAAa,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAC7B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QAC9D,OAAO,IAAI,SAAS,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/adapters",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.36",
|
|
4
4
|
"description": "Unified provider adapters for CLEO (Claude Code, OpenCode, Cursor)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@cleocode/contracts": "2026.4.
|
|
16
|
-
"@cleocode/caamp": "2026.4.
|
|
15
|
+
"@cleocode/contracts": "2026.4.36",
|
|
16
|
+
"@cleocode/caamp": "2026.4.36"
|
|
17
17
|
},
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"engines": {
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Adapter
|
|
3
|
+
*
|
|
4
|
+
* Main CLEOProviderAdapter implementation for the Pi coding agent
|
|
5
|
+
* (https://github.com/badlogic/pi-mono). Pi is CAAMP's first-class primary
|
|
6
|
+
* harness and owns skills, instructions, extensions, and subagent spawning
|
|
7
|
+
* through native filesystem conventions.
|
|
8
|
+
*
|
|
9
|
+
* Pi supports 11 of 16 CAAMP canonical events through its TypeScript extension
|
|
10
|
+
* system (session_start, session_shutdown, input, turn_end, tool_call,
|
|
11
|
+
* tool_result, before_agent_start, agent_end, before_provider_request, context).
|
|
12
|
+
*
|
|
13
|
+
* Detection: PI_CLI_PATH env var, PI_CODING_AGENT_DIR env var, PI_HOME env var,
|
|
14
|
+
* or presence of ~/.pi/agent/ directory.
|
|
15
|
+
*
|
|
16
|
+
* @task T553
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { exec } from 'node:child_process';
|
|
20
|
+
import { existsSync } from 'node:fs';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { promisify } from 'node:util';
|
|
24
|
+
import type {
|
|
25
|
+
AdapterCapabilities,
|
|
26
|
+
AdapterHealthStatus,
|
|
27
|
+
CLEOProviderAdapter,
|
|
28
|
+
} from '@cleocode/contracts';
|
|
29
|
+
import { PiHookProvider } from './hooks.js';
|
|
30
|
+
import { PiInstallProvider } from './install.js';
|
|
31
|
+
import { PiSpawnProvider } from './spawn.js';
|
|
32
|
+
|
|
33
|
+
const execAsync = promisify(exec);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the Pi global state root directory.
|
|
37
|
+
*
|
|
38
|
+
* Honours `PI_CODING_AGENT_DIR` env var when set (with `~` expansion),
|
|
39
|
+
* then `PI_HOME`, then falls back to `~/.pi/agent`.
|
|
40
|
+
*/
|
|
41
|
+
function getPiAgentDir(): string {
|
|
42
|
+
const env = process.env['PI_CODING_AGENT_DIR'];
|
|
43
|
+
if (env !== undefined && env.length > 0) {
|
|
44
|
+
if (env === '~') return homedir();
|
|
45
|
+
if (env.startsWith('~/')) return join(homedir(), env.slice(2));
|
|
46
|
+
return env;
|
|
47
|
+
}
|
|
48
|
+
const piHome = process.env['PI_HOME'];
|
|
49
|
+
if (piHome !== undefined && piHome.length > 0) {
|
|
50
|
+
return join(piHome, 'agent');
|
|
51
|
+
}
|
|
52
|
+
return join(homedir(), '.pi', 'agent');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* CLEO provider adapter for Pi coding agent.
|
|
57
|
+
*
|
|
58
|
+
* Bridges CLEO's adapter system with Pi's native capabilities:
|
|
59
|
+
* - Hooks: Maps Pi events (session_start, tool_call, etc.) to CAAMP events
|
|
60
|
+
* - Spawn: Launches subagent processes via the `pi` CLI
|
|
61
|
+
* - Install: Manages AGENTS.md instruction files and global Pi state root
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* Pi is CAAMP's first-class primary harness (ADR-035). It supports 11 of 16
|
|
65
|
+
* canonical hook events through its TypeScript extension system. Extensions
|
|
66
|
+
* live at `~/.pi/agent/extensions/` (global) or `<projectDir>/.pi/extensions/`
|
|
67
|
+
* (project scope).
|
|
68
|
+
*
|
|
69
|
+
* The session_shutdown event handler in `cleo-cant-bridge.ts` clears module
|
|
70
|
+
* cache. The adapter's hook mapping ensures that Pi's session lifecycle events
|
|
71
|
+
* are visible in the CAAMP event stream for downstream listeners (e.g. memory
|
|
72
|
+
* refresh triggers, backup triggers).
|
|
73
|
+
*
|
|
74
|
+
* Detection hierarchy (first match wins):
|
|
75
|
+
* 1. `PI_CLI_PATH` env var set
|
|
76
|
+
* 2. `PI_CODING_AGENT_DIR` env var set
|
|
77
|
+
* 3. `PI_HOME` env var set
|
|
78
|
+
* 4. `~/.pi/agent/` directory exists
|
|
79
|
+
* 5. `pi` CLI available in PATH
|
|
80
|
+
*/
|
|
81
|
+
export class PiAdapter implements CLEOProviderAdapter {
|
|
82
|
+
/** Unique provider identifier. */
|
|
83
|
+
readonly id = 'pi';
|
|
84
|
+
/** Human-readable provider name. */
|
|
85
|
+
readonly name = 'Pi';
|
|
86
|
+
/** Adapter version string. */
|
|
87
|
+
readonly version = '1.0.0';
|
|
88
|
+
|
|
89
|
+
/** Declared capabilities for this provider. */
|
|
90
|
+
capabilities: AdapterCapabilities = {
|
|
91
|
+
supportsHooks: true,
|
|
92
|
+
// 11/16 canonical events — derived from piEventCatalog in CAAMP hook-mappings.json.
|
|
93
|
+
// ResponseComplete, PostToolUseFailure, PermissionRequest, PostModel, PostCompact,
|
|
94
|
+
// and ConfigChange are not supported by Pi's extension system.
|
|
95
|
+
supportedHookEvents: [
|
|
96
|
+
'SessionStart',
|
|
97
|
+
'SessionEnd',
|
|
98
|
+
'PromptSubmit',
|
|
99
|
+
'Notification',
|
|
100
|
+
'PreToolUse',
|
|
101
|
+
'PostToolUse',
|
|
102
|
+
'SubagentStart',
|
|
103
|
+
'SubagentStop',
|
|
104
|
+
'PreModel',
|
|
105
|
+
'PreCompact',
|
|
106
|
+
],
|
|
107
|
+
supportsSpawn: true,
|
|
108
|
+
supportsInstall: true,
|
|
109
|
+
supportsInstructionFiles: true,
|
|
110
|
+
instructionFilePattern: 'AGENTS.md',
|
|
111
|
+
supportsContextMonitor: false,
|
|
112
|
+
supportsStatusline: false,
|
|
113
|
+
supportsProviderPaths: true,
|
|
114
|
+
supportsTransport: false,
|
|
115
|
+
supportsTaskSync: false,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** Hook provider for CAAMP event mapping and registration. */
|
|
119
|
+
hooks: PiHookProvider;
|
|
120
|
+
/** Spawn provider for launching subagent processes via `pi` CLI. */
|
|
121
|
+
spawn: PiSpawnProvider;
|
|
122
|
+
/** Install provider for managing AGENTS.md instruction files. */
|
|
123
|
+
install: PiInstallProvider;
|
|
124
|
+
|
|
125
|
+
/** Project directory this adapter was initialized with, or null. */
|
|
126
|
+
private projectDir: string | null = null;
|
|
127
|
+
/** Whether {@link initialize} has been called. */
|
|
128
|
+
private initialized = false;
|
|
129
|
+
|
|
130
|
+
constructor() {
|
|
131
|
+
this.hooks = new PiHookProvider();
|
|
132
|
+
this.spawn = new PiSpawnProvider();
|
|
133
|
+
this.install = new PiInstallProvider();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Initialize the adapter for a given project directory.
|
|
138
|
+
*
|
|
139
|
+
* Validates the environment by checking for the Pi CLI and Pi state root.
|
|
140
|
+
*
|
|
141
|
+
* @param projectDir - Root directory of the project
|
|
142
|
+
*/
|
|
143
|
+
async initialize(projectDir: string): Promise<void> {
|
|
144
|
+
this.projectDir = projectDir;
|
|
145
|
+
this.initialized = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Dispose the adapter and clean up resources.
|
|
150
|
+
*
|
|
151
|
+
* Unregisters hooks and releases any tracked state.
|
|
152
|
+
*/
|
|
153
|
+
async dispose(): Promise<void> {
|
|
154
|
+
if (this.hooks.isRegistered()) {
|
|
155
|
+
await this.hooks.unregisterNativeHooks();
|
|
156
|
+
}
|
|
157
|
+
this.initialized = false;
|
|
158
|
+
this.projectDir = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Run a health check to verify Pi is accessible.
|
|
163
|
+
*
|
|
164
|
+
* Checks:
|
|
165
|
+
* 1. Adapter has been initialized
|
|
166
|
+
* 2. Pi CLI is available (via PI_CLI_PATH or `which pi`)
|
|
167
|
+
* 3. Pi global state root (~/.pi/agent/ or PI_CODING_AGENT_DIR) exists
|
|
168
|
+
*
|
|
169
|
+
* @returns Health status with details about each check
|
|
170
|
+
*/
|
|
171
|
+
async healthCheck(): Promise<AdapterHealthStatus> {
|
|
172
|
+
const details: Record<string, unknown> = {};
|
|
173
|
+
|
|
174
|
+
if (!this.initialized) {
|
|
175
|
+
return {
|
|
176
|
+
healthy: false,
|
|
177
|
+
provider: this.id,
|
|
178
|
+
details: { error: 'Adapter not initialized' },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check Pi CLI availability
|
|
183
|
+
let cliAvailable = false;
|
|
184
|
+
const cliPath = process.env['PI_CLI_PATH'] ?? 'pi';
|
|
185
|
+
try {
|
|
186
|
+
if (cliPath !== 'pi') {
|
|
187
|
+
const { stdout } = await execAsync(`test -x "${cliPath}" && echo ok`);
|
|
188
|
+
cliAvailable = stdout.trim() === 'ok';
|
|
189
|
+
details.cliPath = cliPath;
|
|
190
|
+
} else {
|
|
191
|
+
const { stdout } = await execAsync('which pi');
|
|
192
|
+
cliAvailable = stdout.trim().length > 0;
|
|
193
|
+
details.cliPath = stdout.trim();
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
details.cliAvailable = false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check for Pi global state root
|
|
200
|
+
const agentDir = getPiAgentDir();
|
|
201
|
+
const agentDirExists = existsSync(agentDir);
|
|
202
|
+
details.agentDirExists = agentDirExists;
|
|
203
|
+
details.agentDir = agentDir;
|
|
204
|
+
|
|
205
|
+
// Check for project-level .pi/ directory
|
|
206
|
+
if (this.projectDir) {
|
|
207
|
+
const projectPiDir = join(this.projectDir, '.pi');
|
|
208
|
+
details.projectPiDirExists = existsSync(projectPiDir);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check detection env vars
|
|
212
|
+
details.piCodingAgentDirSet = process.env['PI_CODING_AGENT_DIR'] !== undefined;
|
|
213
|
+
details.piHomeSet = process.env['PI_HOME'] !== undefined;
|
|
214
|
+
details.piCliPathSet = process.env['PI_CLI_PATH'] !== undefined;
|
|
215
|
+
|
|
216
|
+
// Healthy if either CLI is available or global state root exists
|
|
217
|
+
const healthy = cliAvailable || agentDirExists;
|
|
218
|
+
details.cliAvailable = cliAvailable;
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
healthy,
|
|
222
|
+
provider: this.id,
|
|
223
|
+
details,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Check whether the adapter has been initialized.
|
|
229
|
+
*/
|
|
230
|
+
isInitialized(): boolean {
|
|
231
|
+
return this.initialized;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get the project directory this adapter was initialized with.
|
|
236
|
+
*/
|
|
237
|
+
getProjectDir(): string | null {
|
|
238
|
+
return this.projectDir;
|
|
239
|
+
}
|
|
240
|
+
}
|