Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/cli/src/command-tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ describe("root command tree", () => {
"claude-hook-ingest",
"codex-hook-inject",
"codex-hook-ingest",
"pi-hook-inject",
"pi-hook-ingest",
"enqueue-raw-event",
].sort(),
);
Expand All @@ -78,6 +80,8 @@ describe("root command tree", () => {
"claude-hook-ingest",
"codex-hook-inject",
"codex-hook-ingest",
"pi-hook-inject",
"pi-hook-ingest",
"enqueue-raw-event",
]) {
expect(help).not.toMatch(new RegExp(`^\\s+${hiddenName}(?:\\s|$)`, "m"));
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/command-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
showMemoryCommand,
} from "./commands/memory.js";
import { packCommand, promptPackLedgerCommand } from "./commands/pack.js";
import { piHookIngestCommand } from "./commands/pi-hook-ingest.js";
import { piHookInjectCommand } from "./commands/pi-hook-inject.js";
import { recentCommand } from "./commands/recent.js";
import { searchCommand } from "./commands/search.js";
import { serveCommand } from "./commands/serve.js";
Expand Down Expand Up @@ -168,6 +170,8 @@ export function registerRootCommands(program: Command): Command {
program.addCommand(claudeHookFileContextCommand, { hidden: true });
program.addCommand(codexHookInjectCommand, { hidden: true });
program.addCommand(codexHookIngestCommand, { hidden: true });
program.addCommand(piHookInjectCommand, { hidden: true });
program.addCommand(piHookIngestCommand, { hidden: true });
program.addCommand(dbCommand);
program.addCommand(distillCommand);
// Warned compatibility aliases — visible for their first warned release;
Expand Down
51 changes: 51 additions & 0 deletions packages/cli/src/commands/pi-hook-ingest-spool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Durability layer for `pi-hook-ingest`. The lock/spool/drain/
* quarantine machinery is shared in `hook-ingest-spool.ts`; this file
* wires the pi-specific config (dirs, TTL 300s, 20 acquire attempts,
* error name/message) and keeps the pi flush predicate.
*/
import { createHookIngestSpool } from "./hook-ingest-spool.js";

export type { SpoolDrainResult, SpoolHandler } from "./hook-ingest-spool.js";

const spool = createHookIngestSpool({
logPrefix: "codemem pi-hook-ingest",
lockDirEnv: "CODEMEM_PI_HOOK_LOCK_DIR",
lockDirDefault: "~/.codemem/pi-hook-ingest.lock",
lockTtlEnv: "CODEMEM_PI_HOOK_LOCK_TTL_S",
lockTtlDefault: 300,
lockGraceEnv: "CODEMEM_PI_HOOK_LOCK_GRACE_S",
lockGraceDefault: 2,
lockAcquireAttempts: 20,
spoolDirEnv: "CODEMEM_PI_HOOK_SPOOL_DIR",
spoolDirDefault: "~/.codemem/pi-hook-spool",
lockBusyErrorName: "PiHookLockBusyError",
lockBusyErrorMessage: "pi-hook-ingest lock busy",
});

export const PiHookLockBusyError = spool.LockBusyError;
export const withPiHookIngestLock = spool.withLock;
export const spoolPiHookPayload = spool.spoolPayload;
export const drainPiHookSpool = spool.drainSpool;
export const hasPiHookSpooledEntries = spool.hasSpooledEntries;
export const recoverStalePiHookTmpSpool = spool.recoverStaleTmpSpool;
export const piHookLockTtlSeconds = spool.lockTtlSeconds;
export const piHookSpoolDir = spool.spoolDir;

/**
* Boundary flush for pi: compaction and session end both trigger extraction.
* session_before_compact is observe-only (never stored as transcript).
* session_shutdown maps to session_end and also flushes pending events.
*/
export function shouldForcePiBoundaryFlush(payload: Record<string, unknown>): boolean {
const eventName = coercePiEventName(payload);
return eventName === "session_before_compact" || eventName === "session_shutdown";
}

function coercePiEventName(payload: Record<string, unknown>): string {
for (const key of ["piEvent", "pi_event", "event", "type"] as const) {
const value = payload[key];
if (typeof value === "string" && value.trim()) return value.trim();
}
return "";
}
Loading
Loading