-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcodex.ts
More file actions
39 lines (33 loc) · 1.7 KB
/
Copy pathcodex.ts
File metadata and controls
39 lines (33 loc) · 1.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { detectCodex } from "./detect";
import { buildHookCommand, type InstallOutcome, type InstallScope } from "./install";
import { mergeHookRegistrations, type HookConfigDoc } from "./jsonHookMerge";
// Codex has no dedicated session-close event (SessionStart + Stop only,
// where Stop fires per-turn) — Stop still gets registered because it's the
// idle-gap heartbeat sessions/derive.ts relies on. Always written as pure
// JSON hooks.json rather than inline config.toml tables: Bun has a TOML
// *parser* but no round-trip-safe TOML *writer*, and naively
// parsing+re-serializing a user's config.toml would destroy their existing
// comments/formatting. hooks.json is also what Codex already uses on this
// machine (~/.codex/hooks.json), so this matches real-world usage, not just
// a theoretical fallback.
const CODEX_EVENTS = ["SessionStart", "Stop"] as const;
export async function installCodexHooks(
scope: InstallScope,
cwd: string,
opts: { force?: boolean } = {},
): Promise<InstallOutcome> {
const detection = await detectCodex(cwd);
const configPath = scope === "user" ? detection.userConfigPath : detection.projectConfigPath;
const file = Bun.file(configPath);
const existing: HookConfigDoc = (await file.exists()) ? await file.json() : {};
const registrations = await Promise.all(
CODEX_EVENTS.map(async (event) => ({
event,
command: await buildHookCommand("codex", event),
...(event === "SessionStart" ? { matcher: "startup|resume" } : {}),
})),
);
const { doc, outcomes } = mergeHookRegistrations(existing, registrations, opts);
await Bun.write(configPath, JSON.stringify(doc, null, 2) + "\n");
return { tool: "codex", scope, configPath, events: outcomes };
}