@cleocode/adapters 2026.4.31 → 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
package/dist/index.js
CHANGED
|
@@ -2273,6 +2273,638 @@ var init_opencode = __esm({
|
|
|
2273
2273
|
}
|
|
2274
2274
|
});
|
|
2275
2275
|
|
|
2276
|
+
// packages/adapters/src/providers/pi/hooks.ts
|
|
2277
|
+
var PROVIDER_ID4, PI_EVENT_MAP, PiHookProvider;
|
|
2278
|
+
var init_hooks4 = __esm({
|
|
2279
|
+
"packages/adapters/src/providers/pi/hooks.ts"() {
|
|
2280
|
+
"use strict";
|
|
2281
|
+
PROVIDER_ID4 = "pi";
|
|
2282
|
+
PI_EVENT_MAP = {
|
|
2283
|
+
// piEventCatalog: session_start → SessionStart
|
|
2284
|
+
session_start: "SessionStart",
|
|
2285
|
+
// piEventCatalog: session_shutdown → SessionEnd
|
|
2286
|
+
session_shutdown: "SessionEnd",
|
|
2287
|
+
// piEventCatalog: input → PromptSubmit
|
|
2288
|
+
input: "PromptSubmit",
|
|
2289
|
+
// piEventCatalog: turn_end → Notification (assistant turn complete)
|
|
2290
|
+
turn_end: "Notification",
|
|
2291
|
+
// piEventCatalog: tool_call → PreToolUse
|
|
2292
|
+
tool_call: "PreToolUse",
|
|
2293
|
+
// piEventCatalog: tool_execution_start → PreToolUse (duplicate path)
|
|
2294
|
+
tool_execution_start: "PreToolUse",
|
|
2295
|
+
// piEventCatalog: tool_result → PostToolUse
|
|
2296
|
+
tool_result: "PostToolUse",
|
|
2297
|
+
// piEventCatalog: tool_execution_end → PostToolUse (duplicate path)
|
|
2298
|
+
tool_execution_end: "PostToolUse",
|
|
2299
|
+
// piEventCatalog: before_agent_start → SubagentStart
|
|
2300
|
+
before_agent_start: "SubagentStart",
|
|
2301
|
+
// piEventCatalog: agent_end → SubagentStop
|
|
2302
|
+
agent_end: "SubagentStop",
|
|
2303
|
+
// piEventCatalog: before_provider_request → PreModel
|
|
2304
|
+
before_provider_request: "PreModel",
|
|
2305
|
+
// piEventCatalog: context → PreCompact (context assembly is the pre-compaction proxy for Pi)
|
|
2306
|
+
context: "PreCompact"
|
|
2307
|
+
};
|
|
2308
|
+
PiHookProvider = class {
|
|
2309
|
+
/** Whether hooks have been registered for the current session. */
|
|
2310
|
+
registered = false;
|
|
2311
|
+
/**
|
|
2312
|
+
* Map a Pi native event name to a CAAMP canonical hook event name.
|
|
2313
|
+
*
|
|
2314
|
+
* Looks up the native event name in the map derived from the
|
|
2315
|
+
* `piEventCatalog` block in CAAMP hook-mappings.json.
|
|
2316
|
+
* Returns null for unrecognised or unsupported events.
|
|
2317
|
+
*
|
|
2318
|
+
* @param providerEvent - Pi native event (e.g. "session_start", "tool_call")
|
|
2319
|
+
* @returns CAAMP canonical event name, or null if unmapped
|
|
2320
|
+
* @task T553
|
|
2321
|
+
*/
|
|
2322
|
+
mapProviderEvent(providerEvent) {
|
|
2323
|
+
return PI_EVENT_MAP[providerEvent] ?? null;
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Register native hooks for a project.
|
|
2327
|
+
*
|
|
2328
|
+
* For Pi, hooks are registered via the extension system, managed by
|
|
2329
|
+
* the install provider. This method marks hooks as registered without
|
|
2330
|
+
* performing filesystem operations.
|
|
2331
|
+
*
|
|
2332
|
+
* Iterating supported events is handled at install time using
|
|
2333
|
+
* `getSupportedCanonicalEvents()` to enumerate all 11 supported hooks.
|
|
2334
|
+
*
|
|
2335
|
+
* @param _projectDir - Project directory (unused; Pi uses extension system)
|
|
2336
|
+
* @task T553
|
|
2337
|
+
*/
|
|
2338
|
+
async registerNativeHooks(_projectDir) {
|
|
2339
|
+
this.registered = true;
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Unregister native hooks.
|
|
2343
|
+
*
|
|
2344
|
+
* For Pi, this is a no-op since hooks are managed through the extension
|
|
2345
|
+
* system. Unregistration happens via the install provider's uninstall method.
|
|
2346
|
+
*
|
|
2347
|
+
* @task T553
|
|
2348
|
+
*/
|
|
2349
|
+
async unregisterNativeHooks() {
|
|
2350
|
+
this.registered = false;
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* Check whether hooks have been registered via `registerNativeHooks`.
|
|
2354
|
+
*/
|
|
2355
|
+
isRegistered() {
|
|
2356
|
+
return this.registered;
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Get the native→canonical event mapping for introspection and debugging.
|
|
2360
|
+
*
|
|
2361
|
+
* Returns the map derived from the `piEventCatalog` block in CAAMP
|
|
2362
|
+
* hook-mappings.json. Use `getSupportedCanonicalEvents()` to enumerate
|
|
2363
|
+
* canonical names via live CAAMP APIs.
|
|
2364
|
+
*
|
|
2365
|
+
* @returns Immutable record of native event name → canonical event name
|
|
2366
|
+
*/
|
|
2367
|
+
getEventMap() {
|
|
2368
|
+
return { ...PI_EVENT_MAP };
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Enumerate supported canonical events via CAAMP's `getSupportedEvents()`.
|
|
2372
|
+
*
|
|
2373
|
+
* Calls `getSupportedEvents('pi')` from the CAAMP normalizer to get the
|
|
2374
|
+
* authoritative list. Pi supports 11 of 16 canonical events. Falls back
|
|
2375
|
+
* to the unique values of the static event map when CAAMP is unavailable.
|
|
2376
|
+
*
|
|
2377
|
+
* @returns Array of CAAMP canonical event names supported by Pi
|
|
2378
|
+
* @task T553
|
|
2379
|
+
*/
|
|
2380
|
+
async getSupportedCanonicalEvents() {
|
|
2381
|
+
try {
|
|
2382
|
+
const { getSupportedEvents } = await import("@cleocode/caamp");
|
|
2383
|
+
return getSupportedEvents(PROVIDER_ID4);
|
|
2384
|
+
} catch {
|
|
2385
|
+
return [...new Set(Object.values(PI_EVENT_MAP))];
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
/**
|
|
2389
|
+
* Retrieve the full provider hook profile from CAAMP.
|
|
2390
|
+
*
|
|
2391
|
+
* Calls `getProviderHookProfile('pi')` from the CAAMP normalizer to get
|
|
2392
|
+
* the complete profile including hook system type, config path, handler
|
|
2393
|
+
* types, and all event mappings. Returns null when CAAMP is unavailable.
|
|
2394
|
+
*
|
|
2395
|
+
* @returns Provider hook profile or null if CAAMP is unavailable
|
|
2396
|
+
* @task T553
|
|
2397
|
+
*/
|
|
2398
|
+
async getProviderProfile() {
|
|
2399
|
+
try {
|
|
2400
|
+
const { getProviderHookProfile } = await import("@cleocode/caamp");
|
|
2401
|
+
return getProviderHookProfile(PROVIDER_ID4) ?? null;
|
|
2402
|
+
} catch {
|
|
2403
|
+
return null;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Translate a CAAMP canonical event to its Pi native name via CAAMP.
|
|
2408
|
+
*
|
|
2409
|
+
* Calls `toNative(canonical, 'pi')` from the CAAMP normalizer.
|
|
2410
|
+
* Returns null for unsupported events or when CAAMP is unavailable.
|
|
2411
|
+
*
|
|
2412
|
+
* @param canonical - CAAMP canonical event name (e.g. "PreToolUse")
|
|
2413
|
+
* @returns Pi native event name or null
|
|
2414
|
+
* @task T553
|
|
2415
|
+
*/
|
|
2416
|
+
async toNativeEvent(canonical) {
|
|
2417
|
+
try {
|
|
2418
|
+
const { toNative } = await import("@cleocode/caamp");
|
|
2419
|
+
return toNative(canonical, PROVIDER_ID4);
|
|
2420
|
+
} catch {
|
|
2421
|
+
const entry = Object.entries(PI_EVENT_MAP).find(([, v]) => v === canonical);
|
|
2422
|
+
return entry?.[0] ?? null;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
});
|
|
2428
|
+
|
|
2429
|
+
// packages/adapters/src/providers/pi/install.ts
|
|
2430
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
2431
|
+
import { homedir as homedir11 } from "node:os";
|
|
2432
|
+
import { join as join22 } from "node:path";
|
|
2433
|
+
function getPiAgentDir() {
|
|
2434
|
+
const env = process.env["PI_CODING_AGENT_DIR"];
|
|
2435
|
+
if (env !== void 0 && env.length > 0) {
|
|
2436
|
+
if (env === "~") return homedir11();
|
|
2437
|
+
if (env.startsWith("~/")) return join22(homedir11(), env.slice(2));
|
|
2438
|
+
return env;
|
|
2439
|
+
}
|
|
2440
|
+
const piHome = process.env["PI_HOME"];
|
|
2441
|
+
if (piHome !== void 0 && piHome.length > 0) {
|
|
2442
|
+
return join22(piHome, "agent");
|
|
2443
|
+
}
|
|
2444
|
+
return join22(homedir11(), ".pi", "agent");
|
|
2445
|
+
}
|
|
2446
|
+
var INSTRUCTION_REFERENCES7, PiInstallProvider;
|
|
2447
|
+
var init_install4 = __esm({
|
|
2448
|
+
"packages/adapters/src/providers/pi/install.ts"() {
|
|
2449
|
+
"use strict";
|
|
2450
|
+
INSTRUCTION_REFERENCES7 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2451
|
+
PiInstallProvider = class {
|
|
2452
|
+
/**
|
|
2453
|
+
* Install CLEO into a Pi coding agent project.
|
|
2454
|
+
*
|
|
2455
|
+
* @param options - Installation options including project directory
|
|
2456
|
+
* @returns Result describing what was installed
|
|
2457
|
+
*/
|
|
2458
|
+
async install(options) {
|
|
2459
|
+
const { projectDir } = options;
|
|
2460
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2461
|
+
const details = {};
|
|
2462
|
+
const projectUpdated = this.updateInstructionFile(projectDir, "AGENTS.md");
|
|
2463
|
+
if (projectUpdated) {
|
|
2464
|
+
details.instructionFile = join22(projectDir, "AGENTS.md");
|
|
2465
|
+
}
|
|
2466
|
+
let globalUpdated = false;
|
|
2467
|
+
try {
|
|
2468
|
+
const globalDir = getPiAgentDir();
|
|
2469
|
+
globalUpdated = this.updateInstructionFile(globalDir, "AGENTS.md");
|
|
2470
|
+
if (globalUpdated) {
|
|
2471
|
+
details.globalInstructionFile = join22(globalDir, "AGENTS.md");
|
|
2472
|
+
}
|
|
2473
|
+
} catch {
|
|
2474
|
+
}
|
|
2475
|
+
const instructionFileUpdated = projectUpdated || globalUpdated;
|
|
2476
|
+
return {
|
|
2477
|
+
success: true,
|
|
2478
|
+
installedAt,
|
|
2479
|
+
instructionFileUpdated,
|
|
2480
|
+
details
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
/**
|
|
2484
|
+
* Uninstall CLEO from the current Pi project.
|
|
2485
|
+
*
|
|
2486
|
+
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
2487
|
+
*/
|
|
2488
|
+
async uninstall() {
|
|
2489
|
+
}
|
|
2490
|
+
/**
|
|
2491
|
+
* Check whether CLEO is installed in the current environment.
|
|
2492
|
+
*
|
|
2493
|
+
* Checks for CLEO references in the project AGENTS.md.
|
|
2494
|
+
*/
|
|
2495
|
+
async isInstalled() {
|
|
2496
|
+
const agentsMdPath = join22(process.cwd(), "AGENTS.md");
|
|
2497
|
+
if (existsSync15(agentsMdPath)) {
|
|
2498
|
+
try {
|
|
2499
|
+
const content = readFileSync9(agentsMdPath, "utf-8");
|
|
2500
|
+
if (INSTRUCTION_REFERENCES7.some((ref) => content.includes(ref))) {
|
|
2501
|
+
return true;
|
|
2502
|
+
}
|
|
2503
|
+
} catch {
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
try {
|
|
2507
|
+
const globalPath = join22(getPiAgentDir(), "AGENTS.md");
|
|
2508
|
+
if (existsSync15(globalPath)) {
|
|
2509
|
+
const content = readFileSync9(globalPath, "utf-8");
|
|
2510
|
+
if (INSTRUCTION_REFERENCES7.some((ref) => content.includes(ref))) {
|
|
2511
|
+
return true;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
} catch {
|
|
2515
|
+
}
|
|
2516
|
+
return false;
|
|
2517
|
+
}
|
|
2518
|
+
/**
|
|
2519
|
+
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
2520
|
+
*
|
|
2521
|
+
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
2522
|
+
*
|
|
2523
|
+
* @param projectDir - Project root directory
|
|
2524
|
+
*/
|
|
2525
|
+
async ensureInstructionReferences(projectDir) {
|
|
2526
|
+
this.updateInstructionFile(projectDir, "AGENTS.md");
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Update an instruction file with CLEO @-references.
|
|
2530
|
+
*
|
|
2531
|
+
* @param dir - Directory containing the instruction file
|
|
2532
|
+
* @param filename - Name of the instruction file (e.g. "AGENTS.md")
|
|
2533
|
+
* @returns true if the file was created or modified
|
|
2534
|
+
*/
|
|
2535
|
+
updateInstructionFile(dir, filename) {
|
|
2536
|
+
const filePath = join22(dir, filename);
|
|
2537
|
+
let content = "";
|
|
2538
|
+
let existed = false;
|
|
2539
|
+
if (existsSync15(filePath)) {
|
|
2540
|
+
content = readFileSync9(filePath, "utf-8");
|
|
2541
|
+
existed = true;
|
|
2542
|
+
}
|
|
2543
|
+
const missingRefs = INSTRUCTION_REFERENCES7.filter((ref) => !content.includes(ref));
|
|
2544
|
+
if (missingRefs.length === 0) {
|
|
2545
|
+
return false;
|
|
2546
|
+
}
|
|
2547
|
+
const refsBlock = missingRefs.join("\n");
|
|
2548
|
+
if (existed) {
|
|
2549
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
2550
|
+
content = content + separator + refsBlock + "\n";
|
|
2551
|
+
} else {
|
|
2552
|
+
mkdirSync3(dir, { recursive: true });
|
|
2553
|
+
content = refsBlock + "\n";
|
|
2554
|
+
}
|
|
2555
|
+
writeFileSync8(filePath, content, "utf-8");
|
|
2556
|
+
return true;
|
|
2557
|
+
}
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
});
|
|
2561
|
+
|
|
2562
|
+
// packages/adapters/src/providers/pi/spawn.ts
|
|
2563
|
+
import { exec as exec8, spawn as nodeSpawn3 } from "node:child_process";
|
|
2564
|
+
import { unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
|
|
2565
|
+
import { promisify as promisify8 } from "node:util";
|
|
2566
|
+
function getPiCliPath() {
|
|
2567
|
+
return process.env["PI_CLI_PATH"] ?? "pi";
|
|
2568
|
+
}
|
|
2569
|
+
var execAsync8, PiSpawnProvider;
|
|
2570
|
+
var init_spawn3 = __esm({
|
|
2571
|
+
"packages/adapters/src/providers/pi/spawn.ts"() {
|
|
2572
|
+
"use strict";
|
|
2573
|
+
init_src();
|
|
2574
|
+
execAsync8 = promisify8(exec8);
|
|
2575
|
+
PiSpawnProvider = class {
|
|
2576
|
+
/** Map of instance IDs to tracked process info. */
|
|
2577
|
+
processMap = /* @__PURE__ */ new Map();
|
|
2578
|
+
/**
|
|
2579
|
+
* Check if the Pi CLI is available.
|
|
2580
|
+
*
|
|
2581
|
+
* Checks `PI_CLI_PATH` env var first, then tries `which pi`.
|
|
2582
|
+
*
|
|
2583
|
+
* @returns true if the Pi CLI is accessible
|
|
2584
|
+
*/
|
|
2585
|
+
async canSpawn() {
|
|
2586
|
+
const cliPath = getPiCliPath();
|
|
2587
|
+
try {
|
|
2588
|
+
if (cliPath !== "pi") {
|
|
2589
|
+
const { stdout } = await execAsync8(`test -x "${cliPath}" && echo ok`);
|
|
2590
|
+
return stdout.trim() === "ok";
|
|
2591
|
+
}
|
|
2592
|
+
await execAsync8("which pi");
|
|
2593
|
+
return true;
|
|
2594
|
+
} catch {
|
|
2595
|
+
return false;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Spawn a subagent via Pi CLI.
|
|
2600
|
+
*
|
|
2601
|
+
* Writes the prompt to a temporary file and spawns a detached Pi
|
|
2602
|
+
* process. The process runs independently of the parent.
|
|
2603
|
+
*
|
|
2604
|
+
* @param context - Spawn context with taskId, prompt, and options
|
|
2605
|
+
* @returns Spawn result with instance ID and status
|
|
2606
|
+
*/
|
|
2607
|
+
async spawn(context) {
|
|
2608
|
+
const instanceId = `pi-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
2609
|
+
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2610
|
+
let tmpFile;
|
|
2611
|
+
try {
|
|
2612
|
+
tmpFile = `/tmp/pi-spawn-${instanceId}.txt`;
|
|
2613
|
+
await writeFile3(tmpFile, context.prompt, "utf-8");
|
|
2614
|
+
const cliPath = getPiCliPath();
|
|
2615
|
+
const args = [tmpFile];
|
|
2616
|
+
const spawnOpts = {
|
|
2617
|
+
detached: true,
|
|
2618
|
+
stdio: "ignore"
|
|
2619
|
+
};
|
|
2620
|
+
if (context.workingDirectory) {
|
|
2621
|
+
spawnOpts.cwd = context.workingDirectory;
|
|
2622
|
+
}
|
|
2623
|
+
const child = nodeSpawn3(cliPath, args, spawnOpts);
|
|
2624
|
+
child.unref();
|
|
2625
|
+
if (child.pid) {
|
|
2626
|
+
this.processMap.set(instanceId, {
|
|
2627
|
+
pid: child.pid,
|
|
2628
|
+
taskId: context.taskId,
|
|
2629
|
+
startTime
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
const capturedTmpFile = tmpFile;
|
|
2633
|
+
child.on("exit", async () => {
|
|
2634
|
+
this.processMap.delete(instanceId);
|
|
2635
|
+
try {
|
|
2636
|
+
await unlink2(capturedTmpFile);
|
|
2637
|
+
} catch {
|
|
2638
|
+
}
|
|
2639
|
+
});
|
|
2640
|
+
return {
|
|
2641
|
+
instanceId,
|
|
2642
|
+
taskId: context.taskId,
|
|
2643
|
+
providerId: "pi",
|
|
2644
|
+
status: "running",
|
|
2645
|
+
startTime
|
|
2646
|
+
};
|
|
2647
|
+
} catch (error) {
|
|
2648
|
+
console.error(`[PiSpawnProvider] Failed to spawn: ${getErrorMessage(error)}`);
|
|
2649
|
+
if (tmpFile) {
|
|
2650
|
+
try {
|
|
2651
|
+
await unlink2(tmpFile);
|
|
2652
|
+
} catch {
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
return {
|
|
2656
|
+
instanceId,
|
|
2657
|
+
taskId: context.taskId,
|
|
2658
|
+
providerId: "pi",
|
|
2659
|
+
status: "failed",
|
|
2660
|
+
startTime,
|
|
2661
|
+
endTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2662
|
+
error: getErrorMessage(error)
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* List currently running Pi subagent processes.
|
|
2668
|
+
*
|
|
2669
|
+
* Checks each tracked process via kill(pid, 0) to verify it is still alive.
|
|
2670
|
+
* Dead processes are automatically cleaned from the tracking map.
|
|
2671
|
+
*
|
|
2672
|
+
* @returns Array of spawn results for running processes
|
|
2673
|
+
*/
|
|
2674
|
+
async listRunning() {
|
|
2675
|
+
const running = [];
|
|
2676
|
+
for (const [instanceId, tracked] of this.processMap.entries()) {
|
|
2677
|
+
try {
|
|
2678
|
+
process.kill(tracked.pid, 0);
|
|
2679
|
+
running.push({
|
|
2680
|
+
instanceId,
|
|
2681
|
+
taskId: tracked.taskId,
|
|
2682
|
+
providerId: "pi",
|
|
2683
|
+
status: "running",
|
|
2684
|
+
startTime: tracked.startTime
|
|
2685
|
+
});
|
|
2686
|
+
} catch {
|
|
2687
|
+
this.processMap.delete(instanceId);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
return running;
|
|
2691
|
+
}
|
|
2692
|
+
/**
|
|
2693
|
+
* Terminate a running spawn by instance ID.
|
|
2694
|
+
*
|
|
2695
|
+
* Sends SIGTERM to the tracked process. If the process is not found
|
|
2696
|
+
* or has already exited, this is a no-op.
|
|
2697
|
+
*
|
|
2698
|
+
* @param instanceId - ID of the spawn instance to terminate
|
|
2699
|
+
*/
|
|
2700
|
+
async terminate(instanceId) {
|
|
2701
|
+
const tracked = this.processMap.get(instanceId);
|
|
2702
|
+
if (!tracked) return;
|
|
2703
|
+
try {
|
|
2704
|
+
process.kill(tracked.pid, "SIGTERM");
|
|
2705
|
+
} catch {
|
|
2706
|
+
}
|
|
2707
|
+
this.processMap.delete(instanceId);
|
|
2708
|
+
}
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
|
|
2713
|
+
// packages/adapters/src/providers/pi/adapter.ts
|
|
2714
|
+
import { exec as exec9 } from "node:child_process";
|
|
2715
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
2716
|
+
import { homedir as homedir12 } from "node:os";
|
|
2717
|
+
import { join as join23 } from "node:path";
|
|
2718
|
+
import { promisify as promisify9 } from "node:util";
|
|
2719
|
+
function getPiAgentDir2() {
|
|
2720
|
+
const env = process.env["PI_CODING_AGENT_DIR"];
|
|
2721
|
+
if (env !== void 0 && env.length > 0) {
|
|
2722
|
+
if (env === "~") return homedir12();
|
|
2723
|
+
if (env.startsWith("~/")) return join23(homedir12(), env.slice(2));
|
|
2724
|
+
return env;
|
|
2725
|
+
}
|
|
2726
|
+
const piHome = process.env["PI_HOME"];
|
|
2727
|
+
if (piHome !== void 0 && piHome.length > 0) {
|
|
2728
|
+
return join23(piHome, "agent");
|
|
2729
|
+
}
|
|
2730
|
+
return join23(homedir12(), ".pi", "agent");
|
|
2731
|
+
}
|
|
2732
|
+
var execAsync9, PiAdapter;
|
|
2733
|
+
var init_adapter4 = __esm({
|
|
2734
|
+
"packages/adapters/src/providers/pi/adapter.ts"() {
|
|
2735
|
+
"use strict";
|
|
2736
|
+
init_hooks4();
|
|
2737
|
+
init_install4();
|
|
2738
|
+
init_spawn3();
|
|
2739
|
+
execAsync9 = promisify9(exec9);
|
|
2740
|
+
PiAdapter = class {
|
|
2741
|
+
/** Unique provider identifier. */
|
|
2742
|
+
id = "pi";
|
|
2743
|
+
/** Human-readable provider name. */
|
|
2744
|
+
name = "Pi";
|
|
2745
|
+
/** Adapter version string. */
|
|
2746
|
+
version = "1.0.0";
|
|
2747
|
+
/** Declared capabilities for this provider. */
|
|
2748
|
+
capabilities = {
|
|
2749
|
+
supportsHooks: true,
|
|
2750
|
+
// 11/16 canonical events — derived from piEventCatalog in CAAMP hook-mappings.json.
|
|
2751
|
+
// ResponseComplete, PostToolUseFailure, PermissionRequest, PostModel, PostCompact,
|
|
2752
|
+
// and ConfigChange are not supported by Pi's extension system.
|
|
2753
|
+
supportedHookEvents: [
|
|
2754
|
+
"SessionStart",
|
|
2755
|
+
"SessionEnd",
|
|
2756
|
+
"PromptSubmit",
|
|
2757
|
+
"Notification",
|
|
2758
|
+
"PreToolUse",
|
|
2759
|
+
"PostToolUse",
|
|
2760
|
+
"SubagentStart",
|
|
2761
|
+
"SubagentStop",
|
|
2762
|
+
"PreModel",
|
|
2763
|
+
"PreCompact"
|
|
2764
|
+
],
|
|
2765
|
+
supportsSpawn: true,
|
|
2766
|
+
supportsInstall: true,
|
|
2767
|
+
supportsInstructionFiles: true,
|
|
2768
|
+
instructionFilePattern: "AGENTS.md",
|
|
2769
|
+
supportsContextMonitor: false,
|
|
2770
|
+
supportsStatusline: false,
|
|
2771
|
+
supportsProviderPaths: true,
|
|
2772
|
+
supportsTransport: false,
|
|
2773
|
+
supportsTaskSync: false
|
|
2774
|
+
};
|
|
2775
|
+
/** Hook provider for CAAMP event mapping and registration. */
|
|
2776
|
+
hooks;
|
|
2777
|
+
/** Spawn provider for launching subagent processes via `pi` CLI. */
|
|
2778
|
+
spawn;
|
|
2779
|
+
/** Install provider for managing AGENTS.md instruction files. */
|
|
2780
|
+
install;
|
|
2781
|
+
/** Project directory this adapter was initialized with, or null. */
|
|
2782
|
+
projectDir = null;
|
|
2783
|
+
/** Whether {@link initialize} has been called. */
|
|
2784
|
+
initialized = false;
|
|
2785
|
+
constructor() {
|
|
2786
|
+
this.hooks = new PiHookProvider();
|
|
2787
|
+
this.spawn = new PiSpawnProvider();
|
|
2788
|
+
this.install = new PiInstallProvider();
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* Initialize the adapter for a given project directory.
|
|
2792
|
+
*
|
|
2793
|
+
* Validates the environment by checking for the Pi CLI and Pi state root.
|
|
2794
|
+
*
|
|
2795
|
+
* @param projectDir - Root directory of the project
|
|
2796
|
+
*/
|
|
2797
|
+
async initialize(projectDir) {
|
|
2798
|
+
this.projectDir = projectDir;
|
|
2799
|
+
this.initialized = true;
|
|
2800
|
+
}
|
|
2801
|
+
/**
|
|
2802
|
+
* Dispose the adapter and clean up resources.
|
|
2803
|
+
*
|
|
2804
|
+
* Unregisters hooks and releases any tracked state.
|
|
2805
|
+
*/
|
|
2806
|
+
async dispose() {
|
|
2807
|
+
if (this.hooks.isRegistered()) {
|
|
2808
|
+
await this.hooks.unregisterNativeHooks();
|
|
2809
|
+
}
|
|
2810
|
+
this.initialized = false;
|
|
2811
|
+
this.projectDir = null;
|
|
2812
|
+
}
|
|
2813
|
+
/**
|
|
2814
|
+
* Run a health check to verify Pi is accessible.
|
|
2815
|
+
*
|
|
2816
|
+
* Checks:
|
|
2817
|
+
* 1. Adapter has been initialized
|
|
2818
|
+
* 2. Pi CLI is available (via PI_CLI_PATH or `which pi`)
|
|
2819
|
+
* 3. Pi global state root (~/.pi/agent/ or PI_CODING_AGENT_DIR) exists
|
|
2820
|
+
*
|
|
2821
|
+
* @returns Health status with details about each check
|
|
2822
|
+
*/
|
|
2823
|
+
async healthCheck() {
|
|
2824
|
+
const details = {};
|
|
2825
|
+
if (!this.initialized) {
|
|
2826
|
+
return {
|
|
2827
|
+
healthy: false,
|
|
2828
|
+
provider: this.id,
|
|
2829
|
+
details: { error: "Adapter not initialized" }
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2832
|
+
let cliAvailable = false;
|
|
2833
|
+
const cliPath = process.env["PI_CLI_PATH"] ?? "pi";
|
|
2834
|
+
try {
|
|
2835
|
+
if (cliPath !== "pi") {
|
|
2836
|
+
const { stdout } = await execAsync9(`test -x "${cliPath}" && echo ok`);
|
|
2837
|
+
cliAvailable = stdout.trim() === "ok";
|
|
2838
|
+
details.cliPath = cliPath;
|
|
2839
|
+
} else {
|
|
2840
|
+
const { stdout } = await execAsync9("which pi");
|
|
2841
|
+
cliAvailable = stdout.trim().length > 0;
|
|
2842
|
+
details.cliPath = stdout.trim();
|
|
2843
|
+
}
|
|
2844
|
+
} catch {
|
|
2845
|
+
details.cliAvailable = false;
|
|
2846
|
+
}
|
|
2847
|
+
const agentDir = getPiAgentDir2();
|
|
2848
|
+
const agentDirExists = existsSync16(agentDir);
|
|
2849
|
+
details.agentDirExists = agentDirExists;
|
|
2850
|
+
details.agentDir = agentDir;
|
|
2851
|
+
if (this.projectDir) {
|
|
2852
|
+
const projectPiDir = join23(this.projectDir, ".pi");
|
|
2853
|
+
details.projectPiDirExists = existsSync16(projectPiDir);
|
|
2854
|
+
}
|
|
2855
|
+
details.piCodingAgentDirSet = process.env["PI_CODING_AGENT_DIR"] !== void 0;
|
|
2856
|
+
details.piHomeSet = process.env["PI_HOME"] !== void 0;
|
|
2857
|
+
details.piCliPathSet = process.env["PI_CLI_PATH"] !== void 0;
|
|
2858
|
+
const healthy = cliAvailable || agentDirExists;
|
|
2859
|
+
details.cliAvailable = cliAvailable;
|
|
2860
|
+
return {
|
|
2861
|
+
healthy,
|
|
2862
|
+
provider: this.id,
|
|
2863
|
+
details
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Check whether the adapter has been initialized.
|
|
2868
|
+
*/
|
|
2869
|
+
isInitialized() {
|
|
2870
|
+
return this.initialized;
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* Get the project directory this adapter was initialized with.
|
|
2874
|
+
*/
|
|
2875
|
+
getProjectDir() {
|
|
2876
|
+
return this.projectDir;
|
|
2877
|
+
}
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
});
|
|
2881
|
+
|
|
2882
|
+
// packages/adapters/src/providers/pi/index.ts
|
|
2883
|
+
var pi_exports = {};
|
|
2884
|
+
__export(pi_exports, {
|
|
2885
|
+
PiAdapter: () => PiAdapter,
|
|
2886
|
+
PiHookProvider: () => PiHookProvider,
|
|
2887
|
+
PiInstallProvider: () => PiInstallProvider,
|
|
2888
|
+
PiSpawnProvider: () => PiSpawnProvider,
|
|
2889
|
+
createAdapter: () => createAdapter7,
|
|
2890
|
+
default: () => pi_default
|
|
2891
|
+
});
|
|
2892
|
+
function createAdapter7() {
|
|
2893
|
+
return new PiAdapter();
|
|
2894
|
+
}
|
|
2895
|
+
var pi_default;
|
|
2896
|
+
var init_pi = __esm({
|
|
2897
|
+
"packages/adapters/src/providers/pi/index.ts"() {
|
|
2898
|
+
"use strict";
|
|
2899
|
+
init_adapter4();
|
|
2900
|
+
init_adapter4();
|
|
2901
|
+
init_hooks4();
|
|
2902
|
+
init_install4();
|
|
2903
|
+
init_spawn3();
|
|
2904
|
+
pi_default = PiAdapter;
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
|
|
2276
2908
|
// packages/adapters/src/index.ts
|
|
2277
2909
|
init_claude_code();
|
|
2278
2910
|
|
|
@@ -3216,17 +3848,17 @@ function createAdapter5() {
|
|
|
3216
3848
|
init_opencode();
|
|
3217
3849
|
|
|
3218
3850
|
// packages/adapters/src/registry.ts
|
|
3219
|
-
import { readFileSync as
|
|
3220
|
-
import { dirname as dirname3, join as
|
|
3851
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
3852
|
+
import { dirname as dirname3, join as join24, resolve } from "node:path";
|
|
3221
3853
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3222
|
-
var PROVIDER_IDS = ["claude-code", "opencode", "cursor"];
|
|
3854
|
+
var PROVIDER_IDS = ["claude-code", "opencode", "cursor", "pi"];
|
|
3223
3855
|
function getProviderManifests() {
|
|
3224
3856
|
const manifests = [];
|
|
3225
3857
|
const baseDir = resolve(dirname3(fileURLToPath2(import.meta.url)), "providers");
|
|
3226
3858
|
for (const providerId of PROVIDER_IDS) {
|
|
3227
3859
|
try {
|
|
3228
|
-
const manifestPath =
|
|
3229
|
-
const raw =
|
|
3860
|
+
const manifestPath = join24(baseDir, providerId, "manifest.json");
|
|
3861
|
+
const raw = readFileSync10(manifestPath, "utf-8");
|
|
3230
3862
|
manifests.push(JSON.parse(raw));
|
|
3231
3863
|
} catch {
|
|
3232
3864
|
}
|
|
@@ -3247,6 +3879,10 @@ async function discoverProviders() {
|
|
|
3247
3879
|
const { CursorAdapter: CursorAdapter2 } = await Promise.resolve().then(() => (init_cursor(), cursor_exports));
|
|
3248
3880
|
return new CursorAdapter2();
|
|
3249
3881
|
});
|
|
3882
|
+
providers.set("pi", async () => {
|
|
3883
|
+
const { PiAdapter: PiAdapter2 } = await Promise.resolve().then(() => (init_pi(), pi_exports));
|
|
3884
|
+
return new PiAdapter2();
|
|
3885
|
+
});
|
|
3250
3886
|
return providers;
|
|
3251
3887
|
}
|
|
3252
3888
|
export {
|