@goodfoot/agent-hooks 1.0.0

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.
Files changed (68) hide show
  1. package/README.md +137 -0
  2. package/dist/agents/antigravity/events.js +42 -0
  3. package/dist/agents/antigravity/hooks.js +59 -0
  4. package/dist/agents/antigravity/index.js +28 -0
  5. package/dist/agents/antigravity/outputs.js +81 -0
  6. package/dist/agents/antigravity/transport.js +84 -0
  7. package/dist/agents/antigravity/types.js +11 -0
  8. package/dist/agents/claude-code/env.js +27 -0
  9. package/dist/agents/claude-code/events.js +87 -0
  10. package/dist/agents/claude-code/hooks.js +1000 -0
  11. package/dist/agents/claude-code/index.js +57 -0
  12. package/dist/agents/claude-code/outputs.js +579 -0
  13. package/dist/agents/claude-code/tool-helpers.js +840 -0
  14. package/dist/agents/claude-code/transport.js +254 -0
  15. package/dist/agents/claude-code/types.js +11 -0
  16. package/dist/agents/codex/cli-support.js +318 -0
  17. package/dist/agents/codex/constants.js +29 -0
  18. package/dist/agents/codex/events.js +68 -0
  19. package/dist/agents/codex/hooks.js +93 -0
  20. package/dist/agents/codex/index.js +21 -0
  21. package/dist/agents/codex/outputs.js +189 -0
  22. package/dist/agents/codex/scaffold.js +273 -0
  23. package/dist/agents/codex/transport.js +139 -0
  24. package/dist/agents/codex/types.js +1 -0
  25. package/dist/cli.js +1357 -0
  26. package/dist/constants.js +39 -0
  27. package/dist/core/define-hook.js +61 -0
  28. package/dist/core/env.js +186 -0
  29. package/dist/core/logger.js +508 -0
  30. package/dist/core/stdin.js +38 -0
  31. package/dist/core/transport.js +231 -0
  32. package/dist/core/types.js +10 -0
  33. package/dist/index.js +17 -0
  34. package/dist/scaffold.js +512 -0
  35. package/package.json +61 -0
  36. package/types/agents/antigravity/events.d.ts +50 -0
  37. package/types/agents/antigravity/hooks.d.ts +76 -0
  38. package/types/agents/antigravity/index.d.ts +27 -0
  39. package/types/agents/antigravity/outputs.d.ts +107 -0
  40. package/types/agents/antigravity/transport.d.ts +47 -0
  41. package/types/agents/antigravity/types.d.ts +58 -0
  42. package/types/agents/claude-code/env.d.ts +27 -0
  43. package/types/agents/claude-code/events.d.ts +69 -0
  44. package/types/agents/claude-code/hooks.d.ts +1182 -0
  45. package/types/agents/claude-code/index.d.ts +23 -0
  46. package/types/agents/claude-code/outputs.d.ts +1160 -0
  47. package/types/agents/claude-code/tool-helpers.d.ts +807 -0
  48. package/types/agents/claude-code/transport.d.ts +119 -0
  49. package/types/agents/claude-code/types.d.ts +557 -0
  50. package/types/agents/codex/cli-support.d.ts +105 -0
  51. package/types/agents/codex/constants.d.ts +10 -0
  52. package/types/agents/codex/events.d.ts +63 -0
  53. package/types/agents/codex/hooks.d.ts +135 -0
  54. package/types/agents/codex/index.d.ts +19 -0
  55. package/types/agents/codex/outputs.d.ts +190 -0
  56. package/types/agents/codex/scaffold.d.ts +14 -0
  57. package/types/agents/codex/transport.d.ts +74 -0
  58. package/types/agents/codex/types.d.ts +131 -0
  59. package/types/cli.d.ts +315 -0
  60. package/types/constants.d.ts +9 -0
  61. package/types/core/define-hook.d.ts +39 -0
  62. package/types/core/env.d.ts +150 -0
  63. package/types/core/logger.d.ts +476 -0
  64. package/types/core/stdin.d.ts +21 -0
  65. package/types/core/transport.d.ts +131 -0
  66. package/types/core/types.d.ts +137 -0
  67. package/types/index.d.ts +18 -0
  68. package/types/scaffold.d.ts +46 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Wire transport for Codex hooks, rewritten against the core
3
+ * `drive()`/`Transport` interface; the wire semantics carry over
4
+ * `codex-hooks/src/runtime.ts` verbatim:
5
+ *
6
+ * - **BlockError → its reason written to stderr + exit 2, regardless of
7
+ * policy** — a single translation for every event including
8
+ * `SubagentStart`; there is deliberately no per-event table here (Codex
9
+ * has no narrower native deny shape than the stderr channel). The source
10
+ * runtime already classified `BlockError` ahead of the policy branch; that
11
+ * precedence now lives in `drive()` via {@link BlockError} extending the
12
+ * core `HookBlockError`.
13
+ * - every other unexpected failure (stdin read, parse, handler, serialize,
14
+ * write, cleanup) under the default `"error"` policy → stacktrace on
15
+ * stderr + exit 1 (`ERROR`) — note Codex exits **1**, unlike Claude Code's
16
+ * handler-error exit 2; under `"continue"` → reported and swallowed to the
17
+ * event's valid empty output `{}` at exit 0. Malformed stdin is therefore
18
+ * NOT unconditionally fail-open on Codex — the policy decides, exactly as
19
+ * in the source runtime.
20
+ * - success → `JSON.stringify(output.stdout)` at exit 0; a `null`/`undefined`
21
+ * result serializes as `{}`, valid for every event's output schema.
22
+ * - a returned `stderr` **field** on an output object is carried by
23
+ * `convertToHookOutput` but discarded by the success path — the source
24
+ * runtime only ever serialized `output.stdout`; blocking is expressed
25
+ * exclusively through the thrown-`BlockError` channel.
26
+ *
27
+ * String returns (`"Loaded from text"`) are normalized into the event's
28
+ * additionalContext output for the three text-output events; any other event
29
+ * rejecting plain text throws exactly like the source, flowing through the
30
+ * standard policy-gated error path.
31
+ * @module
32
+ * @example
33
+ * ```typescript
34
+ * // In a compiled hook file
35
+ * import { execute } from './transport.js';
36
+ * import myHook from './my-hook.js';
37
+ *
38
+ * execute(myHook);
39
+ * ```
40
+ */
41
+ import { drive } from "../../core/transport.js";
42
+ import { EVENTS_WITH_TEXT_OUTPUT } from "./constants.js";
43
+ import { BlockError, EXIT_CODES, sessionStartOutput, subagentStartOutput, userPromptSubmitOutput, } from "./outputs.js";
44
+ /**
45
+ * Converts a SpecificHookOutput to the wire shape by stripping the `_type`
46
+ * discriminator (source parity: builders produce wire-format directly).
47
+ */
48
+ export function convertToHookOutput(output) {
49
+ return output.stderr !== undefined ? { stdout: output.stdout, stderr: output.stderr } : { stdout: output.stdout };
50
+ }
51
+ function formatErrorText(error) {
52
+ return error instanceof Error ? `${error.stack ?? error.message}\n` : `${String(error)}\n`;
53
+ }
54
+ /**
55
+ * Normalizes a plain-string handler return into the event's additionalContext
56
+ * output; non-text events reject strings with the source package's exact
57
+ * error, which then flows through the standard policy-gated error path.
58
+ */
59
+ export function normalizeStringOutput(hookEventName, result) {
60
+ if (!EVENTS_WITH_TEXT_OUTPUT.has(hookEventName)) {
61
+ throw new Error(`${hookEventName} hooks cannot return plain text`);
62
+ }
63
+ if (hookEventName === "SessionStart") {
64
+ return sessionStartOutput({ additionalContext: result });
65
+ }
66
+ if (hookEventName === "SubagentStart") {
67
+ return subagentStartOutput({ additionalContext: result });
68
+ }
69
+ return userPromptSubmitOutput({ additionalContext: result });
70
+ }
71
+ // ============================================================================
72
+ // Transport
73
+ // ============================================================================
74
+ /**
75
+ * Builds the Codex wire transport. Unlike the Claude Code adapter there is no
76
+ * per-event block-shape table: every caught {@link BlockError} translates to
77
+ * `stderr: reason + "\n"` at exit 2 under every policy value, and no
78
+ * `rawStdout` predicate exists because Codex has no plain-text-stdout
79
+ * protocol events (the `rawOutput()` builder is ordinary JSON passthrough).
80
+ */
81
+ export function createCodexTransport() {
82
+ return {
83
+ finalize(outcome) {
84
+ switch (outcome.kind) {
85
+ case "response":
86
+ case "rawStdout": {
87
+ // The rawStdout outcome cannot occur for Codex (no plain-text
88
+ // protocol events, no rawStdout predicate) but the union demands
89
+ // handling; it serializes as the empty response.
90
+ const stdoutJson = outcome.kind === "response" && outcome.output !== null && outcome.output !== undefined
91
+ ? JSON.stringify(convertToHookOutput(outcome.output).stdout)
92
+ : "{}";
93
+ return { stdout: stdoutJson, exitCode: EXIT_CODES.SUCCESS };
94
+ }
95
+ case "block": {
96
+ const reason = outcome.error instanceof BlockError ? outcome.error.reason : outcome.error.message;
97
+ return { stderr: `${reason}\n`, exitCode: EXIT_CODES.BLOCK };
98
+ }
99
+ case "handlerError": {
100
+ // Codex surfaces EVERY unexpected failure — stdin read/parse
101
+ // included — through one policy gate exiting ERROR(1) on the
102
+ // default policy; malformed stdin is not unconditionally fail-open
103
+ // here (that is Claude Code's rule, not Codex's).
104
+ return { stderr: formatErrorText(outcome.error), exitCode: EXIT_CODES.ERROR };
105
+ }
106
+ }
107
+ },
108
+ };
109
+ }
110
+ // ============================================================================
111
+ // Execute
112
+ // ============================================================================
113
+ /**
114
+ * Executes a hook handler with full runtime orchestration (delegates to the
115
+ * shared core driver with this agent's transport). String returns are
116
+ * normalized before the driver sees them so that a plain-text violation
117
+ * classifies through the same policy gate as any other handler failure,
118
+ * matching the source runtime's observable wire behaviour.
119
+ */
120
+ export async function execute(hookFn) {
121
+ const eventName = hookFn.hookEventName;
122
+ const composed = ((input, context) => {
123
+ const result = hookFn(input, context);
124
+ const normalize = (value) => {
125
+ if (typeof value === "string") {
126
+ return normalizeStringOutput(eventName, value);
127
+ }
128
+ return value;
129
+ };
130
+ return result instanceof Promise ? result.then(normalize) : normalize(result);
131
+ });
132
+ composed.eventName = hookFn.eventName ?? eventName;
133
+ composed.matcher = hookFn.matcher;
134
+ composed.timeout = hookFn.timeout;
135
+ composed.unexpectedError = hookFn.unexpectedError;
136
+ composed.onUnexpectedError = hookFn.onUnexpectedError;
137
+ composed.createContext = hookFn.createContext;
138
+ await drive(createCodexTransport(), composed);
139
+ }
@@ -0,0 +1 @@
1
+ export {};