@cleocode/adapters 2026.4.30 → 2026.4.35
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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Hook Provider
|
|
3
|
+
*
|
|
4
|
+
* Maps Pi coding agent native event names to CAAMP canonical hook events.
|
|
5
|
+
* Pi supports 11 of 16 canonical events through its extension/event system.
|
|
6
|
+
*
|
|
7
|
+
* Event translation uses CAAMP normalizer APIs:
|
|
8
|
+
* - `toCanonical(nativeName, 'pi')` for runtime event name resolution
|
|
9
|
+
* - `getSupportedEvents('pi')` to enumerate supported canonical events
|
|
10
|
+
* - `getProviderHookProfile('pi')` for the full provider profile
|
|
11
|
+
*
|
|
12
|
+
* A static map derived from CAAMP hook-mappings.json piEventCatalog is
|
|
13
|
+
* maintained as a fallback for environments where CAAMP's runtime
|
|
14
|
+
* resolution is unavailable.
|
|
15
|
+
*
|
|
16
|
+
* Mappings (11/16 supported):
|
|
17
|
+
* session_start → SessionStart
|
|
18
|
+
* session_shutdown → SessionEnd
|
|
19
|
+
* input → PromptSubmit
|
|
20
|
+
* turn_end → Notification (assistant turn complete)
|
|
21
|
+
* tool_call → PreToolUse
|
|
22
|
+
* tool_result → PostToolUse
|
|
23
|
+
* before_agent_start → SubagentStart
|
|
24
|
+
* agent_end → SubagentStop
|
|
25
|
+
* before_provider_request → PreModel
|
|
26
|
+
* context → PreCompact (context assembly = pre-compaction proxy)
|
|
27
|
+
*
|
|
28
|
+
* Unsupported (5/16):
|
|
29
|
+
* ResponseComplete, PostToolUseFailure, PermissionRequest,
|
|
30
|
+
* PostModel, PostCompact, ConfigChange
|
|
31
|
+
*
|
|
32
|
+
* @task T553
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import type { AdapterHookProvider } from '@cleocode/contracts';
|
|
36
|
+
|
|
37
|
+
/** CAAMP provider identifier for Pi. */
|
|
38
|
+
const PROVIDER_ID = 'pi' as const;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Fallback map from Pi native event names to CAAMP canonical names.
|
|
42
|
+
*
|
|
43
|
+
* Derived from the `piEventCatalog` block in CAAMP hook-mappings.json.
|
|
44
|
+
* Covers all 11 supported events. ResponseComplete, PostToolUseFailure,
|
|
45
|
+
* PermissionRequest, PostModel, PostCompact, and ConfigChange are not
|
|
46
|
+
* supported by Pi and are absent from this map.
|
|
47
|
+
*
|
|
48
|
+
* Used as fallback when CAAMP runtime is unavailable, and as the
|
|
49
|
+
* synchronous implementation of `mapProviderEvent()`.
|
|
50
|
+
*/
|
|
51
|
+
const PI_EVENT_MAP: Record<string, string> = {
|
|
52
|
+
// piEventCatalog: session_start → SessionStart
|
|
53
|
+
session_start: 'SessionStart',
|
|
54
|
+
// piEventCatalog: session_shutdown → SessionEnd
|
|
55
|
+
session_shutdown: 'SessionEnd',
|
|
56
|
+
// piEventCatalog: input → PromptSubmit
|
|
57
|
+
input: 'PromptSubmit',
|
|
58
|
+
// piEventCatalog: turn_end → Notification (assistant turn complete)
|
|
59
|
+
turn_end: 'Notification',
|
|
60
|
+
// piEventCatalog: tool_call → PreToolUse
|
|
61
|
+
tool_call: 'PreToolUse',
|
|
62
|
+
// piEventCatalog: tool_execution_start → PreToolUse (duplicate path)
|
|
63
|
+
tool_execution_start: 'PreToolUse',
|
|
64
|
+
// piEventCatalog: tool_result → PostToolUse
|
|
65
|
+
tool_result: 'PostToolUse',
|
|
66
|
+
// piEventCatalog: tool_execution_end → PostToolUse (duplicate path)
|
|
67
|
+
tool_execution_end: 'PostToolUse',
|
|
68
|
+
// piEventCatalog: before_agent_start → SubagentStart
|
|
69
|
+
before_agent_start: 'SubagentStart',
|
|
70
|
+
// piEventCatalog: agent_end → SubagentStop
|
|
71
|
+
agent_end: 'SubagentStop',
|
|
72
|
+
// piEventCatalog: before_provider_request → PreModel
|
|
73
|
+
before_provider_request: 'PreModel',
|
|
74
|
+
// piEventCatalog: context → PreCompact (context assembly is the pre-compaction proxy for Pi)
|
|
75
|
+
context: 'PreCompact',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Hook provider for Pi coding agent.
|
|
80
|
+
*
|
|
81
|
+
* Pi registers hooks via its TypeScript extension system. Extensions are
|
|
82
|
+
* loaded from `~/.pi/agent/extensions/*.ts` (global) or
|
|
83
|
+
* `<projectDir>/.pi/extensions/*.ts` (project scope).
|
|
84
|
+
*
|
|
85
|
+
* Event mapping is based on the `piEventCatalog` block in CAAMP
|
|
86
|
+
* hook-mappings.json. Async accessors (`getSupportedCanonicalEvents`,
|
|
87
|
+
* `getProviderProfile`) call CAAMP directly when available.
|
|
88
|
+
*
|
|
89
|
+
* Since hooks are registered through the extension system (managed by the
|
|
90
|
+
* install provider), `registerNativeHooks` and `unregisterNativeHooks`
|
|
91
|
+
* track registration state without performing filesystem operations.
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* Pi is CAAMP's first-class primary harness (ADR-035). Its native events
|
|
95
|
+
* use snake_case (e.g. `session_start`, `tool_call`) unlike the PascalCase
|
|
96
|
+
* CAAMP canonical names. The static event map covers all 11 supported events.
|
|
97
|
+
* Async CAAMP accessors fall back to the static map when CAAMP is unavailable.
|
|
98
|
+
*
|
|
99
|
+
* Pi does NOT support ResponseComplete, PostToolUseFailure, PermissionRequest,
|
|
100
|
+
* PostModel, PostCompact, or ConfigChange canonical events.
|
|
101
|
+
*
|
|
102
|
+
* All hook dispatch is best-effort — hooks MUST never block or crash Pi.
|
|
103
|
+
*
|
|
104
|
+
* @task T553
|
|
105
|
+
*/
|
|
106
|
+
export class PiHookProvider implements AdapterHookProvider {
|
|
107
|
+
/** Whether hooks have been registered for the current session. */
|
|
108
|
+
private registered = false;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Map a Pi native event name to a CAAMP canonical hook event name.
|
|
112
|
+
*
|
|
113
|
+
* Looks up the native event name in the map derived from the
|
|
114
|
+
* `piEventCatalog` block in CAAMP hook-mappings.json.
|
|
115
|
+
* Returns null for unrecognised or unsupported events.
|
|
116
|
+
*
|
|
117
|
+
* @param providerEvent - Pi native event (e.g. "session_start", "tool_call")
|
|
118
|
+
* @returns CAAMP canonical event name, or null if unmapped
|
|
119
|
+
* @task T553
|
|
120
|
+
*/
|
|
121
|
+
mapProviderEvent(providerEvent: string): string | null {
|
|
122
|
+
return PI_EVENT_MAP[providerEvent] ?? null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Register native hooks for a project.
|
|
127
|
+
*
|
|
128
|
+
* For Pi, hooks are registered via the extension system, managed by
|
|
129
|
+
* the install provider. This method marks hooks as registered without
|
|
130
|
+
* performing filesystem operations.
|
|
131
|
+
*
|
|
132
|
+
* Iterating supported events is handled at install time using
|
|
133
|
+
* `getSupportedCanonicalEvents()` to enumerate all 11 supported hooks.
|
|
134
|
+
*
|
|
135
|
+
* @param _projectDir - Project directory (unused; Pi uses extension system)
|
|
136
|
+
* @task T553
|
|
137
|
+
*/
|
|
138
|
+
async registerNativeHooks(_projectDir: string): Promise<void> {
|
|
139
|
+
this.registered = true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Unregister native hooks.
|
|
144
|
+
*
|
|
145
|
+
* For Pi, this is a no-op since hooks are managed through the extension
|
|
146
|
+
* system. Unregistration happens via the install provider's uninstall method.
|
|
147
|
+
*
|
|
148
|
+
* @task T553
|
|
149
|
+
*/
|
|
150
|
+
async unregisterNativeHooks(): Promise<void> {
|
|
151
|
+
this.registered = false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check whether hooks have been registered via `registerNativeHooks`.
|
|
156
|
+
*/
|
|
157
|
+
isRegistered(): boolean {
|
|
158
|
+
return this.registered;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get the native→canonical event mapping for introspection and debugging.
|
|
163
|
+
*
|
|
164
|
+
* Returns the map derived from the `piEventCatalog` block in CAAMP
|
|
165
|
+
* hook-mappings.json. Use `getSupportedCanonicalEvents()` to enumerate
|
|
166
|
+
* canonical names via live CAAMP APIs.
|
|
167
|
+
*
|
|
168
|
+
* @returns Immutable record of native event name → canonical event name
|
|
169
|
+
*/
|
|
170
|
+
getEventMap(): Readonly<Record<string, string>> {
|
|
171
|
+
return { ...PI_EVENT_MAP };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Enumerate supported canonical events via CAAMP's `getSupportedEvents()`.
|
|
176
|
+
*
|
|
177
|
+
* Calls `getSupportedEvents('pi')` from the CAAMP normalizer to get the
|
|
178
|
+
* authoritative list. Pi supports 11 of 16 canonical events. Falls back
|
|
179
|
+
* to the unique values of the static event map when CAAMP is unavailable.
|
|
180
|
+
*
|
|
181
|
+
* @returns Array of CAAMP canonical event names supported by Pi
|
|
182
|
+
* @task T553
|
|
183
|
+
*/
|
|
184
|
+
async getSupportedCanonicalEvents(): Promise<string[]> {
|
|
185
|
+
try {
|
|
186
|
+
const { getSupportedEvents } = await import('@cleocode/caamp');
|
|
187
|
+
return getSupportedEvents(PROVIDER_ID) as string[];
|
|
188
|
+
} catch {
|
|
189
|
+
return [...new Set(Object.values(PI_EVENT_MAP))];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Retrieve the full provider hook profile from CAAMP.
|
|
195
|
+
*
|
|
196
|
+
* Calls `getProviderHookProfile('pi')` from the CAAMP normalizer to get
|
|
197
|
+
* the complete profile including hook system type, config path, handler
|
|
198
|
+
* types, and all event mappings. Returns null when CAAMP is unavailable.
|
|
199
|
+
*
|
|
200
|
+
* @returns Provider hook profile or null if CAAMP is unavailable
|
|
201
|
+
* @task T553
|
|
202
|
+
*/
|
|
203
|
+
async getProviderProfile(): Promise<unknown | null> {
|
|
204
|
+
try {
|
|
205
|
+
const { getProviderHookProfile } = await import('@cleocode/caamp');
|
|
206
|
+
return getProviderHookProfile(PROVIDER_ID) ?? null;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Translate a CAAMP canonical event to its Pi native name via CAAMP.
|
|
214
|
+
*
|
|
215
|
+
* Calls `toNative(canonical, 'pi')` from the CAAMP normalizer.
|
|
216
|
+
* Returns null for unsupported events or when CAAMP is unavailable.
|
|
217
|
+
*
|
|
218
|
+
* @param canonical - CAAMP canonical event name (e.g. "PreToolUse")
|
|
219
|
+
* @returns Pi native event name or null
|
|
220
|
+
* @task T553
|
|
221
|
+
*/
|
|
222
|
+
async toNativeEvent(canonical: string): Promise<string | null> {
|
|
223
|
+
try {
|
|
224
|
+
const { toNative } = await import('@cleocode/caamp');
|
|
225
|
+
return toNative(canonical as Parameters<typeof toNative>[0], PROVIDER_ID);
|
|
226
|
+
} catch {
|
|
227
|
+
// Invert the static map as fallback — return the first match
|
|
228
|
+
const entry = Object.entries(PI_EVENT_MAP).find(([, v]) => v === canonical);
|
|
229
|
+
return entry?.[0] ?? null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* CLEO provider adapter for Pi coding agent (https://github.com/badlogic/pi-mono).
|
|
5
|
+
* Pi is CAAMP's first-class primary harness with 11/16 canonical hook events.
|
|
6
|
+
* Default export is the adapter class for dynamic loading by AdapterManager.
|
|
7
|
+
*
|
|
8
|
+
* @task T553
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { PiAdapter } from './adapter.js';
|
|
12
|
+
|
|
13
|
+
export { PiAdapter } from './adapter.js';
|
|
14
|
+
export { PiHookProvider } from './hooks.js';
|
|
15
|
+
export { PiInstallProvider } from './install.js';
|
|
16
|
+
export { PiSpawnProvider } from './spawn.js';
|
|
17
|
+
|
|
18
|
+
export default PiAdapter;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Factory function for creating Pi adapter instances.
|
|
22
|
+
* Used by AdapterManager's dynamic import fallback.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* This is the primary entry point for dynamic adapter loading.
|
|
26
|
+
* AdapterManager calls this function when it resolves the pi provider
|
|
27
|
+
* via its import-based discovery mechanism.
|
|
28
|
+
*
|
|
29
|
+
* @returns A new {@link PiAdapter} instance ready for initialization
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { createAdapter } from '@cleocode/adapters/providers/pi';
|
|
34
|
+
*
|
|
35
|
+
* const adapter = createAdapter();
|
|
36
|
+
* await adapter.initialize('/path/to/project');
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function createAdapter(): PiAdapter {
|
|
40
|
+
return new PiAdapter();
|
|
41
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Install Provider
|
|
3
|
+
*
|
|
4
|
+
* Handles CLEO installation into Pi coding agent environments:
|
|
5
|
+
* - Ensures AGENTS.md has CLEO @-references (project and global scope)
|
|
6
|
+
* - Manages Pi settings.json to register CLEO extension path
|
|
7
|
+
*
|
|
8
|
+
* Pi uses AGENTS.md (not CLAUDE.md) as its instruction file. The global
|
|
9
|
+
* instruction file lives at `~/.pi/agent/AGENTS.md`; the project-level
|
|
10
|
+
* file lives at `<projectDir>/AGENTS.md`.
|
|
11
|
+
*
|
|
12
|
+
* Detection: Pi is detected by the `PI_CODING_AGENT_DIR` or `PI_HOME`
|
|
13
|
+
* environment variables, or by presence of `~/.pi/agent/` directory.
|
|
14
|
+
*
|
|
15
|
+
* @task T553
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
22
|
+
|
|
23
|
+
/** Lines that should appear in AGENTS.md to reference CLEO. */
|
|
24
|
+
const INSTRUCTION_REFERENCES = ['@~/.cleo/templates/CLEO-INJECTION.md', '@.cleo/memory-bridge.md'];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the Pi global state root directory.
|
|
28
|
+
*
|
|
29
|
+
* Honours `PI_CODING_AGENT_DIR` env var when set (with `~` expansion),
|
|
30
|
+
* then `PI_HOME`, then falls back to `~/.pi/agent`.
|
|
31
|
+
*/
|
|
32
|
+
function getPiAgentDir(): string {
|
|
33
|
+
const env = process.env['PI_CODING_AGENT_DIR'];
|
|
34
|
+
if (env !== undefined && env.length > 0) {
|
|
35
|
+
if (env === '~') return homedir();
|
|
36
|
+
if (env.startsWith('~/')) return join(homedir(), env.slice(2));
|
|
37
|
+
return env;
|
|
38
|
+
}
|
|
39
|
+
const piHome = process.env['PI_HOME'];
|
|
40
|
+
if (piHome !== undefined && piHome.length > 0) {
|
|
41
|
+
return join(piHome, 'agent');
|
|
42
|
+
}
|
|
43
|
+
return join(homedir(), '.pi', 'agent');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Install provider for Pi coding agent.
|
|
48
|
+
*
|
|
49
|
+
* Manages CLEO's integration with Pi by:
|
|
50
|
+
* 1. Ensuring project AGENTS.md contains @-references to CLEO instruction files
|
|
51
|
+
* 2. Ensuring global AGENTS.md (~/.pi/agent/AGENTS.md) contains @-references
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* Installation is idempotent — running install multiple times on the same
|
|
55
|
+
* project produces the same result. Pi's AGENTS.md is auto-discovered from
|
|
56
|
+
* the working directory upwards, so project-level injection is the primary
|
|
57
|
+
* mechanism. Global injection ensures fresh sessions always load CLEO context.
|
|
58
|
+
*/
|
|
59
|
+
export class PiInstallProvider implements AdapterInstallProvider {
|
|
60
|
+
/**
|
|
61
|
+
* Install CLEO into a Pi coding agent project.
|
|
62
|
+
*
|
|
63
|
+
* @param options - Installation options including project directory
|
|
64
|
+
* @returns Result describing what was installed
|
|
65
|
+
*/
|
|
66
|
+
async install(options: InstallOptions): Promise<InstallResult> {
|
|
67
|
+
const { projectDir } = options;
|
|
68
|
+
const installedAt = new Date().toISOString();
|
|
69
|
+
const details: Record<string, unknown> = {};
|
|
70
|
+
|
|
71
|
+
// Step 1: Ensure project AGENTS.md has @-references
|
|
72
|
+
const projectUpdated = this.updateInstructionFile(projectDir, 'AGENTS.md');
|
|
73
|
+
if (projectUpdated) {
|
|
74
|
+
details.instructionFile = join(projectDir, 'AGENTS.md');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Step 2: Ensure global AGENTS.md has @-references (best-effort)
|
|
78
|
+
let globalUpdated = false;
|
|
79
|
+
try {
|
|
80
|
+
const globalDir = getPiAgentDir();
|
|
81
|
+
globalUpdated = this.updateInstructionFile(globalDir, 'AGENTS.md');
|
|
82
|
+
if (globalUpdated) {
|
|
83
|
+
details.globalInstructionFile = join(globalDir, 'AGENTS.md');
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Global install is best-effort — never block project install
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const instructionFileUpdated = projectUpdated || globalUpdated;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
installedAt,
|
|
94
|
+
instructionFileUpdated,
|
|
95
|
+
details,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Uninstall CLEO from the current Pi project.
|
|
101
|
+
*
|
|
102
|
+
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
103
|
+
*/
|
|
104
|
+
async uninstall(): Promise<void> {}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check whether CLEO is installed in the current environment.
|
|
108
|
+
*
|
|
109
|
+
* Checks for CLEO references in the project AGENTS.md.
|
|
110
|
+
*/
|
|
111
|
+
async isInstalled(): Promise<boolean> {
|
|
112
|
+
const agentsMdPath = join(process.cwd(), 'AGENTS.md');
|
|
113
|
+
if (existsSync(agentsMdPath)) {
|
|
114
|
+
try {
|
|
115
|
+
const content = readFileSync(agentsMdPath, 'utf-8');
|
|
116
|
+
if (INSTRUCTION_REFERENCES.some((ref) => content.includes(ref))) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// Fall through
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Also check global AGENTS.md
|
|
125
|
+
try {
|
|
126
|
+
const globalPath = join(getPiAgentDir(), 'AGENTS.md');
|
|
127
|
+
if (existsSync(globalPath)) {
|
|
128
|
+
const content = readFileSync(globalPath, 'utf-8');
|
|
129
|
+
if (INSTRUCTION_REFERENCES.some((ref) => content.includes(ref))) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// Fall through
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
142
|
+
*
|
|
143
|
+
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
144
|
+
*
|
|
145
|
+
* @param projectDir - Project root directory
|
|
146
|
+
*/
|
|
147
|
+
async ensureInstructionReferences(projectDir: string): Promise<void> {
|
|
148
|
+
this.updateInstructionFile(projectDir, 'AGENTS.md');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Update an instruction file with CLEO @-references.
|
|
153
|
+
*
|
|
154
|
+
* @param dir - Directory containing the instruction file
|
|
155
|
+
* @param filename - Name of the instruction file (e.g. "AGENTS.md")
|
|
156
|
+
* @returns true if the file was created or modified
|
|
157
|
+
*/
|
|
158
|
+
private updateInstructionFile(dir: string, filename: string): boolean {
|
|
159
|
+
const filePath = join(dir, filename);
|
|
160
|
+
let content = '';
|
|
161
|
+
let existed = false;
|
|
162
|
+
|
|
163
|
+
if (existsSync(filePath)) {
|
|
164
|
+
content = readFileSync(filePath, 'utf-8');
|
|
165
|
+
existed = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const missingRefs = INSTRUCTION_REFERENCES.filter((ref) => !content.includes(ref));
|
|
169
|
+
|
|
170
|
+
if (missingRefs.length === 0) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const refsBlock = missingRefs.join('\n');
|
|
175
|
+
|
|
176
|
+
if (existed) {
|
|
177
|
+
// Append missing references
|
|
178
|
+
const separator = content.endsWith('\n') ? '' : '\n';
|
|
179
|
+
content = content + separator + refsBlock + '\n';
|
|
180
|
+
} else {
|
|
181
|
+
// Create new file with references — ensure parent dir exists
|
|
182
|
+
mkdirSync(dir, { recursive: true });
|
|
183
|
+
content = refsBlock + '\n';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
writeFileSync(filePath, content, 'utf-8');
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "pi",
|
|
3
|
+
"name": "Pi Adapter",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "CLEO adapter for Pi coding agent — CAAMP's first-class primary harness",
|
|
6
|
+
"provider": "pi",
|
|
7
|
+
"entryPoint": "src/index.ts",
|
|
8
|
+
"capabilities": {
|
|
9
|
+
"supportsHooks": true,
|
|
10
|
+
"supportedHookEvents": [
|
|
11
|
+
"SessionStart",
|
|
12
|
+
"SessionEnd",
|
|
13
|
+
"PromptSubmit",
|
|
14
|
+
"Notification",
|
|
15
|
+
"PreToolUse",
|
|
16
|
+
"PostToolUse",
|
|
17
|
+
"SubagentStart",
|
|
18
|
+
"SubagentStop",
|
|
19
|
+
"PreModel",
|
|
20
|
+
"PreCompact"
|
|
21
|
+
],
|
|
22
|
+
"supportsSpawn": true,
|
|
23
|
+
"supportsInstall": true,
|
|
24
|
+
"supportsMcp": false,
|
|
25
|
+
"supportsInstructionFiles": true,
|
|
26
|
+
"instructionFilePattern": "AGENTS.md",
|
|
27
|
+
"supportsContextMonitor": false,
|
|
28
|
+
"supportsStatusline": false,
|
|
29
|
+
"supportsProviderPaths": true,
|
|
30
|
+
"supportsTransport": false,
|
|
31
|
+
"supportsTaskSync": false
|
|
32
|
+
},
|
|
33
|
+
"detectionPatterns": [
|
|
34
|
+
{ "type": "env", "pattern": "PI_CLI_PATH", "description": "Custom Pi CLI path" },
|
|
35
|
+
{ "type": "env", "pattern": "PI_CODING_AGENT_DIR", "description": "Pi global state root directory" },
|
|
36
|
+
{ "type": "env", "pattern": "PI_HOME", "description": "Pi home directory" },
|
|
37
|
+
{ "type": "file", "pattern": ".pi/settings.json", "description": "Pi project config directory" },
|
|
38
|
+
{ "type": "cli", "pattern": "pi", "description": "Pi CLI available in PATH" }
|
|
39
|
+
]
|
|
40
|
+
}
|