@agimon-ai/doompi-hook 0.0.1-alpha.2

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 (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/adapters/hookDocuments.cjs +2 -0
  5. package/dist/adapters/hookDocuments.cjs.map +1 -0
  6. package/dist/adapters/hookDocuments.d.cts +23 -0
  7. package/dist/adapters/hookDocuments.d.cts.map +1 -0
  8. package/dist/adapters/hookDocuments.d.mts +23 -0
  9. package/dist/adapters/hookDocuments.d.mts.map +1 -0
  10. package/dist/adapters/hookDocuments.mjs +2 -0
  11. package/dist/adapters/hookDocuments.mjs.map +1 -0
  12. package/dist/adapters/hookRunner.cjs +2 -0
  13. package/dist/adapters/hookRunner.cjs.map +1 -0
  14. package/dist/adapters/hookRunner.d.cts +22 -0
  15. package/dist/adapters/hookRunner.d.cts.map +1 -0
  16. package/dist/adapters/hookRunner.d.mts +22 -0
  17. package/dist/adapters/hookRunner.d.mts.map +1 -0
  18. package/dist/adapters/hookRunner.mjs +2 -0
  19. package/dist/adapters/hookRunner.mjs.map +1 -0
  20. package/dist/adapters/pi/extension.cjs +2 -0
  21. package/dist/adapters/pi/extension.cjs.map +1 -0
  22. package/dist/adapters/pi/extension.d.cts +21 -0
  23. package/dist/adapters/pi/extension.d.cts.map +1 -0
  24. package/dist/adapters/pi/extension.d.mts +21 -0
  25. package/dist/adapters/pi/extension.d.mts.map +1 -0
  26. package/dist/adapters/pi/extension.mjs +2 -0
  27. package/dist/adapters/pi/extension.mjs.map +1 -0
  28. package/dist/adapters/pi/hookHandlers.cjs +8 -0
  29. package/dist/adapters/pi/hookHandlers.cjs.map +1 -0
  30. package/dist/adapters/pi/hookHandlers.mjs +8 -0
  31. package/dist/adapters/pi/hookHandlers.mjs.map +1 -0
  32. package/dist/adapters/telemetry/logSinkTelemetry.cjs +2 -0
  33. package/dist/adapters/telemetry/logSinkTelemetry.cjs.map +1 -0
  34. package/dist/adapters/telemetry/logSinkTelemetry.d.cts +14 -0
  35. package/dist/adapters/telemetry/logSinkTelemetry.d.cts.map +1 -0
  36. package/dist/adapters/telemetry/logSinkTelemetry.d.mts +14 -0
  37. package/dist/adapters/telemetry/logSinkTelemetry.d.mts.map +1 -0
  38. package/dist/adapters/telemetry/logSinkTelemetry.mjs +2 -0
  39. package/dist/adapters/telemetry/logSinkTelemetry.mjs.map +1 -0
  40. package/dist/extensions/pi.cjs +1 -0
  41. package/dist/extensions/pi.d.cts +2 -0
  42. package/dist/extensions/pi.d.mts +2 -0
  43. package/dist/extensions/pi.mjs +1 -0
  44. package/dist/index.cjs +1 -0
  45. package/dist/index.d.cts +12 -0
  46. package/dist/index.d.mts +12 -0
  47. package/dist/index.mjs +1 -0
  48. package/dist/services/hookDecisions.cjs +3 -0
  49. package/dist/services/hookDecisions.cjs.map +1 -0
  50. package/dist/services/hookDecisions.d.cts +19 -0
  51. package/dist/services/hookDecisions.d.cts.map +1 -0
  52. package/dist/services/hookDecisions.d.mts +19 -0
  53. package/dist/services/hookDecisions.d.mts.map +1 -0
  54. package/dist/services/hookDecisions.mjs +3 -0
  55. package/dist/services/hookDecisions.mjs.map +1 -0
  56. package/dist/services/hookPayload.cjs +2 -0
  57. package/dist/services/hookPayload.cjs.map +1 -0
  58. package/dist/services/hookPayload.d.cts +9 -0
  59. package/dist/services/hookPayload.d.cts.map +1 -0
  60. package/dist/services/hookPayload.d.mts +9 -0
  61. package/dist/services/hookPayload.d.mts.map +1 -0
  62. package/dist/services/hookPayload.mjs +2 -0
  63. package/dist/services/hookPayload.mjs.map +1 -0
  64. package/dist/services/hookRegistry.cjs +2 -0
  65. package/dist/services/hookRegistry.cjs.map +1 -0
  66. package/dist/services/hookRegistry.d.cts +37 -0
  67. package/dist/services/hookRegistry.d.cts.map +1 -0
  68. package/dist/services/hookRegistry.d.mts +37 -0
  69. package/dist/services/hookRegistry.d.mts.map +1 -0
  70. package/dist/services/hookRegistry.mjs +2 -0
  71. package/dist/services/hookRegistry.mjs.map +1 -0
  72. package/dist/services/pluginHooks.cjs +2 -0
  73. package/dist/services/pluginHooks.cjs.map +1 -0
  74. package/dist/services/pluginHooks.d.cts +13 -0
  75. package/dist/services/pluginHooks.d.cts.map +1 -0
  76. package/dist/services/pluginHooks.d.mts +13 -0
  77. package/dist/services/pluginHooks.d.mts.map +1 -0
  78. package/dist/services/pluginHooks.mjs +2 -0
  79. package/dist/services/pluginHooks.mjs.map +1 -0
  80. package/dist/services/toolNames.cjs +2 -0
  81. package/dist/services/toolNames.cjs.map +1 -0
  82. package/dist/services/toolNames.d.cts +8 -0
  83. package/dist/services/toolNames.d.cts.map +1 -0
  84. package/dist/services/toolNames.d.mts +8 -0
  85. package/dist/services/toolNames.d.mts.map +1 -0
  86. package/dist/services/toolNames.mjs +2 -0
  87. package/dist/services/toolNames.mjs.map +1 -0
  88. package/dist/types/hooks.cjs +2 -0
  89. package/dist/types/hooks.cjs.map +1 -0
  90. package/dist/types/hooks.d.cts +145 -0
  91. package/dist/types/hooks.d.cts.map +1 -0
  92. package/dist/types/hooks.d.mts +145 -0
  93. package/dist/types/hooks.d.mts.map +1 -0
  94. package/dist/types/hooks.mjs +2 -0
  95. package/dist/types/hooks.mjs.map +1 -0
  96. package/dist/types/telemetry.cjs +2 -0
  97. package/dist/types/telemetry.cjs.map +1 -0
  98. package/dist/types/telemetry.d.cts +20 -0
  99. package/dist/types/telemetry.d.cts.map +1 -0
  100. package/dist/types/telemetry.d.mts +20 -0
  101. package/dist/types/telemetry.d.mts.map +1 -0
  102. package/dist/types/telemetry.mjs +2 -0
  103. package/dist/types/telemetry.mjs.map +1 -0
  104. package/llms.txt +7 -0
  105. package/package.json +87 -0
@@ -0,0 +1,2 @@
1
+ import hookExtension, { HookExtensionOptions } from "../adapters/pi/extension.mjs";
2
+ export { type HookExtensionOptions, hookExtension as default, hookExtension };
@@ -0,0 +1 @@
1
+ import e from"../adapters/pi/extension.mjs";export{e as default,e as hookExtension};
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./types/telemetry.cjs"),t=require("./services/toolNames.cjs"),n=require("./services/hookRegistry.cjs"),r=require("./adapters/hookDocuments.cjs"),i=require("./adapters/hookRunner.cjs"),a=require("./adapters/telemetry/logSinkTelemetry.cjs"),o=require("./services/hookDecisions.cjs"),s=require("./services/hookPayload.cjs"),c=require("./services/pluginHooks.cjs"),l=require("./types/hooks.cjs"),u=require("./adapters/pi/extension.cjs");exports.HOOK_EVENT=l.HOOK_EVENT,exports.HOOK_TELEMETRY_EVENT=e.HOOK_TELEMETRY_EVENT,exports.additionalContextsFrom=o.additionalContextsFrom,exports.createBashHookRunner=i.createBashHookRunner,exports.createHookDocumentReader=r.createHookDocumentReader,exports.createHookTelemetry=a.createHookTelemetry,exports.decisionReason=o.decisionReason,exports.decisionsFrom=o.decisionsFrom,exports.failuresFrom=o.failuresFrom,exports.hookExtension=u.default,exports.hookFailureMessage=o.hookFailureMessage,exports.isDenied=o.isDenied,exports.matchesTool=t.matchesTool,exports.registryCacheKey=n.registryCacheKey,exports.registryEntries=n.registryEntries,exports.selectPluginHooks=c.selectPluginHooks,exports.selectRegistryHooks=n.selectRegistryHooks,exports.sessionHookPayload=s.sessionHookPayload,exports.toClaudeToolName=t.toClaudeToolName,exports.toolHookPayload=s.toolHookPayload,exports.toolResultMessages=o.toolResultMessages;
@@ -0,0 +1,12 @@
1
+ import { HOOK_EVENT, HookCommand, HookDecision, HookDocumentReader, HookDocumentSource, HookEventName, HookFailure, HookFailureReason, HookOutcome, HookPayload, HookRunOptions, HookRunner, HookToolEvent, ParsedRegistrySource, PluginDocumentRead, PluginHookConfig, PluginHookDocument, PluginHookGroup, PluginHookSourceRef, RegistryBinding, RegistryDocument, RegistryEntry, RegistryGroup, RegistryRead, ResolvedHook } from "./types/hooks.cjs";
2
+ import { HOOK_TELEMETRY_EVENT, HookTelemetry, HookTelemetryAttributes, HookTelemetryEventName } from "./types/telemetry.cjs";
3
+ import hookExtension, { HookExtensionOptions } from "./adapters/pi/extension.cjs";
4
+ import { HookDocumentReaderOptions, createHookDocumentReader } from "./adapters/hookDocuments.cjs";
5
+ import { BashHookRunnerOptions, createBashHookRunner } from "./adapters/hookRunner.cjs";
6
+ import { HookTelemetryOptions, createHookTelemetry } from "./adapters/telemetry/logSinkTelemetry.cjs";
7
+ import { additionalContextsFrom, decisionReason, decisionsFrom, failuresFrom, hookFailureMessage, isDenied, toolResultMessages } from "./services/hookDecisions.cjs";
8
+ import { sessionHookPayload, toolHookPayload } from "./services/hookPayload.cjs";
9
+ import { RegistrySelection, registryCacheKey, registryEntries, selectRegistryHooks } from "./services/hookRegistry.cjs";
10
+ import { selectPluginHooks } from "./services/pluginHooks.cjs";
11
+ import { matchesTool, toClaudeToolName } from "./services/toolNames.cjs";
12
+ export { type BashHookRunnerOptions, HOOK_EVENT, HOOK_TELEMETRY_EVENT, type HookCommand, type HookDecision, type HookDocumentReader, type HookDocumentReaderOptions, type HookDocumentSource, type HookEventName, type HookExtensionOptions, type HookFailure, type HookFailureReason, type HookOutcome, type HookPayload, type HookRunOptions, type HookRunner, type HookTelemetry, type HookTelemetryAttributes, type HookTelemetryEventName, type HookTelemetryOptions, type HookToolEvent, type ParsedRegistrySource, type PluginDocumentRead, type PluginHookConfig, type PluginHookDocument, type PluginHookGroup, type PluginHookSourceRef, type RegistryBinding, type RegistryDocument, type RegistryEntry, type RegistryGroup, type RegistryRead, type RegistrySelection, type ResolvedHook, additionalContextsFrom, createBashHookRunner, createHookDocumentReader, createHookTelemetry, decisionReason, decisionsFrom, failuresFrom, hookExtension, hookFailureMessage, isDenied, matchesTool, registryCacheKey, registryEntries, selectPluginHooks, selectRegistryHooks, sessionHookPayload, toClaudeToolName, toolHookPayload, toolResultMessages };
@@ -0,0 +1,12 @@
1
+ import { HOOK_EVENT, HookCommand, HookDecision, HookDocumentReader, HookDocumentSource, HookEventName, HookFailure, HookFailureReason, HookOutcome, HookPayload, HookRunOptions, HookRunner, HookToolEvent, ParsedRegistrySource, PluginDocumentRead, PluginHookConfig, PluginHookDocument, PluginHookGroup, PluginHookSourceRef, RegistryBinding, RegistryDocument, RegistryEntry, RegistryGroup, RegistryRead, ResolvedHook } from "./types/hooks.mjs";
2
+ import { HOOK_TELEMETRY_EVENT, HookTelemetry, HookTelemetryAttributes, HookTelemetryEventName } from "./types/telemetry.mjs";
3
+ import hookExtension, { HookExtensionOptions } from "./adapters/pi/extension.mjs";
4
+ import { HookDocumentReaderOptions, createHookDocumentReader } from "./adapters/hookDocuments.mjs";
5
+ import { BashHookRunnerOptions, createBashHookRunner } from "./adapters/hookRunner.mjs";
6
+ import { HookTelemetryOptions, createHookTelemetry } from "./adapters/telemetry/logSinkTelemetry.mjs";
7
+ import { additionalContextsFrom, decisionReason, decisionsFrom, failuresFrom, hookFailureMessage, isDenied, toolResultMessages } from "./services/hookDecisions.mjs";
8
+ import { sessionHookPayload, toolHookPayload } from "./services/hookPayload.mjs";
9
+ import { RegistrySelection, registryCacheKey, registryEntries, selectRegistryHooks } from "./services/hookRegistry.mjs";
10
+ import { selectPluginHooks } from "./services/pluginHooks.mjs";
11
+ import { matchesTool, toClaudeToolName } from "./services/toolNames.mjs";
12
+ export { type BashHookRunnerOptions, HOOK_EVENT, HOOK_TELEMETRY_EVENT, type HookCommand, type HookDecision, type HookDocumentReader, type HookDocumentReaderOptions, type HookDocumentSource, type HookEventName, type HookExtensionOptions, type HookFailure, type HookFailureReason, type HookOutcome, type HookPayload, type HookRunOptions, type HookRunner, type HookTelemetry, type HookTelemetryAttributes, type HookTelemetryEventName, type HookTelemetryOptions, type HookToolEvent, type ParsedRegistrySource, type PluginDocumentRead, type PluginHookConfig, type PluginHookDocument, type PluginHookGroup, type PluginHookSourceRef, type RegistryBinding, type RegistryDocument, type RegistryEntry, type RegistryGroup, type RegistryRead, type RegistrySelection, type ResolvedHook, additionalContextsFrom, createBashHookRunner, createHookDocumentReader, createHookTelemetry, decisionReason, decisionsFrom, failuresFrom, hookExtension, hookFailureMessage, isDenied, matchesTool, registryCacheKey, registryEntries, selectPluginHooks, selectRegistryHooks, sessionHookPayload, toClaudeToolName, toolHookPayload, toolResultMessages };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{HOOK_TELEMETRY_EVENT as e}from"./types/telemetry.mjs";import{matchesTool as t,toClaudeToolName as n}from"./services/toolNames.mjs";import{registryCacheKey as r,registryEntries as i,selectRegistryHooks as a}from"./services/hookRegistry.mjs";import{createHookDocumentReader as o}from"./adapters/hookDocuments.mjs";import{createBashHookRunner as s}from"./adapters/hookRunner.mjs";import{createHookTelemetry as c}from"./adapters/telemetry/logSinkTelemetry.mjs";import{additionalContextsFrom as l,decisionReason as u,decisionsFrom as d,failuresFrom as f,hookFailureMessage as p,isDenied as m,toolResultMessages as h}from"./services/hookDecisions.mjs";import{sessionHookPayload as g,toolHookPayload as _}from"./services/hookPayload.mjs";import{selectPluginHooks as v}from"./services/pluginHooks.mjs";import{HOOK_EVENT as y}from"./types/hooks.mjs";import b from"./adapters/pi/extension.mjs";export{y as HOOK_EVENT,e as HOOK_TELEMETRY_EVENT,l as additionalContextsFrom,s as createBashHookRunner,o as createHookDocumentReader,c as createHookTelemetry,u as decisionReason,d as decisionsFrom,f as failuresFrom,b as hookExtension,p as hookFailureMessage,m as isDenied,t as matchesTool,r as registryCacheKey,i as registryEntries,v as selectPluginHooks,a as selectRegistryHooks,g as sessionHookPayload,n as toClaudeToolName,_ as toolHookPayload,h as toolResultMessages};
@@ -0,0 +1,3 @@
1
+ function e(e){return e.flatMap(e=>e.decision?[e.decision]:[])}function t(e){return e.flatMap(e=>e.failure?[e.failure]:[])}function n(e){return e?.decision===`block`||e?.hookSpecificOutput?.permissionDecision===`deny`}function r(e){return e?.reason??e?.hookSpecificOutput?.reason}function i(e){return e.map(e=>e.hookSpecificOutput?.additionalContext).filter(e=>!!e)}function a(e){return e.map(e=>e.hookSpecificOutput?.additionalContext??r(e)).filter(e=>!!e)}function o(e){return[`One or more repository hooks did not complete, so their checks were not applied:`,...e.map(e=>`- ${e.command}: ${e.message}`),`Options:`,`- Inspect the hook command or configuration and rerun after correcting it.`,`- Continue only if the missed advisory check is acceptable.`,`- Ask the user before bypassing a hook that may enforce a guardrail.`].join(`
2
+ `)}exports.additionalContextsFrom=i,exports.decisionReason=r,exports.decisionsFrom=e,exports.failuresFrom=t,exports.hookFailureMessage=o,exports.isDenied=n,exports.toolResultMessages=a;
3
+ //# sourceMappingURL=hookDecisions.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookDecisions.cjs","names":[],"sources":["../../src/services/hookDecisions.ts"],"sourcesContent":["import type { HookDecision, HookFailure, HookOutcome } from '../types/hooks.ts';\n\nconst BLOCK_DECISION = 'block';\nconst DENY_PERMISSION = 'deny';\n\nexport function decisionsFrom(outcomes: ReadonlyArray<HookOutcome>): HookDecision[] {\n return outcomes.flatMap((outcome) => (outcome.decision ? [outcome.decision] : []));\n}\n\nexport function failuresFrom(outcomes: ReadonlyArray<HookOutcome>): HookFailure[] {\n return outcomes.flatMap((outcome) => (outcome.failure ? [outcome.failure] : []));\n}\n\n/** Both spellings a hook may use to refuse the call it observed. */\nexport function isDenied(decision: HookDecision | undefined): boolean {\n return decision?.decision === BLOCK_DECISION || decision?.hookSpecificOutput?.permissionDecision === DENY_PERMISSION;\n}\n\nexport function decisionReason(decision: HookDecision | undefined): string | undefined {\n return decision?.reason ?? decision?.hookSpecificOutput?.reason;\n}\n\n/** Context a hook wants added to the conversation rather than used to block. */\nexport function additionalContextsFrom(decisions: ReadonlyArray<HookDecision>): string[] {\n return decisions\n .map((decision) => decision.hookSpecificOutput?.additionalContext)\n .filter((value): value is string => Boolean(value));\n}\n\n/** What a post-tool hook has to say, whichever field it said it in. */\nexport function toolResultMessages(decisions: ReadonlyArray<HookDecision>): string[] {\n return decisions\n .map((decision) => decision.hookSpecificOutput?.additionalContext ?? decisionReason(decision))\n .filter((value): value is string => Boolean(value));\n}\n\n/**\n * A guardrail that never ran looks exactly like one that passed, so the agent is\n * told which checks were missed and what its options are.\n */\nexport function hookFailureMessage(failures: ReadonlyArray<HookFailure>): string {\n return [\n 'One or more repository hooks did not complete, so their checks were not applied:',\n ...failures.map((failure) => `- ${failure.command}: ${failure.message}`),\n 'Options:',\n '- Inspect the hook command or configuration and rerun after correcting it.',\n '- Continue only if the missed advisory check is acceptable.',\n '- Ask the user before bypassing a hook that may enforce a guardrail.',\n ].join('\\n');\n}\n"],"mappings":"AAKA,SAAgB,EAAc,EAAsD,CAClF,OAAO,EAAS,QAAS,GAAa,EAAQ,SAAW,CAAC,EAAQ,QAAQ,EAAI,CAAC,CAAE,CACnF,CAEA,SAAgB,EAAa,EAAqD,CAChF,OAAO,EAAS,QAAS,GAAa,EAAQ,QAAU,CAAC,EAAQ,OAAO,EAAI,CAAC,CAAE,CACjF,CAGA,SAAgB,EAAS,EAA6C,CACpE,OAAO,GAAU,WAAa,SAAkB,GAAU,oBAAoB,qBAAuB,MACvG,CAEA,SAAgB,EAAe,EAAwD,CACrF,OAAO,GAAU,QAAU,GAAU,oBAAoB,MAC3D,CAGA,SAAgB,EAAuB,EAAkD,CACvF,OAAO,EACJ,IAAK,GAAa,EAAS,oBAAoB,iBAAiB,CAAC,CACjE,OAAQ,GAA2B,EAAQ,CAAM,CACtD,CAGA,SAAgB,EAAmB,EAAkD,CACnF,OAAO,EACJ,IAAK,GAAa,EAAS,oBAAoB,mBAAqB,EAAe,CAAQ,CAAC,CAAC,CAC7F,OAAQ,GAA2B,EAAQ,CAAM,CACtD,CAMA,SAAgB,EAAmB,EAA8C,CAC/E,MAAO,CACL,mFACA,GAAG,EAAS,IAAK,GAAY,KAAK,EAAQ,QAAQ,IAAI,EAAQ,SAAS,EACvE,WACA,6EACA,8DACA,sEACF,CAAC,CAAC,KAAK;CAAI,CACb"}
@@ -0,0 +1,19 @@
1
+ import { HookDecision, HookFailure, HookOutcome } from "../types/hooks.cjs";
2
+ //#region src/services/hookDecisions.d.ts
3
+ declare function decisionsFrom(outcomes: ReadonlyArray<HookOutcome>): HookDecision[];
4
+ declare function failuresFrom(outcomes: ReadonlyArray<HookOutcome>): HookFailure[];
5
+ /** Both spellings a hook may use to refuse the call it observed. */
6
+ declare function isDenied(decision: HookDecision | undefined): boolean;
7
+ declare function decisionReason(decision: HookDecision | undefined): string | undefined;
8
+ /** Context a hook wants added to the conversation rather than used to block. */
9
+ declare function additionalContextsFrom(decisions: ReadonlyArray<HookDecision>): string[];
10
+ /** What a post-tool hook has to say, whichever field it said it in. */
11
+ declare function toolResultMessages(decisions: ReadonlyArray<HookDecision>): string[];
12
+ /**
13
+ * A guardrail that never ran looks exactly like one that passed, so the agent is
14
+ * told which checks were missed and what its options are.
15
+ */
16
+ declare function hookFailureMessage(failures: ReadonlyArray<HookFailure>): string;
17
+ //#endregion
18
+ export { additionalContextsFrom, decisionReason, decisionsFrom, failuresFrom, hookFailureMessage, isDenied, toolResultMessages };
19
+ //# sourceMappingURL=hookDecisions.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookDecisions.d.cts","names":[],"sources":["../../src/services/hookDecisions.ts"],"mappings":";;iBAKgB,cAAc,UAAU,cAAc,eAAe;iBAIrD,aAAa,UAAU,cAAc,eAAe;;iBAKpD,SAAS,UAAU;iBAInB,eAAe,UAAU;;iBAKzB,uBAAuB,WAAW,cAAc;;iBAOhD,mBAAmB,WAAW,cAAc;;;;;iBAU5C,mBAAmB,UAAU,cAAc"}
@@ -0,0 +1,19 @@
1
+ import { HookDecision, HookFailure, HookOutcome } from "../types/hooks.mjs";
2
+ //#region src/services/hookDecisions.d.ts
3
+ declare function decisionsFrom(outcomes: ReadonlyArray<HookOutcome>): HookDecision[];
4
+ declare function failuresFrom(outcomes: ReadonlyArray<HookOutcome>): HookFailure[];
5
+ /** Both spellings a hook may use to refuse the call it observed. */
6
+ declare function isDenied(decision: HookDecision | undefined): boolean;
7
+ declare function decisionReason(decision: HookDecision | undefined): string | undefined;
8
+ /** Context a hook wants added to the conversation rather than used to block. */
9
+ declare function additionalContextsFrom(decisions: ReadonlyArray<HookDecision>): string[];
10
+ /** What a post-tool hook has to say, whichever field it said it in. */
11
+ declare function toolResultMessages(decisions: ReadonlyArray<HookDecision>): string[];
12
+ /**
13
+ * A guardrail that never ran looks exactly like one that passed, so the agent is
14
+ * told which checks were missed and what its options are.
15
+ */
16
+ declare function hookFailureMessage(failures: ReadonlyArray<HookFailure>): string;
17
+ //#endregion
18
+ export { additionalContextsFrom, decisionReason, decisionsFrom, failuresFrom, hookFailureMessage, isDenied, toolResultMessages };
19
+ //# sourceMappingURL=hookDecisions.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookDecisions.d.mts","names":[],"sources":["../../src/services/hookDecisions.ts"],"mappings":";;iBAKgB,cAAc,UAAU,cAAc,eAAe;iBAIrD,aAAa,UAAU,cAAc,eAAe;;iBAKpD,SAAS,UAAU;iBAInB,eAAe,UAAU;;iBAKzB,uBAAuB,WAAW,cAAc;;iBAOhD,mBAAmB,WAAW,cAAc;;;;;iBAU5C,mBAAmB,UAAU,cAAc"}
@@ -0,0 +1,3 @@
1
+ function e(e){return e.flatMap(e=>e.decision?[e.decision]:[])}function t(e){return e.flatMap(e=>e.failure?[e.failure]:[])}function n(e){return e?.decision===`block`||e?.hookSpecificOutput?.permissionDecision===`deny`}function r(e){return e?.reason??e?.hookSpecificOutput?.reason}function i(e){return e.map(e=>e.hookSpecificOutput?.additionalContext).filter(e=>!!e)}function a(e){return e.map(e=>e.hookSpecificOutput?.additionalContext??r(e)).filter(e=>!!e)}function o(e){return[`One or more repository hooks did not complete, so their checks were not applied:`,...e.map(e=>`- ${e.command}: ${e.message}`),`Options:`,`- Inspect the hook command or configuration and rerun after correcting it.`,`- Continue only if the missed advisory check is acceptable.`,`- Ask the user before bypassing a hook that may enforce a guardrail.`].join(`
2
+ `)}export{i as additionalContextsFrom,r as decisionReason,e as decisionsFrom,t as failuresFrom,o as hookFailureMessage,n as isDenied,a as toolResultMessages};
3
+ //# sourceMappingURL=hookDecisions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookDecisions.mjs","names":[],"sources":["../../src/services/hookDecisions.ts"],"sourcesContent":["import type { HookDecision, HookFailure, HookOutcome } from '../types/hooks.ts';\n\nconst BLOCK_DECISION = 'block';\nconst DENY_PERMISSION = 'deny';\n\nexport function decisionsFrom(outcomes: ReadonlyArray<HookOutcome>): HookDecision[] {\n return outcomes.flatMap((outcome) => (outcome.decision ? [outcome.decision] : []));\n}\n\nexport function failuresFrom(outcomes: ReadonlyArray<HookOutcome>): HookFailure[] {\n return outcomes.flatMap((outcome) => (outcome.failure ? [outcome.failure] : []));\n}\n\n/** Both spellings a hook may use to refuse the call it observed. */\nexport function isDenied(decision: HookDecision | undefined): boolean {\n return decision?.decision === BLOCK_DECISION || decision?.hookSpecificOutput?.permissionDecision === DENY_PERMISSION;\n}\n\nexport function decisionReason(decision: HookDecision | undefined): string | undefined {\n return decision?.reason ?? decision?.hookSpecificOutput?.reason;\n}\n\n/** Context a hook wants added to the conversation rather than used to block. */\nexport function additionalContextsFrom(decisions: ReadonlyArray<HookDecision>): string[] {\n return decisions\n .map((decision) => decision.hookSpecificOutput?.additionalContext)\n .filter((value): value is string => Boolean(value));\n}\n\n/** What a post-tool hook has to say, whichever field it said it in. */\nexport function toolResultMessages(decisions: ReadonlyArray<HookDecision>): string[] {\n return decisions\n .map((decision) => decision.hookSpecificOutput?.additionalContext ?? decisionReason(decision))\n .filter((value): value is string => Boolean(value));\n}\n\n/**\n * A guardrail that never ran looks exactly like one that passed, so the agent is\n * told which checks were missed and what its options are.\n */\nexport function hookFailureMessage(failures: ReadonlyArray<HookFailure>): string {\n return [\n 'One or more repository hooks did not complete, so their checks were not applied:',\n ...failures.map((failure) => `- ${failure.command}: ${failure.message}`),\n 'Options:',\n '- Inspect the hook command or configuration and rerun after correcting it.',\n '- Continue only if the missed advisory check is acceptable.',\n '- Ask the user before bypassing a hook that may enforce a guardrail.',\n ].join('\\n');\n}\n"],"mappings":"AAKA,SAAgB,EAAc,EAAsD,CAClF,OAAO,EAAS,QAAS,GAAa,EAAQ,SAAW,CAAC,EAAQ,QAAQ,EAAI,CAAC,CAAE,CACnF,CAEA,SAAgB,EAAa,EAAqD,CAChF,OAAO,EAAS,QAAS,GAAa,EAAQ,QAAU,CAAC,EAAQ,OAAO,EAAI,CAAC,CAAE,CACjF,CAGA,SAAgB,EAAS,EAA6C,CACpE,OAAO,GAAU,WAAa,SAAkB,GAAU,oBAAoB,qBAAuB,MACvG,CAEA,SAAgB,EAAe,EAAwD,CACrF,OAAO,GAAU,QAAU,GAAU,oBAAoB,MAC3D,CAGA,SAAgB,EAAuB,EAAkD,CACvF,OAAO,EACJ,IAAK,GAAa,EAAS,oBAAoB,iBAAiB,CAAC,CACjE,OAAQ,GAA2B,EAAQ,CAAM,CACtD,CAGA,SAAgB,EAAmB,EAAkD,CACnF,OAAO,EACJ,IAAK,GAAa,EAAS,oBAAoB,mBAAqB,EAAe,CAAQ,CAAC,CAAC,CAC7F,OAAQ,GAA2B,EAAQ,CAAM,CACtD,CAMA,SAAgB,EAAmB,EAA8C,CAC/E,MAAO,CACL,mFACA,GAAG,EAAS,IAAK,GAAY,KAAK,EAAQ,QAAQ,IAAI,EAAQ,SAAS,EACvE,WACA,6EACA,8DACA,sEACF,CAAC,CAAC,KAAK;CAAI,CACb"}
@@ -0,0 +1,2 @@
1
+ const e=require("./toolNames.cjs");function t(t,n,r,i){return{session_id:i,transcript_path:``,cwd:r,hook_event_name:n,tool_name:e.toClaudeToolName(t.toolName),tool_input:t.input,...t.type===`tool_result`?{tool_response:{success:!t.isError,content:t.content}}:{}}}function n(e,t){return{session_id:e,cwd:t}}exports.sessionHookPayload=n,exports.toolHookPayload=t;
2
+ //# sourceMappingURL=hookPayload.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookPayload.cjs","names":["toClaudeToolName"],"sources":["../../src/services/hookPayload.ts"],"sourcesContent":["import type { HookPayload, HookToolEvent } from '../types/hooks.ts';\nimport { toClaudeToolName } from './toolNames.ts';\n\nconst TOOL_RESULT_EVENT = 'tool_result';\n\n/** The payload shape a Claude Code session hook is written against. */\nexport function toolHookPayload(\n event: HookToolEvent,\n hookEventName: string,\n repoRoot: string,\n sessionId: string,\n): HookPayload {\n return {\n session_id: sessionId,\n transcript_path: '',\n cwd: repoRoot,\n hook_event_name: hookEventName,\n tool_name: toClaudeToolName(event.toolName),\n tool_input: event.input,\n ...(event.type === TOOL_RESULT_EVENT ? { tool_response: { success: !event.isError, content: event.content } } : {}),\n };\n}\n\n/** Session lifecycle hooks observe no tool, so they only need where and who. */\nexport function sessionHookPayload(sessionId: string, repoRoot: string): HookPayload {\n return { session_id: sessionId, cwd: repoRoot };\n}\n"],"mappings":"mCAMA,SAAgB,EACd,EACA,EACA,EACA,EACa,CACb,MAAO,CACL,WAAY,EACZ,gBAAiB,GACjB,IAAK,EACL,gBAAiB,EACjB,UAAWA,EAAAA,iBAAiB,EAAM,QAAQ,EAC1C,WAAY,EAAM,MAClB,GAAI,EAAM,OAAS,cAAoB,CAAE,cAAe,CAAE,QAAS,CAAC,EAAM,QAAS,QAAS,EAAM,OAAQ,CAAE,EAAI,CAAC,CACnH,CACF,CAGA,SAAgB,EAAmB,EAAmB,EAA+B,CACnF,MAAO,CAAE,WAAY,EAAW,IAAK,CAAS,CAChD"}
@@ -0,0 +1,9 @@
1
+ import { HookPayload, HookToolEvent } from "../types/hooks.cjs";
2
+ //#region src/services/hookPayload.d.ts
3
+ /** The payload shape a Claude Code session hook is written against. */
4
+ declare function toolHookPayload(event: HookToolEvent, hookEventName: string, repoRoot: string, sessionId: string): HookPayload;
5
+ /** Session lifecycle hooks observe no tool, so they only need where and who. */
6
+ declare function sessionHookPayload(sessionId: string, repoRoot: string): HookPayload;
7
+ //#endregion
8
+ export { sessionHookPayload, toolHookPayload };
9
+ //# sourceMappingURL=hookPayload.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookPayload.d.cts","names":[],"sources":["../../src/services/hookPayload.ts"],"mappings":";;;iBAMgB,gBACd,OAAO,eACP,uBACA,kBACA,oBACC;;iBAaa,mBAAmB,mBAAmB,mBAAmB"}
@@ -0,0 +1,9 @@
1
+ import { HookPayload, HookToolEvent } from "../types/hooks.mjs";
2
+ //#region src/services/hookPayload.d.ts
3
+ /** The payload shape a Claude Code session hook is written against. */
4
+ declare function toolHookPayload(event: HookToolEvent, hookEventName: string, repoRoot: string, sessionId: string): HookPayload;
5
+ /** Session lifecycle hooks observe no tool, so they only need where and who. */
6
+ declare function sessionHookPayload(sessionId: string, repoRoot: string): HookPayload;
7
+ //#endregion
8
+ export { sessionHookPayload, toolHookPayload };
9
+ //# sourceMappingURL=hookPayload.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookPayload.d.mts","names":[],"sources":["../../src/services/hookPayload.ts"],"mappings":";;;iBAMgB,gBACd,OAAO,eACP,uBACA,kBACA,oBACC;;iBAaa,mBAAmB,mBAAmB,mBAAmB"}
@@ -0,0 +1,2 @@
1
+ import{toClaudeToolName as e}from"./toolNames.mjs";function t(t,n,r,i){return{session_id:i,transcript_path:``,cwd:r,hook_event_name:n,tool_name:e(t.toolName),tool_input:t.input,...t.type===`tool_result`?{tool_response:{success:!t.isError,content:t.content}}:{}}}function n(e,t){return{session_id:e,cwd:t}}export{n as sessionHookPayload,t as toolHookPayload};
2
+ //# sourceMappingURL=hookPayload.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookPayload.mjs","names":[],"sources":["../../src/services/hookPayload.ts"],"sourcesContent":["import type { HookPayload, HookToolEvent } from '../types/hooks.ts';\nimport { toClaudeToolName } from './toolNames.ts';\n\nconst TOOL_RESULT_EVENT = 'tool_result';\n\n/** The payload shape a Claude Code session hook is written against. */\nexport function toolHookPayload(\n event: HookToolEvent,\n hookEventName: string,\n repoRoot: string,\n sessionId: string,\n): HookPayload {\n return {\n session_id: sessionId,\n transcript_path: '',\n cwd: repoRoot,\n hook_event_name: hookEventName,\n tool_name: toClaudeToolName(event.toolName),\n tool_input: event.input,\n ...(event.type === TOOL_RESULT_EVENT ? { tool_response: { success: !event.isError, content: event.content } } : {}),\n };\n}\n\n/** Session lifecycle hooks observe no tool, so they only need where and who. */\nexport function sessionHookPayload(sessionId: string, repoRoot: string): HookPayload {\n return { session_id: sessionId, cwd: repoRoot };\n}\n"],"mappings":"mDAMA,SAAgB,EACd,EACA,EACA,EACA,EACa,CACb,MAAO,CACL,WAAY,EACZ,gBAAiB,GACjB,IAAK,EACL,gBAAiB,EACjB,UAAW,EAAiB,EAAM,QAAQ,EAC1C,WAAY,EAAM,MAClB,GAAI,EAAM,OAAS,cAAoB,CAAE,cAAe,CAAE,QAAS,CAAC,EAAM,QAAS,QAAS,EAAM,OAAQ,CAAE,EAAI,CAAC,CACnH,CACF,CAGA,SAAgB,EAAmB,EAAmB,EAA+B,CACnF,MAAO,CAAE,WAAY,EAAW,IAAK,CAAS,CAChD"}
@@ -0,0 +1,2 @@
1
+ const e=require("./toolNames.cjs");function t(e){return e.map(e=>`${e.baseDirectory} ${e.text}`).join(` `)}function n(e){let t=new Map,n=0;for(let r of e)for(let[e,i]of Object.entries(r.document.groups??{})){let a=[];for(let t of i.hooks??[])n+=1,t.pi&&a.push({...t.pi,event:t.event,order:t.pi.order??0,position:n,groupId:e,core:i.core===!0,baseDirectory:r.baseDirectory});t.set(e,a)}let r=[...t.values()].flat();return r.sort((e,t)=>e.order-t.order||e.position-t.position),r}function r(t,n){let r=n.allowedGroups===void 0?void 0:new Set(n.allowedGroups);return t.filter(e=>e.core||!r||r.has(e.groupId)).filter(e=>e.event===n.event).filter(e=>!(e.skipInSubagent&&n.inSubagent)).filter(t=>e.matchesTool(t.matcher,n.toolName)).map(e=>({hook:{command:e.command,timeout:e.timeout},root:e.baseDirectory}))}exports.registryCacheKey=t,exports.registryEntries=n,exports.selectRegistryHooks=r;
2
+ //# sourceMappingURL=hookRegistry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookRegistry.cjs","names":["matchesTool"],"sources":["../../src/services/hookRegistry.ts"],"sourcesContent":["import type { HookDocumentSource, ParsedRegistrySource, RegistryEntry, ResolvedHook } from '../types/hooks.ts';\nimport { matchesTool } from './toolNames.ts';\n\n/** Which rows a dispatch keeps, once the session's selection is known. */\nexport interface RegistrySelection {\n event: string;\n toolName?: string;\n /** Selected group ids, or undefined for \"every group\", the standalone default. */\n allowedGroups?: readonly string[];\n inSubagent: boolean;\n}\n\n/**\n * Identity of a set of registry sources, for callers that cache the parse.\n *\n * Keyed on contents rather than mtime, so it stays correct for an in-place\n * rewrite and needs no invalidation hook. Reading both files is cheap; parsing\n * is what a cache built on this skips.\n */\nexport function registryCacheKey(sources: ReadonlyArray<HookDocumentSource>): string {\n return sources.map((source) => `${source.baseDirectory} ${source.text}`).join(' ');\n}\n\n/**\n * Flattens parsed registry documents into sorted rows, keeping only bindings\n * that declare a `pi` frontend.\n *\n * A later source replaces the group of the same id outright rather than merging\n * into it, so a repository that redefines a group owns it completely.\n */\nexport function registryEntries(sources: ReadonlyArray<ParsedRegistrySource>): RegistryEntry[] {\n const groups = new Map<string, RegistryEntry[]>();\n let position = 0;\n for (const source of sources) {\n for (const [groupId, group] of Object.entries(source.document.groups ?? {})) {\n const entries: RegistryEntry[] = [];\n for (const hook of group.hooks ?? []) {\n // Every declared hook advances the position, whether or not it is kept,\n // so the sort tiebreaker stays the order the file declares them in.\n position += 1;\n if (!hook.pi) continue;\n entries.push({\n ...hook.pi,\n event: hook.event,\n order: hook.pi.order ?? 0,\n position,\n groupId,\n core: group.core === true,\n baseDirectory: source.baseDirectory,\n });\n }\n groups.set(groupId, entries);\n }\n }\n\n const entries = [...groups.values()].flat();\n entries.sort((left, right) => left.order - right.order || left.position - right.position);\n return entries;\n}\n\n/**\n * The rows one dispatch runs.\n *\n * Group selection changes when /mode switches mid-session, so inclusion is\n * applied per call rather than being baked into a cached parse. Core groups\n * always load; the rest are gated by the layers the harness resolved.\n */\nexport function selectRegistryHooks(\n entries: ReadonlyArray<RegistryEntry>,\n selection: RegistrySelection,\n): ResolvedHook[] {\n const allowed = selection.allowedGroups === undefined ? undefined : new Set(selection.allowedGroups);\n return entries\n .filter((entry) => entry.core || !allowed || allowed.has(entry.groupId))\n .filter((entry) => entry.event === selection.event)\n .filter((entry) => !(entry.skipInSubagent && selection.inSubagent))\n .filter((entry) => matchesTool(entry.matcher, selection.toolName))\n .map((entry) => ({\n hook: { command: entry.command, timeout: entry.timeout },\n // The declaring config's root, so a global hook can reach its own scripts\n // through CLAUDE_PLUGIN_ROOT while still running against this repository.\n root: entry.baseDirectory,\n }));\n}\n"],"mappings":"mCAmBA,SAAgB,EAAiB,EAAoD,CACnF,OAAO,EAAQ,IAAK,GAAW,GAAG,EAAO,cAAc,GAAG,EAAO,MAAM,CAAC,CAAC,KAAK,GAAG,CACnF,CASA,SAAgB,EAAgB,EAA+D,CAC7F,IAAM,EAAS,IAAI,IACf,EAAW,EACf,IAAK,IAAM,KAAU,EACnB,IAAK,GAAM,CAAC,EAAS,KAAU,OAAO,QAAQ,EAAO,SAAS,QAAU,CAAC,CAAC,EAAG,CAC3E,IAAM,EAA2B,CAAC,EAClC,IAAK,IAAM,KAAQ,EAAM,OAAS,CAAC,EAGjC,GAAY,EACP,EAAK,IACV,EAAQ,KAAK,CACX,GAAG,EAAK,GACR,MAAO,EAAK,MACZ,MAAO,EAAK,GAAG,OAAS,EACxB,WACA,UACA,KAAM,EAAM,OAAS,GACrB,cAAe,EAAO,aACxB,CAAC,EAEH,EAAO,IAAI,EAAS,CAAO,CAC7B,CAGF,IAAM,EAAU,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,KAAK,EAE1C,OADA,EAAQ,MAAM,EAAM,IAAU,EAAK,MAAQ,EAAM,OAAS,EAAK,SAAW,EAAM,QAAQ,EACjF,CACT,CASA,SAAgB,EACd,EACA,EACgB,CAChB,IAAM,EAAU,EAAU,gBAAkB,IAAA,GAAY,IAAA,GAAY,IAAI,IAAI,EAAU,aAAa,EACnG,OAAO,EACJ,OAAQ,GAAU,EAAM,MAAQ,CAAC,GAAW,EAAQ,IAAI,EAAM,OAAO,CAAC,CAAC,CACvE,OAAQ,GAAU,EAAM,QAAU,EAAU,KAAK,CAAC,CAClD,OAAQ,GAAU,EAAE,EAAM,gBAAkB,EAAU,WAAW,CAAC,CAClE,OAAQ,GAAUA,EAAAA,YAAY,EAAM,QAAS,EAAU,QAAQ,CAAC,CAAC,CACjE,IAAK,IAAW,CACf,KAAM,CAAE,QAAS,EAAM,QAAS,QAAS,EAAM,OAAQ,EAGvD,KAAM,EAAM,aACd,EAAE,CACN"}
@@ -0,0 +1,37 @@
1
+ import { HookDocumentSource, ParsedRegistrySource, RegistryEntry, ResolvedHook } from "../types/hooks.cjs";
2
+ //#region src/services/hookRegistry.d.ts
3
+ /** Which rows a dispatch keeps, once the session's selection is known. */
4
+ interface RegistrySelection {
5
+ event: string;
6
+ toolName?: string;
7
+ /** Selected group ids, or undefined for "every group", the standalone default. */
8
+ allowedGroups?: readonly string[];
9
+ inSubagent: boolean;
10
+ }
11
+ /**
12
+ * Identity of a set of registry sources, for callers that cache the parse.
13
+ *
14
+ * Keyed on contents rather than mtime, so it stays correct for an in-place
15
+ * rewrite and needs no invalidation hook. Reading both files is cheap; parsing
16
+ * is what a cache built on this skips.
17
+ */
18
+ declare function registryCacheKey(sources: ReadonlyArray<HookDocumentSource>): string;
19
+ /**
20
+ * Flattens parsed registry documents into sorted rows, keeping only bindings
21
+ * that declare a `pi` frontend.
22
+ *
23
+ * A later source replaces the group of the same id outright rather than merging
24
+ * into it, so a repository that redefines a group owns it completely.
25
+ */
26
+ declare function registryEntries(sources: ReadonlyArray<ParsedRegistrySource>): RegistryEntry[];
27
+ /**
28
+ * The rows one dispatch runs.
29
+ *
30
+ * Group selection changes when /mode switches mid-session, so inclusion is
31
+ * applied per call rather than being baked into a cached parse. Core groups
32
+ * always load; the rest are gated by the layers the harness resolved.
33
+ */
34
+ declare function selectRegistryHooks(entries: ReadonlyArray<RegistryEntry>, selection: RegistrySelection): ResolvedHook[];
35
+ //#endregion
36
+ export { RegistrySelection, registryCacheKey, registryEntries, selectRegistryHooks };
37
+ //# sourceMappingURL=hookRegistry.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookRegistry.d.cts","names":[],"sources":["../../src/services/hookRegistry.ts"],"mappings":";;;UAIiB;EACf;EACA;;EAEA;EACA;;;;;;;;;iBAUc,iBAAiB,SAAS,cAAc;;;;;;;;iBAWxC,gBAAgB,SAAS,cAAc,wBAAwB;;;;;;;;iBAqC/D,oBACd,SAAS,cAAc,gBACvB,WAAW,oBACV"}
@@ -0,0 +1,37 @@
1
+ import { HookDocumentSource, ParsedRegistrySource, RegistryEntry, ResolvedHook } from "../types/hooks.mjs";
2
+ //#region src/services/hookRegistry.d.ts
3
+ /** Which rows a dispatch keeps, once the session's selection is known. */
4
+ interface RegistrySelection {
5
+ event: string;
6
+ toolName?: string;
7
+ /** Selected group ids, or undefined for "every group", the standalone default. */
8
+ allowedGroups?: readonly string[];
9
+ inSubagent: boolean;
10
+ }
11
+ /**
12
+ * Identity of a set of registry sources, for callers that cache the parse.
13
+ *
14
+ * Keyed on contents rather than mtime, so it stays correct for an in-place
15
+ * rewrite and needs no invalidation hook. Reading both files is cheap; parsing
16
+ * is what a cache built on this skips.
17
+ */
18
+ declare function registryCacheKey(sources: ReadonlyArray<HookDocumentSource>): string;
19
+ /**
20
+ * Flattens parsed registry documents into sorted rows, keeping only bindings
21
+ * that declare a `pi` frontend.
22
+ *
23
+ * A later source replaces the group of the same id outright rather than merging
24
+ * into it, so a repository that redefines a group owns it completely.
25
+ */
26
+ declare function registryEntries(sources: ReadonlyArray<ParsedRegistrySource>): RegistryEntry[];
27
+ /**
28
+ * The rows one dispatch runs.
29
+ *
30
+ * Group selection changes when /mode switches mid-session, so inclusion is
31
+ * applied per call rather than being baked into a cached parse. Core groups
32
+ * always load; the rest are gated by the layers the harness resolved.
33
+ */
34
+ declare function selectRegistryHooks(entries: ReadonlyArray<RegistryEntry>, selection: RegistrySelection): ResolvedHook[];
35
+ //#endregion
36
+ export { RegistrySelection, registryCacheKey, registryEntries, selectRegistryHooks };
37
+ //# sourceMappingURL=hookRegistry.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookRegistry.d.mts","names":[],"sources":["../../src/services/hookRegistry.ts"],"mappings":";;;UAIiB;EACf;EACA;;EAEA;EACA;;;;;;;;;iBAUc,iBAAiB,SAAS,cAAc;;;;;;;;iBAWxC,gBAAgB,SAAS,cAAc,wBAAwB;;;;;;;;iBAqC/D,oBACd,SAAS,cAAc,gBACvB,WAAW,oBACV"}
@@ -0,0 +1,2 @@
1
+ import{matchesTool as e}from"./toolNames.mjs";function t(e){return e.map(e=>`${e.baseDirectory} ${e.text}`).join(` `)}function n(e){let t=new Map,n=0;for(let r of e)for(let[e,i]of Object.entries(r.document.groups??{})){let a=[];for(let t of i.hooks??[])n+=1,t.pi&&a.push({...t.pi,event:t.event,order:t.pi.order??0,position:n,groupId:e,core:i.core===!0,baseDirectory:r.baseDirectory});t.set(e,a)}let r=[...t.values()].flat();return r.sort((e,t)=>e.order-t.order||e.position-t.position),r}function r(t,n){let r=n.allowedGroups===void 0?void 0:new Set(n.allowedGroups);return t.filter(e=>e.core||!r||r.has(e.groupId)).filter(e=>e.event===n.event).filter(e=>!(e.skipInSubagent&&n.inSubagent)).filter(t=>e(t.matcher,n.toolName)).map(e=>({hook:{command:e.command,timeout:e.timeout},root:e.baseDirectory}))}export{t as registryCacheKey,n as registryEntries,r as selectRegistryHooks};
2
+ //# sourceMappingURL=hookRegistry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookRegistry.mjs","names":[],"sources":["../../src/services/hookRegistry.ts"],"sourcesContent":["import type { HookDocumentSource, ParsedRegistrySource, RegistryEntry, ResolvedHook } from '../types/hooks.ts';\nimport { matchesTool } from './toolNames.ts';\n\n/** Which rows a dispatch keeps, once the session's selection is known. */\nexport interface RegistrySelection {\n event: string;\n toolName?: string;\n /** Selected group ids, or undefined for \"every group\", the standalone default. */\n allowedGroups?: readonly string[];\n inSubagent: boolean;\n}\n\n/**\n * Identity of a set of registry sources, for callers that cache the parse.\n *\n * Keyed on contents rather than mtime, so it stays correct for an in-place\n * rewrite and needs no invalidation hook. Reading both files is cheap; parsing\n * is what a cache built on this skips.\n */\nexport function registryCacheKey(sources: ReadonlyArray<HookDocumentSource>): string {\n return sources.map((source) => `${source.baseDirectory} ${source.text}`).join(' ');\n}\n\n/**\n * Flattens parsed registry documents into sorted rows, keeping only bindings\n * that declare a `pi` frontend.\n *\n * A later source replaces the group of the same id outright rather than merging\n * into it, so a repository that redefines a group owns it completely.\n */\nexport function registryEntries(sources: ReadonlyArray<ParsedRegistrySource>): RegistryEntry[] {\n const groups = new Map<string, RegistryEntry[]>();\n let position = 0;\n for (const source of sources) {\n for (const [groupId, group] of Object.entries(source.document.groups ?? {})) {\n const entries: RegistryEntry[] = [];\n for (const hook of group.hooks ?? []) {\n // Every declared hook advances the position, whether or not it is kept,\n // so the sort tiebreaker stays the order the file declares them in.\n position += 1;\n if (!hook.pi) continue;\n entries.push({\n ...hook.pi,\n event: hook.event,\n order: hook.pi.order ?? 0,\n position,\n groupId,\n core: group.core === true,\n baseDirectory: source.baseDirectory,\n });\n }\n groups.set(groupId, entries);\n }\n }\n\n const entries = [...groups.values()].flat();\n entries.sort((left, right) => left.order - right.order || left.position - right.position);\n return entries;\n}\n\n/**\n * The rows one dispatch runs.\n *\n * Group selection changes when /mode switches mid-session, so inclusion is\n * applied per call rather than being baked into a cached parse. Core groups\n * always load; the rest are gated by the layers the harness resolved.\n */\nexport function selectRegistryHooks(\n entries: ReadonlyArray<RegistryEntry>,\n selection: RegistrySelection,\n): ResolvedHook[] {\n const allowed = selection.allowedGroups === undefined ? undefined : new Set(selection.allowedGroups);\n return entries\n .filter((entry) => entry.core || !allowed || allowed.has(entry.groupId))\n .filter((entry) => entry.event === selection.event)\n .filter((entry) => !(entry.skipInSubagent && selection.inSubagent))\n .filter((entry) => matchesTool(entry.matcher, selection.toolName))\n .map((entry) => ({\n hook: { command: entry.command, timeout: entry.timeout },\n // The declaring config's root, so a global hook can reach its own scripts\n // through CLAUDE_PLUGIN_ROOT while still running against this repository.\n root: entry.baseDirectory,\n }));\n}\n"],"mappings":"8CAmBA,SAAgB,EAAiB,EAAoD,CACnF,OAAO,EAAQ,IAAK,GAAW,GAAG,EAAO,cAAc,GAAG,EAAO,MAAM,CAAC,CAAC,KAAK,GAAG,CACnF,CASA,SAAgB,EAAgB,EAA+D,CAC7F,IAAM,EAAS,IAAI,IACf,EAAW,EACf,IAAK,IAAM,KAAU,EACnB,IAAK,GAAM,CAAC,EAAS,KAAU,OAAO,QAAQ,EAAO,SAAS,QAAU,CAAC,CAAC,EAAG,CAC3E,IAAM,EAA2B,CAAC,EAClC,IAAK,IAAM,KAAQ,EAAM,OAAS,CAAC,EAGjC,GAAY,EACP,EAAK,IACV,EAAQ,KAAK,CACX,GAAG,EAAK,GACR,MAAO,EAAK,MACZ,MAAO,EAAK,GAAG,OAAS,EACxB,WACA,UACA,KAAM,EAAM,OAAS,GACrB,cAAe,EAAO,aACxB,CAAC,EAEH,EAAO,IAAI,EAAS,CAAO,CAC7B,CAGF,IAAM,EAAU,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,KAAK,EAE1C,OADA,EAAQ,MAAM,EAAM,IAAU,EAAK,MAAQ,EAAM,OAAS,EAAK,SAAW,EAAM,QAAQ,EACjF,CACT,CASA,SAAgB,EACd,EACA,EACgB,CAChB,IAAM,EAAU,EAAU,gBAAkB,IAAA,GAAY,IAAA,GAAY,IAAI,IAAI,EAAU,aAAa,EACnG,OAAO,EACJ,OAAQ,GAAU,EAAM,MAAQ,CAAC,GAAW,EAAQ,IAAI,EAAM,OAAO,CAAC,CAAC,CACvE,OAAQ,GAAU,EAAM,QAAU,EAAU,KAAK,CAAC,CAClD,OAAQ,GAAU,EAAE,EAAM,gBAAkB,EAAU,WAAW,CAAC,CAClE,OAAQ,GAAU,EAAY,EAAM,QAAS,EAAU,QAAQ,CAAC,CAAC,CACjE,IAAK,IAAW,CACf,KAAM,CAAE,QAAS,EAAM,QAAS,QAAS,EAAM,OAAQ,EAGvD,KAAM,EAAM,aACd,EAAE,CACN"}
@@ -0,0 +1,2 @@
1
+ const e=require("./toolNames.cjs");function t(t,n,r){let i=[];for(let a of t)for(let t of a.config.hooks?.[n]??[])if(e.matchesTool(t.matcher,r))for(let e of t.hooks??[])i.push({hook:e,root:a.pluginRoot});return i}exports.selectPluginHooks=t;
2
+ //# sourceMappingURL=pluginHooks.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginHooks.cjs","names":["matchesTool"],"sources":["../../src/services/pluginHooks.ts"],"sourcesContent":["import type { PluginHookDocument, ResolvedHook } from '../types/hooks.ts';\nimport { matchesTool } from './toolNames.ts';\n\n/**\n * The plugin hooks one dispatch runs.\n *\n * Plugin configs are the Claude Code `hooks.json` shape verbatim: an event name\n * maps to groups, each with an optional matcher and a list of commands. Order\n * follows the declaration order of the plugins the harness resolved.\n */\nexport function selectPluginHooks(\n documents: ReadonlyArray<PluginHookDocument>,\n eventName: string,\n toolName?: string,\n): ResolvedHook[] {\n const matches: ResolvedHook[] = [];\n for (const document of documents) {\n for (const group of document.config.hooks?.[eventName] ?? []) {\n if (!matchesTool(group.matcher, toolName)) continue;\n for (const hook of group.hooks ?? []) matches.push({ hook, root: document.pluginRoot });\n }\n }\n return matches;\n}\n"],"mappings":"mCAUA,SAAgB,EACd,EACA,EACA,EACgB,CAChB,IAAM,EAA0B,CAAC,EACjC,IAAK,IAAM,KAAY,EACrB,IAAK,IAAM,KAAS,EAAS,OAAO,QAAQ,IAAc,CAAC,EACpDA,KAAAA,YAAY,EAAM,QAAS,CAAQ,EACxC,IAAK,IAAM,KAAQ,EAAM,OAAS,CAAC,EAAG,EAAQ,KAAK,CAAE,OAAM,KAAM,EAAS,UAAW,CAAC,EAG1F,OAAO,CACT"}
@@ -0,0 +1,13 @@
1
+ import { PluginHookDocument, ResolvedHook } from "../types/hooks.cjs";
2
+ //#region src/services/pluginHooks.d.ts
3
+ /**
4
+ * The plugin hooks one dispatch runs.
5
+ *
6
+ * Plugin configs are the Claude Code `hooks.json` shape verbatim: an event name
7
+ * maps to groups, each with an optional matcher and a list of commands. Order
8
+ * follows the declaration order of the plugins the harness resolved.
9
+ */
10
+ declare function selectPluginHooks(documents: ReadonlyArray<PluginHookDocument>, eventName: string, toolName?: string): ResolvedHook[];
11
+ //#endregion
12
+ export { selectPluginHooks };
13
+ //# sourceMappingURL=pluginHooks.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginHooks.d.cts","names":[],"sources":["../../src/services/pluginHooks.ts"],"mappings":";;;;;;;;;iBAUgB,kBACd,WAAW,cAAc,qBACzB,mBACA,oBACC"}
@@ -0,0 +1,13 @@
1
+ import { PluginHookDocument, ResolvedHook } from "../types/hooks.mjs";
2
+ //#region src/services/pluginHooks.d.ts
3
+ /**
4
+ * The plugin hooks one dispatch runs.
5
+ *
6
+ * Plugin configs are the Claude Code `hooks.json` shape verbatim: an event name
7
+ * maps to groups, each with an optional matcher and a list of commands. Order
8
+ * follows the declaration order of the plugins the harness resolved.
9
+ */
10
+ declare function selectPluginHooks(documents: ReadonlyArray<PluginHookDocument>, eventName: string, toolName?: string): ResolvedHook[];
11
+ //#endregion
12
+ export { selectPluginHooks };
13
+ //# sourceMappingURL=pluginHooks.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginHooks.d.mts","names":[],"sources":["../../src/services/pluginHooks.ts"],"mappings":";;;;;;;;;iBAUgB,kBACd,WAAW,cAAc,qBACzB,mBACA,oBACC"}
@@ -0,0 +1,2 @@
1
+ import{matchesTool as e}from"./toolNames.mjs";function t(t,n,r){let i=[];for(let a of t)for(let t of a.config.hooks?.[n]??[])if(e(t.matcher,r))for(let e of t.hooks??[])i.push({hook:e,root:a.pluginRoot});return i}export{t as selectPluginHooks};
2
+ //# sourceMappingURL=pluginHooks.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginHooks.mjs","names":[],"sources":["../../src/services/pluginHooks.ts"],"sourcesContent":["import type { PluginHookDocument, ResolvedHook } from '../types/hooks.ts';\nimport { matchesTool } from './toolNames.ts';\n\n/**\n * The plugin hooks one dispatch runs.\n *\n * Plugin configs are the Claude Code `hooks.json` shape verbatim: an event name\n * maps to groups, each with an optional matcher and a list of commands. Order\n * follows the declaration order of the plugins the harness resolved.\n */\nexport function selectPluginHooks(\n documents: ReadonlyArray<PluginHookDocument>,\n eventName: string,\n toolName?: string,\n): ResolvedHook[] {\n const matches: ResolvedHook[] = [];\n for (const document of documents) {\n for (const group of document.config.hooks?.[eventName] ?? []) {\n if (!matchesTool(group.matcher, toolName)) continue;\n for (const hook of group.hooks ?? []) matches.push({ hook, root: document.pluginRoot });\n }\n }\n return matches;\n}\n"],"mappings":"8CAUA,SAAgB,EACd,EACA,EACA,EACgB,CAChB,IAAM,EAA0B,CAAC,EACjC,IAAK,IAAM,KAAY,EACrB,IAAK,IAAM,KAAS,EAAS,OAAO,QAAQ,IAAc,CAAC,EACpD,KAAY,EAAM,QAAS,CAAQ,EACxC,IAAK,IAAM,KAAQ,EAAM,OAAS,CAAC,EAAG,EAAQ,KAAK,CAAE,OAAM,KAAM,EAAS,UAAW,CAAC,EAG1F,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ const e=new Map([[`read`,`Read`],[`edit`,`Edit`],[`write`,`Write`],[`bash`,`Bash`],[`find`,`Glob`],[`grep`,`Grep`],[`subagent`,`Agent`]]);function t(t){return e.get(t)??t}function n(e,n){return!n||!e||new RegExp(e).test(t(n))}exports.matchesTool=n,exports.toClaudeToolName=t;
2
+ //# sourceMappingURL=toolNames.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolNames.cjs","names":[],"sources":["../../src/services/toolNames.ts"],"sourcesContent":["/**\n * Pi's tool names translated into the Claude names hook matchers are written\n * against.\n *\n * Hook authors write `matcher: Bash`, and Pi reports `bash`, so a matcher would\n * never fire without this. Only this direction belongs here: rewriting Claude\n * names for Pi is an agent-definition concern the harness owns.\n */\nconst PI_TO_CLAUDE = new Map([\n ['read', 'Read'],\n ['edit', 'Edit'],\n ['write', 'Write'],\n ['bash', 'Bash'],\n ['find', 'Glob'],\n ['grep', 'Grep'],\n ['subagent', 'Agent'],\n]);\n\n/** The Claude name a hook matcher expects for a tool Pi just ran. */\nexport function toClaudeToolName(tool: string): string {\n return PI_TO_CLAUDE.get(tool) ?? tool;\n}\n\n/** Whether a matcher, if the row declares one, accepts the tool that fired. */\nexport function matchesTool(matcher: string | undefined, toolName: string | undefined): boolean {\n if (!toolName || !matcher) return true;\n return new RegExp(matcher).test(toClaudeToolName(toolName));\n}\n"],"mappings":"AAQA,MAAM,EAAe,IAAI,IAAI,CAC3B,CAAC,OAAQ,MAAM,EACf,CAAC,OAAQ,MAAM,EACf,CAAC,QAAS,OAAO,EACjB,CAAC,OAAQ,MAAM,EACf,CAAC,OAAQ,MAAM,EACf,CAAC,OAAQ,MAAM,EACf,CAAC,WAAY,OAAO,CACtB,CAAC,EAGD,SAAgB,EAAiB,EAAsB,CACrD,OAAO,EAAa,IAAI,CAAI,GAAK,CACnC,CAGA,SAAgB,EAAY,EAA6B,EAAuC,CAE9F,MADI,CAAC,GAAY,CAAC,GACX,IAAI,OAAO,CAAO,CAAC,CAAC,KAAK,EAAiB,CAAQ,CAAC,CAC5D"}
@@ -0,0 +1,8 @@
1
+ //#region src/services/toolNames.d.ts
2
+ /** The Claude name a hook matcher expects for a tool Pi just ran. */
3
+ declare function toClaudeToolName(tool: string): string;
4
+ /** Whether a matcher, if the row declares one, accepts the tool that fired. */
5
+ declare function matchesTool(matcher: string | undefined, toolName: string | undefined): boolean;
6
+ //#endregion
7
+ export { matchesTool, toClaudeToolName };
8
+ //# sourceMappingURL=toolNames.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolNames.d.cts","names":[],"sources":["../../src/services/toolNames.ts"],"mappings":";;iBAmBgB,iBAAiB;;iBAKjB,YAAY,6BAA6B"}
@@ -0,0 +1,8 @@
1
+ //#region src/services/toolNames.d.ts
2
+ /** The Claude name a hook matcher expects for a tool Pi just ran. */
3
+ declare function toClaudeToolName(tool: string): string;
4
+ /** Whether a matcher, if the row declares one, accepts the tool that fired. */
5
+ declare function matchesTool(matcher: string | undefined, toolName: string | undefined): boolean;
6
+ //#endregion
7
+ export { matchesTool, toClaudeToolName };
8
+ //# sourceMappingURL=toolNames.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolNames.d.mts","names":[],"sources":["../../src/services/toolNames.ts"],"mappings":";;iBAmBgB,iBAAiB;;iBAKjB,YAAY,6BAA6B"}
@@ -0,0 +1,2 @@
1
+ const e=new Map([[`read`,`Read`],[`edit`,`Edit`],[`write`,`Write`],[`bash`,`Bash`],[`find`,`Glob`],[`grep`,`Grep`],[`subagent`,`Agent`]]);function t(t){return e.get(t)??t}function n(e,n){return!n||!e||new RegExp(e).test(t(n))}export{n as matchesTool,t as toClaudeToolName};
2
+ //# sourceMappingURL=toolNames.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolNames.mjs","names":[],"sources":["../../src/services/toolNames.ts"],"sourcesContent":["/**\n * Pi's tool names translated into the Claude names hook matchers are written\n * against.\n *\n * Hook authors write `matcher: Bash`, and Pi reports `bash`, so a matcher would\n * never fire without this. Only this direction belongs here: rewriting Claude\n * names for Pi is an agent-definition concern the harness owns.\n */\nconst PI_TO_CLAUDE = new Map([\n ['read', 'Read'],\n ['edit', 'Edit'],\n ['write', 'Write'],\n ['bash', 'Bash'],\n ['find', 'Glob'],\n ['grep', 'Grep'],\n ['subagent', 'Agent'],\n]);\n\n/** The Claude name a hook matcher expects for a tool Pi just ran. */\nexport function toClaudeToolName(tool: string): string {\n return PI_TO_CLAUDE.get(tool) ?? tool;\n}\n\n/** Whether a matcher, if the row declares one, accepts the tool that fired. */\nexport function matchesTool(matcher: string | undefined, toolName: string | undefined): boolean {\n if (!toolName || !matcher) return true;\n return new RegExp(matcher).test(toClaudeToolName(toolName));\n}\n"],"mappings":"AAQA,MAAM,EAAe,IAAI,IAAI,CAC3B,CAAC,OAAQ,MAAM,EACf,CAAC,OAAQ,MAAM,EACf,CAAC,QAAS,OAAO,EACjB,CAAC,OAAQ,MAAM,EACf,CAAC,OAAQ,MAAM,EACf,CAAC,OAAQ,MAAM,EACf,CAAC,WAAY,OAAO,CACtB,CAAC,EAGD,SAAgB,EAAiB,EAAsB,CACrD,OAAO,EAAa,IAAI,CAAI,GAAK,CACnC,CAGA,SAAgB,EAAY,EAA6B,EAAuC,CAE9F,MADI,CAAC,GAAY,CAAC,GACX,IAAI,OAAO,CAAO,CAAC,CAAC,KAAK,EAAiB,CAAQ,CAAC,CAC5D"}
@@ -0,0 +1,2 @@
1
+ const e={sessionStart:`SessionStart`,preToolUse:`PreToolUse`,postToolUse:`PostToolUse`,stop:`Stop`,sessionEnd:`SessionEnd`};exports.HOOK_EVENT=e;
2
+ //# sourceMappingURL=hooks.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.cjs","names":[],"sources":["../../src/types/hooks.ts"],"sourcesContent":["/**\n * The hook document contract and the ports that run it.\n *\n * Every name here is written in Claude Code's vocabulary, because that is what\n * `.doom/hooks.yaml` and plugin `hooks.json` files are authored against. The\n * translation onto Pi's lifecycle happens at the adapter boundary.\n */\n\n/** Registry event names. Each is used twice per dispatch, so drift would be silent. */\nexport const HOOK_EVENT = {\n sessionStart: 'SessionStart',\n preToolUse: 'PreToolUse',\n postToolUse: 'PostToolUse',\n stop: 'Stop',\n sessionEnd: 'SessionEnd',\n} as const;\n\nexport type HookEventName = (typeof HOOK_EVENT)[keyof typeof HOOK_EVENT];\n\nexport interface HookCommand {\n command: string;\n /** Seconds before the hook is terminated. Defaults to DEFAULT_HOOK_TIMEOUT_SECONDS. */\n timeout?: number;\n}\n\n/** What one registry row declares for the `pi` frontend. */\nexport interface RegistryBinding {\n matcher?: string;\n command: string;\n timeout?: number;\n skipInSubagent?: boolean;\n order?: number;\n}\n\n/** A registry row plus the group membership used to include or drop it. */\nexport interface RegistryEntry extends RegistryBinding {\n event: string;\n order: number;\n /** Declaration order across every source, used as the sort tiebreaker. */\n position: number;\n groupId: string;\n core: boolean;\n /** Root of the config that declared the group, exported as CLAUDE_PLUGIN_ROOT. */\n baseDirectory: string;\n}\n\nexport interface RegistryGroup {\n core?: boolean;\n hooks?: Array<{ event: string; pi?: RegistryBinding }>;\n}\n\nexport interface RegistryDocument {\n groups?: Record<string, RegistryGroup>;\n}\n\n/** One `.doom/hooks.yaml` as read from disk, before it is parsed. */\nexport interface HookDocumentSource {\n baseDirectory: string;\n text: string;\n}\n\n/** One `.doom/hooks.yaml` after parsing, tagged with the root that declared it. */\nexport interface ParsedRegistrySource {\n baseDirectory: string;\n document: RegistryDocument;\n}\n\nexport interface PluginHookGroup {\n matcher?: string;\n hooks?: HookCommand[];\n}\n\nexport interface PluginHookConfig {\n hooks?: Record<string, PluginHookGroup[]>;\n}\n\n/** A parsed plugin config plus the root exported to its commands. */\nexport interface PluginHookDocument {\n pluginRoot: string;\n config: PluginHookConfig;\n}\n\n/** A hook command paired with the config root it runs against. */\nexport interface ResolvedHook {\n hook: HookCommand;\n root: string;\n}\n\n/** The JSON a hook may write on stdout to steer or block the call it observed. */\nexport interface HookDecision {\n decision?: string;\n reason?: string;\n hookSpecificOutput?: {\n permissionDecision?: string;\n reason?: string;\n additionalContext?: string;\n };\n}\n\nexport type HookFailureReason =\n | 'spawn_failed'\n | 'non_zero_exit'\n | 'timeout'\n | 'invalid_json'\n | 'registry_read'\n | 'plugin_config';\n\nexport interface HookFailure {\n command: string;\n message: string;\n reason: HookFailureReason;\n}\n\nexport interface HookOutcome {\n decision?: HookDecision;\n failure?: HookFailure;\n}\n\n/**\n * The tool fields a payload is built from, named without importing Pi.\n *\n * Pi's ToolCallEvent and ToolResultEvent both satisfy this structurally, so the\n * adapter forwards them unchanged and the payload builder stays host-neutral.\n */\nexport interface HookToolEvent {\n readonly type: string;\n readonly toolName: string;\n readonly input: unknown;\n readonly content?: readonly unknown[];\n readonly isError?: boolean;\n}\n\n/** The JSON document handed to a hook on stdin. */\nexport type HookPayload = Record<string, unknown>;\n\nexport interface HookRunOptions {\n repoRoot: string;\n /** Exported as CLAUDE_PLUGIN_ROOT so a hook can reach its own scripts. */\n pluginRoot?: string;\n}\n\n/** Runs one hook command and reports what it decided or how it failed. */\nexport interface HookRunner {\n run(hook: HookCommand, payload: HookPayload, options: HookRunOptions): Promise<HookOutcome>;\n}\n\n/** Registry rows for this repository, or the failure that emptied them. */\nexport interface RegistryRead {\n entries: RegistryEntry[];\n failure?: HookFailure;\n}\n\n/** Plugin configs that could be read, and one failure per config that could not. */\nexport interface PluginDocumentRead {\n documents: PluginHookDocument[];\n failures: HookFailure[];\n}\n\n/** Where a plugin declares its hooks. Mirrors the harness state entry. */\nexport interface PluginHookSourceRef {\n readonly pluginRoot: string;\n readonly configPath: string;\n}\n\n/** Reads the hook documents a session runs from. */\nexport interface HookDocumentReader {\n registry(repoRoot: string): Promise<RegistryRead>;\n plugins(sources: readonly PluginHookSourceRef[]): Promise<PluginDocumentRead>;\n}\n"],"mappings":"AASA,MAAa,EAAa,CACxB,aAAc,eACd,WAAY,aACZ,YAAa,cACb,KAAM,OACN,WAAY,YACd"}
@@ -0,0 +1,145 @@
1
+ //#region src/types/hooks.d.ts
2
+ /**
3
+ * The hook document contract and the ports that run it.
4
+ *
5
+ * Every name here is written in Claude Code's vocabulary, because that is what
6
+ * `.doom/hooks.yaml` and plugin `hooks.json` files are authored against. The
7
+ * translation onto Pi's lifecycle happens at the adapter boundary.
8
+ */
9
+ /** Registry event names. Each is used twice per dispatch, so drift would be silent. */
10
+ declare const HOOK_EVENT: {
11
+ readonly sessionStart: "SessionStart";
12
+ readonly preToolUse: "PreToolUse";
13
+ readonly postToolUse: "PostToolUse";
14
+ readonly stop: "Stop";
15
+ readonly sessionEnd: "SessionEnd";
16
+ };
17
+ type HookEventName = (typeof HOOK_EVENT)[keyof typeof HOOK_EVENT];
18
+ interface HookCommand {
19
+ command: string;
20
+ /** Seconds before the hook is terminated. Defaults to DEFAULT_HOOK_TIMEOUT_SECONDS. */
21
+ timeout?: number;
22
+ }
23
+ /** What one registry row declares for the `pi` frontend. */
24
+ interface RegistryBinding {
25
+ matcher?: string;
26
+ command: string;
27
+ timeout?: number;
28
+ skipInSubagent?: boolean;
29
+ order?: number;
30
+ }
31
+ /** A registry row plus the group membership used to include or drop it. */
32
+ interface RegistryEntry extends RegistryBinding {
33
+ event: string;
34
+ order: number;
35
+ /** Declaration order across every source, used as the sort tiebreaker. */
36
+ position: number;
37
+ groupId: string;
38
+ core: boolean;
39
+ /** Root of the config that declared the group, exported as CLAUDE_PLUGIN_ROOT. */
40
+ baseDirectory: string;
41
+ }
42
+ interface RegistryGroup {
43
+ core?: boolean;
44
+ hooks?: Array<{
45
+ event: string;
46
+ pi?: RegistryBinding;
47
+ }>;
48
+ }
49
+ interface RegistryDocument {
50
+ groups?: Record<string, RegistryGroup>;
51
+ }
52
+ /** One `.doom/hooks.yaml` as read from disk, before it is parsed. */
53
+ interface HookDocumentSource {
54
+ baseDirectory: string;
55
+ text: string;
56
+ }
57
+ /** One `.doom/hooks.yaml` after parsing, tagged with the root that declared it. */
58
+ interface ParsedRegistrySource {
59
+ baseDirectory: string;
60
+ document: RegistryDocument;
61
+ }
62
+ interface PluginHookGroup {
63
+ matcher?: string;
64
+ hooks?: HookCommand[];
65
+ }
66
+ interface PluginHookConfig {
67
+ hooks?: Record<string, PluginHookGroup[]>;
68
+ }
69
+ /** A parsed plugin config plus the root exported to its commands. */
70
+ interface PluginHookDocument {
71
+ pluginRoot: string;
72
+ config: PluginHookConfig;
73
+ }
74
+ /** A hook command paired with the config root it runs against. */
75
+ interface ResolvedHook {
76
+ hook: HookCommand;
77
+ root: string;
78
+ }
79
+ /** The JSON a hook may write on stdout to steer or block the call it observed. */
80
+ interface HookDecision {
81
+ decision?: string;
82
+ reason?: string;
83
+ hookSpecificOutput?: {
84
+ permissionDecision?: string;
85
+ reason?: string;
86
+ additionalContext?: string;
87
+ };
88
+ }
89
+ type HookFailureReason = 'spawn_failed' | 'non_zero_exit' | 'timeout' | 'invalid_json' | 'registry_read' | 'plugin_config';
90
+ interface HookFailure {
91
+ command: string;
92
+ message: string;
93
+ reason: HookFailureReason;
94
+ }
95
+ interface HookOutcome {
96
+ decision?: HookDecision;
97
+ failure?: HookFailure;
98
+ }
99
+ /**
100
+ * The tool fields a payload is built from, named without importing Pi.
101
+ *
102
+ * Pi's ToolCallEvent and ToolResultEvent both satisfy this structurally, so the
103
+ * adapter forwards them unchanged and the payload builder stays host-neutral.
104
+ */
105
+ interface HookToolEvent {
106
+ readonly type: string;
107
+ readonly toolName: string;
108
+ readonly input: unknown;
109
+ readonly content?: readonly unknown[];
110
+ readonly isError?: boolean;
111
+ }
112
+ /** The JSON document handed to a hook on stdin. */
113
+ type HookPayload = Record<string, unknown>;
114
+ interface HookRunOptions {
115
+ repoRoot: string;
116
+ /** Exported as CLAUDE_PLUGIN_ROOT so a hook can reach its own scripts. */
117
+ pluginRoot?: string;
118
+ }
119
+ /** Runs one hook command and reports what it decided or how it failed. */
120
+ interface HookRunner {
121
+ run(hook: HookCommand, payload: HookPayload, options: HookRunOptions): Promise<HookOutcome>;
122
+ }
123
+ /** Registry rows for this repository, or the failure that emptied them. */
124
+ interface RegistryRead {
125
+ entries: RegistryEntry[];
126
+ failure?: HookFailure;
127
+ }
128
+ /** Plugin configs that could be read, and one failure per config that could not. */
129
+ interface PluginDocumentRead {
130
+ documents: PluginHookDocument[];
131
+ failures: HookFailure[];
132
+ }
133
+ /** Where a plugin declares its hooks. Mirrors the harness state entry. */
134
+ interface PluginHookSourceRef {
135
+ readonly pluginRoot: string;
136
+ readonly configPath: string;
137
+ }
138
+ /** Reads the hook documents a session runs from. */
139
+ interface HookDocumentReader {
140
+ registry(repoRoot: string): Promise<RegistryRead>;
141
+ plugins(sources: readonly PluginHookSourceRef[]): Promise<PluginDocumentRead>;
142
+ }
143
+ //#endregion
144
+ export { HOOK_EVENT, HookCommand, HookDecision, HookDocumentReader, HookDocumentSource, HookEventName, HookFailure, HookFailureReason, HookOutcome, HookPayload, HookRunOptions, HookRunner, HookToolEvent, ParsedRegistrySource, PluginDocumentRead, PluginHookConfig, PluginHookDocument, PluginHookGroup, PluginHookSourceRef, RegistryBinding, RegistryDocument, RegistryEntry, RegistryGroup, RegistryRead, ResolvedHook };
145
+ //# sourceMappingURL=hooks.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.cts","names":[],"sources":["../../src/types/hooks.ts"],"mappings":";;;;;;;;;cASa;;;;;;;KAQD,wBAAwB,yBAAyB;UAE5C;EACf;;EAEA;;;UAIe;EACf;EACA;EACA;EACA;EACA;;;UAIe,sBAAsB;EACrC;EACA;;EAEA;EACA;EACA;;EAEA;;UAGe;EACf;EACA,QAAQ;IAAQ;IAAe,KAAK;;;UAGrB;EACf,SAAS,eAAe;;;UAIT;EACf;EACA;;;UAIe;EACf;EACA,UAAU;;UAGK;EACf;EACA,QAAQ;;UAGO;EACf,QAAQ,eAAe;;;UAIR;EACf;EACA,QAAQ;;;UAIO;EACf,MAAM;EACN;;;UAIe;EACf;EACA;EACA;IACE;IACA;IACA;;;KAIQ;UAQK;EACf;EACA;EACA,QAAQ;;UAGO;EACf,WAAW;EACX,UAAU;;;;;;;;UASK;WACN;WACA;WACA;WACA;WACA;;;KAIC,cAAc;UAET;EACf;;EAEA;;;UAIe;EACf,IAAI,MAAM,aAAa,SAAS,aAAa,SAAS,iBAAiB,QAAQ;;;UAIhE;EACf,SAAS;EACT,UAAU;;;UAIK;EACf,WAAW;EACX,UAAU;;;UAIK;WACN;WACA;;;UAIM;EACf,SAAS,mBAAmB,QAAQ;EACpC,QAAQ,kBAAkB,wBAAwB,QAAQ"}