@cleocode/cleo 2026.3.71 → 2026.3.73

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/cli/index.js CHANGED
@@ -733,22 +733,42 @@ __export(registry_exports, {
733
733
  HookRegistry: () => HookRegistry,
734
734
  hooks: () => hooks
735
735
  });
736
- var DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
736
+ var LEGACY_EVENT_MAP, DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
737
737
  var init_registry = __esm({
738
738
  "packages/core/src/hooks/registry.ts"() {
739
739
  "use strict";
740
740
  init_logger();
741
+ LEGACY_EVENT_MAP = {
742
+ onSessionStart: "SessionStart",
743
+ onSessionEnd: "SessionEnd",
744
+ onToolStart: "PreToolUse",
745
+ onToolComplete: "PostToolUse",
746
+ onFileChange: "Notification",
747
+ onError: "PostToolUseFailure",
748
+ onPromptSubmit: "PromptSubmit",
749
+ onResponseComplete: "ResponseComplete"
750
+ };
741
751
  DEFAULT_HOOK_CONFIG = {
742
752
  enabled: true,
743
753
  events: {
744
- onSessionStart: true,
745
- onSessionEnd: true,
746
- onToolStart: true,
747
- onToolComplete: true,
748
- onFileChange: true,
749
- onError: true,
750
- onPromptSubmit: true,
751
- onResponseComplete: true,
754
+ // CAAMP canonical events (16)
755
+ SessionStart: true,
756
+ SessionEnd: true,
757
+ PromptSubmit: true,
758
+ ResponseComplete: true,
759
+ PreToolUse: true,
760
+ PostToolUse: true,
761
+ PostToolUseFailure: true,
762
+ PermissionRequest: true,
763
+ SubagentStart: true,
764
+ SubagentStop: true,
765
+ PreModel: true,
766
+ PostModel: true,
767
+ PreCompact: true,
768
+ PostCompact: true,
769
+ Notification: true,
770
+ ConfigChange: true,
771
+ // CLEO internal coordination events (5)
752
772
  onWorkAvailable: true,
753
773
  onAgentSpawn: true,
754
774
  onAgentComplete: true,
@@ -759,12 +779,33 @@ var init_registry = __esm({
759
779
  HookRegistry = class {
760
780
  handlers = /* @__PURE__ */ new Map();
761
781
  config = DEFAULT_HOOK_CONFIG;
782
+ /**
783
+ * Resolve a potentially-legacy event name to its canonical equivalent.
784
+ *
785
+ * If the event name matches a known legacy `on`-prefix name, it is
786
+ * remapped and a deprecation warning is logged. Unknown names pass through
787
+ * unchanged so callers using the new canonical names are unaffected.
788
+ */
789
+ resolveEvent(event) {
790
+ const canonical = LEGACY_EVENT_MAP[event];
791
+ if (canonical) {
792
+ getLogger("hooks").warn(
793
+ { legacyEvent: event, canonicalEvent: canonical },
794
+ `[DEPRECATED] Hook event '${event}' has been renamed to '${canonical}'. Update your handler registration.`
795
+ );
796
+ return canonical;
797
+ }
798
+ return event;
799
+ }
762
800
  /**
763
801
  * Register a hook handler for a specific event.
764
802
  *
765
803
  * Handlers are sorted by priority (highest first) and executed
766
804
  * in parallel when the event is dispatched.
767
805
  *
806
+ * Backward compatibility: legacy `on`-prefix event names are automatically
807
+ * remapped to their canonical equivalents.
808
+ *
768
809
  * @param registration - The hook registration containing event, handler, priority, and ID
769
810
  * @returns A function to unregister the handler
770
811
  *
@@ -772,7 +813,7 @@ var init_registry = __esm({
772
813
  * ```typescript
773
814
  * const unregister = hooks.register({
774
815
  * id: 'my-handler',
775
- * event: 'onSessionStart',
816
+ * event: 'SessionStart',
776
817
  * handler: async (root, payload) => { console.log('Session started'); },
777
818
  * priority: 100
778
819
  * });
@@ -781,12 +822,14 @@ var init_registry = __esm({
781
822
  * ```
782
823
  */
783
824
  register(registration) {
784
- const list = this.handlers.get(registration.event) || [];
785
- list.push(registration);
825
+ const resolvedEvent = this.resolveEvent(registration.event);
826
+ const resolvedRegistration = { ...registration, event: resolvedEvent };
827
+ const list = this.handlers.get(resolvedEvent) || [];
828
+ list.push(resolvedRegistration);
786
829
  list.sort((a, b) => b.priority - a.priority);
787
- this.handlers.set(registration.event, list);
830
+ this.handlers.set(resolvedEvent, list);
788
831
  return () => {
789
- const handlers = this.handlers.get(registration.event);
832
+ const handlers = this.handlers.get(resolvedEvent);
790
833
  if (handlers) {
791
834
  const idx = handlers.findIndex((h) => h.id === registration.id);
792
835
  if (idx !== -1) handlers.splice(idx, 1);
@@ -800,14 +843,17 @@ var init_registry = __esm({
800
843
  * execution. Errors in individual handlers are logged but do not block
801
844
  * other handlers or propagate to the caller.
802
845
  *
803
- * @param event - The CAAMP hook event to dispatch
846
+ * Backward compatibility: legacy `on`-prefix event names are automatically
847
+ * remapped to their canonical equivalents.
848
+ *
849
+ * @param event - The CAAMP canonical hook event to dispatch
804
850
  * @param projectRoot - The project root directory path
805
851
  * @param payload - The event payload (typed by event)
806
852
  * @returns Promise that resolves when all handlers have completed
807
853
  *
808
854
  * @example
809
855
  * ```typescript
810
- * await hooks.dispatch('onSessionStart', '/project', {
856
+ * await hooks.dispatch('SessionStart', '/project', {
811
857
  * timestamp: new Date().toISOString(),
812
858
  * sessionId: 'sess-123',
813
859
  * name: 'My Session',
@@ -817,15 +863,19 @@ var init_registry = __esm({
817
863
  */
818
864
  async dispatch(event, projectRoot, payload) {
819
865
  if (!this.config.enabled) return;
820
- if (!this.config.events[event]) return;
821
- const handlers = this.handlers.get(event);
866
+ const resolvedEvent = this.resolveEvent(event);
867
+ if (!this.config.events[resolvedEvent]) return;
868
+ const handlers = this.handlers.get(resolvedEvent);
822
869
  if (!handlers || handlers.length === 0) return;
823
870
  await Promise.allSettled(
824
871
  handlers.map(async (reg) => {
825
872
  try {
826
873
  await reg.handler(projectRoot, payload);
827
874
  } catch (error40) {
828
- getLogger("hooks").warn({ err: error40, hookId: reg.id, event }, "Hook handler failed");
875
+ getLogger("hooks").warn(
876
+ { err: error40, hookId: reg.id, event: resolvedEvent },
877
+ "Hook handler failed"
878
+ );
829
879
  }
830
880
  })
831
881
  );
@@ -834,12 +884,14 @@ var init_registry = __esm({
834
884
  * Check if a specific event is currently enabled.
835
885
  *
836
886
  * Both the global enabled flag and the per-event flag must be true.
887
+ * Automatically resolves legacy `on`-prefix event names.
837
888
  *
838
889
  * @param event - The CAAMP hook event to check
839
890
  * @returns True if the event is enabled
840
891
  */
841
892
  isEnabled(event) {
842
- return this.config.enabled && this.config.events[event];
893
+ const resolvedEvent = this.resolveEvent(event);
894
+ return this.config.enabled && this.config.events[resolvedEvent];
843
895
  }
844
896
  /**
845
897
  * Update the hook system configuration.
@@ -851,7 +903,7 @@ var init_registry = __esm({
851
903
  * @example
852
904
  * ```typescript
853
905
  * hooks.setConfig({ enabled: false }); // Disable all hooks
854
- * hooks.setConfig({ events: { onError: false } }); // Disable specific event
906
+ * hooks.setConfig({ events: { PostToolUseFailure: false } }); // Disable specific event
855
907
  * ```
856
908
  */
857
909
  setConfig(config2) {
@@ -869,12 +921,14 @@ var init_registry = __esm({
869
921
  * List all registered handlers for a specific event.
870
922
  *
871
923
  * Returns handlers in priority order (highest first).
924
+ * Automatically resolves legacy `on`-prefix event names.
872
925
  *
873
926
  * @param event - The CAAMP hook event
874
927
  * @returns Array of hook registrations
875
928
  */
876
929
  listHandlers(event) {
877
- return [...this.handlers.get(event) || []];
930
+ const resolvedEvent = this.resolveEvent(event);
931
+ return [...this.handlers.get(resolvedEvent) || []];
878
932
  }
879
933
  };
880
934
  hooks = new HookRegistry();
@@ -13695,7 +13749,7 @@ async function saveJson(filePath, data, options) {
13695
13749
  }
13696
13750
  await atomicWriteJson(filePath, data, { indent: options?.indent });
13697
13751
  Promise.resolve().then(() => (init_registry(), registry_exports)).then(
13698
- ({ hooks: h }) => h.dispatch("onFileChange", process.cwd(), {
13752
+ ({ hooks: h }) => h.dispatch("Notification", process.cwd(), {
13699
13753
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13700
13754
  filePath,
13701
13755
  changeType: "write"
@@ -20929,13 +20983,13 @@ var init_session_hooks = __esm({
20929
20983
  init_memory_bridge_refresh();
20930
20984
  hooks.register({
20931
20985
  id: "brain-session-start",
20932
- event: "onSessionStart",
20986
+ event: "SessionStart",
20933
20987
  handler: handleSessionStart,
20934
20988
  priority: 100
20935
20989
  });
20936
20990
  hooks.register({
20937
20991
  id: "brain-session-end",
20938
- event: "onSessionEnd",
20992
+ event: "SessionEnd",
20939
20993
  handler: handleSessionEnd,
20940
20994
  priority: 100
20941
20995
  });
@@ -20982,13 +21036,13 @@ var init_task_hooks = __esm({
20982
21036
  init_memory_bridge_refresh();
20983
21037
  hooks.register({
20984
21038
  id: "brain-tool-start",
20985
- event: "onToolStart",
21039
+ event: "PreToolUse",
20986
21040
  handler: handleToolStart,
20987
21041
  priority: 100
20988
21042
  });
20989
21043
  hooks.register({
20990
21044
  id: "brain-tool-complete",
20991
- event: "onToolComplete",
21045
+ event: "PostToolUse",
20992
21046
  handler: handleToolComplete,
20993
21047
  priority: 100
20994
21048
  });
@@ -21018,7 +21072,7 @@ var init_error_hooks = __esm({
21018
21072
  init_registry();
21019
21073
  hooks.register({
21020
21074
  id: "brain-error",
21021
- event: "onError",
21075
+ event: "PostToolUseFailure",
21022
21076
  handler: handleError,
21023
21077
  priority: 100
21024
21078
  });
@@ -21049,6 +21103,7 @@ async function isFileCaptureEnabled(projectRoot) {
21049
21103
  }
21050
21104
  }
21051
21105
  async function handleFileChange(projectRoot, payload) {
21106
+ if (!payload.filePath || !payload.changeType) return;
21052
21107
  if (!await isFileCaptureEnabled(projectRoot)) return;
21053
21108
  const now2 = Date.now();
21054
21109
  const lastWrite = recentWrites.get(payload.filePath);
@@ -21095,7 +21150,7 @@ var init_file_hooks = __esm({
21095
21150
  ];
21096
21151
  hooks.register({
21097
21152
  id: "brain-file-change",
21098
- event: "onFileChange",
21153
+ event: "Notification",
21099
21154
  handler: handleFileChange,
21100
21155
  priority: 100
21101
21156
  });
@@ -21149,22 +21204,51 @@ async function handleResponseComplete(projectRoot, payload) {
21149
21204
  if (!isMissingBrainSchemaError4(err)) throw err;
21150
21205
  }
21151
21206
  }
21207
+ async function handleSystemNotification(projectRoot, payload) {
21208
+ if (payload.filePath || payload.changeType) return;
21209
+ if (!payload.message) return;
21210
+ try {
21211
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21212
+ const config2 = await loadConfig4(projectRoot);
21213
+ if (!config2.brain?.autoCapture) return;
21214
+ } catch {
21215
+ return;
21216
+ }
21217
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21218
+ try {
21219
+ await observeBrain2(projectRoot, {
21220
+ text: `System notification: ${payload.message}`,
21221
+ title: `Notification: ${payload.message.slice(0, 60)}`,
21222
+ type: "discovery",
21223
+ sourceSessionId: payload.sessionId,
21224
+ sourceType: "agent"
21225
+ });
21226
+ } catch (err) {
21227
+ if (!isMissingBrainSchemaError4(err)) throw err;
21228
+ }
21229
+ }
21152
21230
  var init_mcp_hooks = __esm({
21153
21231
  "packages/core/src/hooks/handlers/mcp-hooks.ts"() {
21154
21232
  "use strict";
21155
21233
  init_registry();
21156
21234
  hooks.register({
21157
21235
  id: "brain-prompt-submit",
21158
- event: "onPromptSubmit",
21236
+ event: "PromptSubmit",
21159
21237
  handler: handlePromptSubmit,
21160
21238
  priority: 100
21161
21239
  });
21162
21240
  hooks.register({
21163
21241
  id: "brain-response-complete",
21164
- event: "onResponseComplete",
21242
+ event: "ResponseComplete",
21165
21243
  handler: handleResponseComplete,
21166
21244
  priority: 100
21167
21245
  });
21246
+ hooks.register({
21247
+ id: "brain-system-notification",
21248
+ event: "Notification",
21249
+ handler: handleSystemNotification,
21250
+ priority: 90
21251
+ });
21168
21252
  }
21169
21253
  });
21170
21254
 
@@ -21241,19 +21325,158 @@ var init_work_capture_hooks = __esm({
21241
21325
  ]);
21242
21326
  hooks.register({
21243
21327
  id: "work-capture-prompt-submit",
21244
- event: "onPromptSubmit",
21328
+ event: "PromptSubmit",
21245
21329
  handler: handleWorkPromptSubmit,
21246
21330
  priority: 90
21247
21331
  });
21248
21332
  hooks.register({
21249
21333
  id: "work-capture-response-complete",
21250
- event: "onResponseComplete",
21334
+ event: "ResponseComplete",
21251
21335
  handler: handleWorkResponseComplete,
21252
21336
  priority: 90
21253
21337
  });
21254
21338
  }
21255
21339
  });
21256
21340
 
21341
+ // packages/core/src/hooks/handlers/agent-hooks.ts
21342
+ function isMissingBrainSchemaError6(err) {
21343
+ if (!(err instanceof Error)) return false;
21344
+ const message = String(err.message || "").toLowerCase();
21345
+ return message.includes("no such table") && message.includes("brain_");
21346
+ }
21347
+ async function isAutoCaptureEnabled(projectRoot) {
21348
+ try {
21349
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21350
+ const config2 = await loadConfig4(projectRoot);
21351
+ return config2.brain?.autoCapture ?? false;
21352
+ } catch {
21353
+ return false;
21354
+ }
21355
+ }
21356
+ async function handleSubagentStart(projectRoot, payload) {
21357
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
21358
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21359
+ const rolePart = payload.role ? ` role=${payload.role}` : "";
21360
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
21361
+ try {
21362
+ await observeBrain2(projectRoot, {
21363
+ text: `Subagent spawned: ${payload.agentId}${rolePart}${taskPart}`,
21364
+ title: `Subagent start: ${payload.agentId}`,
21365
+ type: "discovery",
21366
+ sourceSessionId: payload.sessionId,
21367
+ sourceType: "agent"
21368
+ });
21369
+ } catch (err) {
21370
+ if (!isMissingBrainSchemaError6(err)) throw err;
21371
+ }
21372
+ }
21373
+ async function handleSubagentStop(projectRoot, payload) {
21374
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
21375
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21376
+ const statusPart = payload.status ? ` status=${payload.status}` : "";
21377
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
21378
+ const summaryPart = payload.summary ? `
21379
+ Summary: ${payload.summary}` : "";
21380
+ try {
21381
+ await observeBrain2(projectRoot, {
21382
+ text: `Subagent completed: ${payload.agentId}${statusPart}${taskPart}${summaryPart}`,
21383
+ title: `Subagent stop: ${payload.agentId}`,
21384
+ type: "change",
21385
+ sourceSessionId: payload.sessionId,
21386
+ sourceType: "agent"
21387
+ });
21388
+ } catch (err) {
21389
+ if (!isMissingBrainSchemaError6(err)) throw err;
21390
+ }
21391
+ }
21392
+ var init_agent_hooks = __esm({
21393
+ "packages/core/src/hooks/handlers/agent-hooks.ts"() {
21394
+ "use strict";
21395
+ init_registry();
21396
+ hooks.register({
21397
+ id: "brain-subagent-start",
21398
+ event: "SubagentStart",
21399
+ handler: handleSubagentStart,
21400
+ priority: 100
21401
+ });
21402
+ hooks.register({
21403
+ id: "brain-subagent-stop",
21404
+ event: "SubagentStop",
21405
+ handler: handleSubagentStop,
21406
+ priority: 100
21407
+ });
21408
+ }
21409
+ });
21410
+
21411
+ // packages/core/src/hooks/handlers/context-hooks.ts
21412
+ function isMissingBrainSchemaError7(err) {
21413
+ if (!(err instanceof Error)) return false;
21414
+ const message = String(err.message || "").toLowerCase();
21415
+ return message.includes("no such table") && message.includes("brain_");
21416
+ }
21417
+ async function isAutoCaptureEnabled2(projectRoot) {
21418
+ try {
21419
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21420
+ const config2 = await loadConfig4(projectRoot);
21421
+ return config2.brain?.autoCapture ?? false;
21422
+ } catch {
21423
+ return false;
21424
+ }
21425
+ }
21426
+ async function handlePreCompact(projectRoot, payload) {
21427
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
21428
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21429
+ const tokensPart = payload.tokensBefore != null ? ` (~${payload.tokensBefore.toLocaleString()} tokens)` : "";
21430
+ const reasonPart = payload.reason ? ` Reason: ${payload.reason}` : "";
21431
+ try {
21432
+ await observeBrain2(projectRoot, {
21433
+ text: `Context compaction about to begin${tokensPart}.${reasonPart}`,
21434
+ title: "Pre-compaction context snapshot",
21435
+ type: "discovery",
21436
+ sourceSessionId: payload.sessionId,
21437
+ sourceType: "agent"
21438
+ });
21439
+ } catch (err) {
21440
+ if (!isMissingBrainSchemaError7(err)) throw err;
21441
+ }
21442
+ }
21443
+ async function handlePostCompact(projectRoot, payload) {
21444
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
21445
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21446
+ const statusPart = payload.success ? "succeeded" : "failed";
21447
+ const beforePart = payload.tokensBefore != null ? ` before=${payload.tokensBefore.toLocaleString()}` : "";
21448
+ const afterPart = payload.tokensAfter != null ? ` after=${payload.tokensAfter.toLocaleString()}` : "";
21449
+ try {
21450
+ await observeBrain2(projectRoot, {
21451
+ text: `Context compaction ${statusPart}${beforePart}${afterPart}`,
21452
+ title: "Post-compaction record",
21453
+ type: "change",
21454
+ sourceSessionId: payload.sessionId,
21455
+ sourceType: "agent"
21456
+ });
21457
+ } catch (err) {
21458
+ if (!isMissingBrainSchemaError7(err)) throw err;
21459
+ }
21460
+ }
21461
+ var init_context_hooks = __esm({
21462
+ "packages/core/src/hooks/handlers/context-hooks.ts"() {
21463
+ "use strict";
21464
+ init_registry();
21465
+ hooks.register({
21466
+ id: "brain-pre-compact",
21467
+ event: "PreCompact",
21468
+ handler: handlePreCompact,
21469
+ priority: 100
21470
+ });
21471
+ hooks.register({
21472
+ id: "brain-post-compact",
21473
+ event: "PostCompact",
21474
+ handler: handlePostCompact,
21475
+ priority: 100
21476
+ });
21477
+ }
21478
+ });
21479
+
21257
21480
  // packages/core/src/hooks/handlers/index.ts
21258
21481
  var init_handlers = __esm({
21259
21482
  "packages/core/src/hooks/handlers/index.ts"() {
@@ -21264,6 +21487,10 @@ var init_handlers = __esm({
21264
21487
  init_file_hooks();
21265
21488
  init_mcp_hooks();
21266
21489
  init_work_capture_hooks();
21490
+ init_agent_hooks();
21491
+ init_context_hooks();
21492
+ init_agent_hooks();
21493
+ init_context_hooks();
21267
21494
  init_error_hooks();
21268
21495
  init_file_hooks();
21269
21496
  init_mcp_hooks();
@@ -23114,7 +23341,7 @@ async function startSession(options, cwd, accessor) {
23114
23341
  });
23115
23342
  }
23116
23343
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
23117
- hooks2.dispatch("onSessionStart", cwd ?? process.cwd(), {
23344
+ hooks2.dispatch("SessionStart", cwd ?? process.cwd(), {
23118
23345
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23119
23346
  sessionId: session.id,
23120
23347
  name: options.name,
@@ -23152,7 +23379,7 @@ async function endSession(options = {}, cwd, accessor) {
23152
23379
  session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
23153
23380
  const duration3 = Math.floor((Date.now() - new Date(session.startedAt).getTime()) / 1e3);
23154
23381
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
23155
- hooks2.dispatch("onSessionEnd", cwd ?? process.cwd(), {
23382
+ hooks2.dispatch("SessionEnd", cwd ?? process.cwd(), {
23156
23383
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23157
23384
  sessionId: session.id,
23158
23385
  duration: duration3,
@@ -35888,7 +36115,7 @@ function validatePayload(event, payload) {
35888
36115
  const errors = result.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`);
35889
36116
  return { valid: false, errors };
35890
36117
  }
35891
- var HookPayloadSchema, OnSessionStartPayloadSchema, OnSessionEndPayloadSchema, OnToolStartPayloadSchema, OnToolCompletePayloadSchema, OnFileChangePayloadSchema, OnErrorPayloadSchema, OnPromptSubmitPayloadSchema, OnResponseCompletePayloadSchema, OnWorkAvailablePayloadSchema, OnAgentSpawnPayloadSchema, OnAgentCompletePayloadSchema, OnCascadeStartPayloadSchema, OnPatrolPayloadSchema, EVENT_SCHEMA_MAP;
36118
+ var HookPayloadSchema, SessionStartPayloadSchema, OnSessionStartPayloadSchema, SessionEndPayloadSchema, OnSessionEndPayloadSchema, PreToolUsePayloadSchema, OnToolStartPayloadSchema, PostToolUsePayloadSchema, OnToolCompletePayloadSchema, NotificationPayloadSchema, OnFileChangePayloadSchema, PostToolUseFailurePayloadSchema, OnErrorPayloadSchema, PromptSubmitPayloadSchema, OnPromptSubmitPayloadSchema, ResponseCompletePayloadSchema, OnResponseCompletePayloadSchema, SubagentStartPayloadSchema, SubagentStopPayloadSchema, PreCompactPayloadSchema, PostCompactPayloadSchema, ConfigChangePayloadSchema, OnWorkAvailablePayloadSchema, OnAgentSpawnPayloadSchema, OnAgentCompletePayloadSchema, OnCascadeStartPayloadSchema, OnPatrolPayloadSchema, EVENT_SCHEMA_MAP;
35892
36119
  var init_payload_schemas = __esm({
35893
36120
  "packages/core/src/hooks/payload-schemas.ts"() {
35894
36121
  "use strict";
@@ -35900,33 +36127,42 @@ var init_payload_schemas = __esm({
35900
36127
  providerId: external_exports.string().optional(),
35901
36128
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35902
36129
  });
35903
- OnSessionStartPayloadSchema = HookPayloadSchema.extend({
36130
+ SessionStartPayloadSchema = HookPayloadSchema.extend({
35904
36131
  sessionId: external_exports.string(),
35905
36132
  name: external_exports.string(),
35906
36133
  scope: external_exports.string(),
35907
36134
  agent: external_exports.string().optional()
35908
36135
  });
35909
- OnSessionEndPayloadSchema = HookPayloadSchema.extend({
36136
+ OnSessionStartPayloadSchema = SessionStartPayloadSchema;
36137
+ SessionEndPayloadSchema = HookPayloadSchema.extend({
35910
36138
  sessionId: external_exports.string(),
35911
36139
  duration: external_exports.number(),
35912
36140
  tasksCompleted: external_exports.array(external_exports.string())
35913
36141
  });
35914
- OnToolStartPayloadSchema = HookPayloadSchema.extend({
36142
+ OnSessionEndPayloadSchema = SessionEndPayloadSchema;
36143
+ PreToolUsePayloadSchema = HookPayloadSchema.extend({
35915
36144
  taskId: external_exports.string(),
35916
36145
  taskTitle: external_exports.string(),
35917
- previousTask: external_exports.string().optional()
36146
+ previousTask: external_exports.string().optional(),
36147
+ toolName: external_exports.string().optional(),
36148
+ toolInput: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35918
36149
  });
35919
- OnToolCompletePayloadSchema = HookPayloadSchema.extend({
36150
+ OnToolStartPayloadSchema = PreToolUsePayloadSchema;
36151
+ PostToolUsePayloadSchema = HookPayloadSchema.extend({
35920
36152
  taskId: external_exports.string(),
35921
36153
  taskTitle: external_exports.string(),
35922
- status: external_exports.enum(["done", "archived", "cancelled"])
36154
+ status: external_exports.enum(["done", "archived", "cancelled"]),
36155
+ toolResult: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35923
36156
  });
35924
- OnFileChangePayloadSchema = HookPayloadSchema.extend({
35925
- filePath: external_exports.string(),
35926
- changeType: external_exports.enum(["write", "create", "delete"]),
35927
- sizeBytes: external_exports.number().optional()
36157
+ OnToolCompletePayloadSchema = PostToolUsePayloadSchema;
36158
+ NotificationPayloadSchema = HookPayloadSchema.extend({
36159
+ filePath: external_exports.string().optional(),
36160
+ changeType: external_exports.enum(["write", "create", "delete"]).optional(),
36161
+ sizeBytes: external_exports.number().optional(),
36162
+ message: external_exports.string().optional()
35928
36163
  });
35929
- OnErrorPayloadSchema = HookPayloadSchema.extend({
36164
+ OnFileChangePayloadSchema = NotificationPayloadSchema;
36165
+ PostToolUseFailurePayloadSchema = HookPayloadSchema.extend({
35930
36166
  errorCode: external_exports.union([external_exports.number(), external_exports.string()]),
35931
36167
  message: external_exports.string(),
35932
36168
  domain: external_exports.string().optional(),
@@ -35934,13 +36170,15 @@ var init_payload_schemas = __esm({
35934
36170
  gateway: external_exports.string().optional(),
35935
36171
  stack: external_exports.string().optional()
35936
36172
  });
35937
- OnPromptSubmitPayloadSchema = HookPayloadSchema.extend({
36173
+ OnErrorPayloadSchema = PostToolUseFailurePayloadSchema;
36174
+ PromptSubmitPayloadSchema = HookPayloadSchema.extend({
35938
36175
  gateway: external_exports.string(),
35939
36176
  domain: external_exports.string(),
35940
36177
  operation: external_exports.string(),
35941
36178
  source: external_exports.string().optional()
35942
36179
  });
35943
- OnResponseCompletePayloadSchema = HookPayloadSchema.extend({
36180
+ OnPromptSubmitPayloadSchema = PromptSubmitPayloadSchema;
36181
+ ResponseCompletePayloadSchema = HookPayloadSchema.extend({
35944
36182
  gateway: external_exports.string(),
35945
36183
  domain: external_exports.string(),
35946
36184
  operation: external_exports.string(),
@@ -35948,6 +36186,32 @@ var init_payload_schemas = __esm({
35948
36186
  durationMs: external_exports.number().optional(),
35949
36187
  errorCode: external_exports.string().optional()
35950
36188
  });
36189
+ OnResponseCompletePayloadSchema = ResponseCompletePayloadSchema;
36190
+ SubagentStartPayloadSchema = HookPayloadSchema.extend({
36191
+ agentId: external_exports.string(),
36192
+ role: external_exports.string().optional(),
36193
+ taskId: external_exports.string().optional()
36194
+ });
36195
+ SubagentStopPayloadSchema = HookPayloadSchema.extend({
36196
+ agentId: external_exports.string(),
36197
+ status: external_exports.enum(["complete", "partial", "blocked", "failed"]).optional(),
36198
+ taskId: external_exports.string().optional(),
36199
+ summary: external_exports.string().optional()
36200
+ });
36201
+ PreCompactPayloadSchema = HookPayloadSchema.extend({
36202
+ tokensBefore: external_exports.number().optional(),
36203
+ reason: external_exports.string().optional()
36204
+ });
36205
+ PostCompactPayloadSchema = HookPayloadSchema.extend({
36206
+ tokensBefore: external_exports.number().optional(),
36207
+ tokensAfter: external_exports.number().optional(),
36208
+ success: external_exports.boolean()
36209
+ });
36210
+ ConfigChangePayloadSchema = HookPayloadSchema.extend({
36211
+ key: external_exports.string(),
36212
+ previousValue: external_exports.unknown().optional(),
36213
+ newValue: external_exports.unknown().optional()
36214
+ });
35951
36215
  OnWorkAvailablePayloadSchema = HookPayloadSchema.extend({
35952
36216
  taskIds: external_exports.array(external_exports.string()),
35953
36217
  epicId: external_exports.string().optional(),
@@ -35979,14 +36243,21 @@ var init_payload_schemas = __esm({
35979
36243
  scope: external_exports.string().optional()
35980
36244
  });
35981
36245
  EVENT_SCHEMA_MAP = {
35982
- onSessionStart: OnSessionStartPayloadSchema,
35983
- onSessionEnd: OnSessionEndPayloadSchema,
35984
- onToolStart: OnToolStartPayloadSchema,
35985
- onToolComplete: OnToolCompletePayloadSchema,
35986
- onFileChange: OnFileChangePayloadSchema,
35987
- onError: OnErrorPayloadSchema,
35988
- onPromptSubmit: OnPromptSubmitPayloadSchema,
35989
- onResponseComplete: OnResponseCompletePayloadSchema,
36246
+ // CAAMP canonical events (16)
36247
+ SessionStart: SessionStartPayloadSchema,
36248
+ SessionEnd: SessionEndPayloadSchema,
36249
+ PreToolUse: PreToolUsePayloadSchema,
36250
+ PostToolUse: PostToolUsePayloadSchema,
36251
+ Notification: NotificationPayloadSchema,
36252
+ PostToolUseFailure: PostToolUseFailurePayloadSchema,
36253
+ PromptSubmit: PromptSubmitPayloadSchema,
36254
+ ResponseComplete: ResponseCompletePayloadSchema,
36255
+ SubagentStart: SubagentStartPayloadSchema,
36256
+ SubagentStop: SubagentStopPayloadSchema,
36257
+ PreCompact: PreCompactPayloadSchema,
36258
+ PostCompact: PostCompactPayloadSchema,
36259
+ ConfigChange: ConfigChangePayloadSchema,
36260
+ // CLEO internal coordination events (5)
35990
36261
  onWorkAvailable: OnWorkAvailablePayloadSchema,
35991
36262
  onAgentSpawn: OnAgentSpawnPayloadSchema,
35992
36263
  onAgentComplete: OnAgentCompletePayloadSchema,
@@ -36016,10 +36287,15 @@ __export(hooks_exports, {
36016
36287
  OnWorkAvailablePayloadSchema: () => OnWorkAvailablePayloadSchema,
36017
36288
  handleError: () => handleError,
36018
36289
  handleFileChange: () => handleFileChange,
36290
+ handlePostCompact: () => handlePostCompact,
36291
+ handlePreCompact: () => handlePreCompact,
36019
36292
  handlePromptSubmit: () => handlePromptSubmit,
36020
36293
  handleResponseComplete: () => handleResponseComplete,
36021
36294
  handleSessionEnd: () => handleSessionEnd,
36022
36295
  handleSessionStart: () => handleSessionStart,
36296
+ handleSubagentStart: () => handleSubagentStart,
36297
+ handleSubagentStop: () => handleSubagentStop,
36298
+ handleSystemNotification: () => handleSystemNotification,
36023
36299
  handleToolComplete: () => handleToolComplete,
36024
36300
  handleToolStart: () => handleToolStart,
36025
36301
  handleWorkPromptSubmit: () => handleWorkPromptSubmit,
@@ -62451,7 +62727,7 @@ async function startTask(taskId, cwd, accessor) {
62451
62727
  accessor
62452
62728
  );
62453
62729
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
62454
- hooks2.dispatch("onToolStart", cwd ?? process.cwd(), {
62730
+ hooks2.dispatch("PreToolUse", cwd ?? process.cwd(), {
62455
62731
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
62456
62732
  taskId,
62457
62733
  taskTitle: task.title
@@ -62477,7 +62753,7 @@ async function stopTask(cwd, accessor) {
62477
62753
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
62478
62754
  if (taskId && task) {
62479
62755
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
62480
- hooks2.dispatch("onToolComplete", cwd ?? process.cwd(), {
62756
+ hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
62481
62757
  timestamp: now2,
62482
62758
  taskId,
62483
62759
  taskTitle: task.title,
@@ -67884,6 +68160,162 @@ var init_bootstrap = __esm({
67884
68160
  }
67885
68161
  });
67886
68162
 
68163
+ // packages/core/src/sessions/snapshot.ts
68164
+ async function serializeSession(projectRoot, options = {}, accessor) {
68165
+ const acc = accessor ?? await getAccessor(projectRoot);
68166
+ const maxObs = options.maxObservations ?? 10;
68167
+ const maxDescLen = options.maxDescriptionLength ?? 500;
68168
+ const sessions2 = await acc.loadSessions();
68169
+ let session;
68170
+ if (options.sessionId) {
68171
+ session = sessions2.find((s) => s.id === options.sessionId);
68172
+ } else {
68173
+ session = sessions2.filter((s) => s.status === "active").sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())[0];
68174
+ }
68175
+ if (!session) {
68176
+ throw new CleoError(
68177
+ 31 /* SESSION_NOT_FOUND */,
68178
+ options.sessionId ? `Session '${options.sessionId}' not found` : "No active session to serialize",
68179
+ { fix: "Use 'cleo session list' to see available sessions" }
68180
+ );
68181
+ }
68182
+ const handoff = await computeHandoff(projectRoot, { sessionId: session.id });
68183
+ const decisionLog = await getDecisionLog(projectRoot, { sessionId: session.id });
68184
+ const decisions = decisionLog.map((d) => ({
68185
+ decision: d.decision,
68186
+ rationale: d.rationale,
68187
+ taskId: d.taskId,
68188
+ recordedAt: d.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
68189
+ }));
68190
+ let observations = [];
68191
+ try {
68192
+ const { searchBrainCompact: searchBrainCompact2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
68193
+ const results = await searchBrainCompact2(projectRoot, {
68194
+ query: session.id,
68195
+ limit: maxObs,
68196
+ tables: ["observations"]
68197
+ });
68198
+ if (Array.isArray(results)) {
68199
+ observations = results.map(
68200
+ (r) => ({
68201
+ id: r.id,
68202
+ text: r.text ?? "",
68203
+ type: r.type ?? "discovery",
68204
+ createdAt: r.createdAt ?? ""
68205
+ })
68206
+ );
68207
+ }
68208
+ } catch {
68209
+ }
68210
+ let activeTask = null;
68211
+ if (session.taskWork?.taskId) {
68212
+ try {
68213
+ const { tasks: tasks2 } = await acc.queryTasks({});
68214
+ const task = tasks2.find((t) => t.id === session.taskWork?.taskId);
68215
+ if (task) {
68216
+ const desc7 = task.description ?? "";
68217
+ activeTask = {
68218
+ taskId: task.id,
68219
+ title: task.title,
68220
+ status: task.status,
68221
+ priority: task.priority ?? "medium",
68222
+ description: desc7.length > maxDescLen ? desc7.slice(0, maxDescLen) + "..." : desc7,
68223
+ acceptance: Array.isArray(task.acceptance) ? task.acceptance.join("\n") : task.acceptance ?? void 0
68224
+ };
68225
+ }
68226
+ } catch {
68227
+ }
68228
+ }
68229
+ const startTime = new Date(session.startedAt).getTime();
68230
+ const now2 = Date.now();
68231
+ const durationMinutes = Math.round((now2 - startTime) / 6e4);
68232
+ return {
68233
+ version: SNAPSHOT_VERSION,
68234
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
68235
+ session,
68236
+ handoff,
68237
+ decisions,
68238
+ observations,
68239
+ activeTask,
68240
+ durationMinutes
68241
+ };
68242
+ }
68243
+ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
68244
+ if (snapshot.version > SNAPSHOT_VERSION) {
68245
+ throw new CleoError(
68246
+ 6 /* VALIDATION_ERROR */,
68247
+ `Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION}`,
68248
+ { fix: "Upgrade @cleocode/core to a newer version that supports this snapshot format" }
68249
+ );
68250
+ }
68251
+ const acc = accessor ?? await getAccessor(projectRoot);
68252
+ const activate = options.activate ?? true;
68253
+ if (activate) {
68254
+ const sessions2 = await acc.loadSessions();
68255
+ const scope = snapshot.session.scope;
68256
+ const activeConflict = sessions2.find(
68257
+ (s) => s.status === "active" && s.scope.type === scope.type && s.scope.epicId === scope.epicId && s.id !== snapshot.session.id
68258
+ );
68259
+ if (activeConflict) {
68260
+ throw new CleoError(
68261
+ 32 /* SCOPE_CONFLICT */,
68262
+ `Active session '${activeConflict.id}' already exists for scope ${scope.type}${scope.epicId ? ":" + scope.epicId : ""}`,
68263
+ {
68264
+ fix: `End the active session first with 'cleo session end' or restore without activating`,
68265
+ alternatives: [
68266
+ { action: "End conflicting session", command: "cleo session end" },
68267
+ { action: "Restore without activating", command: "Restore with activate: false" }
68268
+ ]
68269
+ }
68270
+ );
68271
+ }
68272
+ }
68273
+ const restoredSession = {
68274
+ ...snapshot.session,
68275
+ status: activate ? "active" : snapshot.session.status,
68276
+ notes: [
68277
+ ...snapshot.session.notes ?? [],
68278
+ `Restored from snapshot at ${(/* @__PURE__ */ new Date()).toISOString()} (captured ${snapshot.capturedAt}, duration ${snapshot.durationMinutes}m)`
68279
+ ],
68280
+ resumeCount: (snapshot.session.resumeCount ?? 0) + 1
68281
+ };
68282
+ if (options.agent) {
68283
+ restoredSession.agent = options.agent;
68284
+ restoredSession.notes = [
68285
+ ...restoredSession.notes ?? [],
68286
+ `Agent handoff: ${snapshot.session.agent ?? "unknown"} \u2192 ${options.agent}`
68287
+ ];
68288
+ }
68289
+ restoredSession.handoffJson = JSON.stringify(snapshot.handoff);
68290
+ await acc.upsertSingleSession(restoredSession);
68291
+ try {
68292
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
68293
+ await hooks2.dispatch("SessionStart", projectRoot, {
68294
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
68295
+ sessionId: restoredSession.id,
68296
+ name: restoredSession.name,
68297
+ scope: restoredSession.scope,
68298
+ agent: restoredSession.agent,
68299
+ restored: true,
68300
+ snapshotCapturedAt: snapshot.capturedAt
68301
+ });
68302
+ } catch {
68303
+ }
68304
+ return restoredSession;
68305
+ }
68306
+ var SNAPSHOT_VERSION;
68307
+ var init_snapshot2 = __esm({
68308
+ "packages/core/src/sessions/snapshot.ts"() {
68309
+ "use strict";
68310
+ init_src();
68311
+ init_errors3();
68312
+ init_data_accessor();
68313
+ init_decisions();
68314
+ init_handoff();
68315
+ SNAPSHOT_VERSION = 1;
68316
+ }
68317
+ });
68318
+
67887
68319
  // packages/core/src/cleo.ts
67888
68320
  import path from "node:path";
67889
68321
  var Cleo;
@@ -67906,6 +68338,7 @@ var init_cleo = __esm({
67906
68338
  init_link_store();
67907
68339
  init_release2();
67908
68340
  init_sessions();
68341
+ init_snapshot2();
67909
68342
  init_sticky();
67910
68343
  init_data_accessor();
67911
68344
  init_task_work();
@@ -68015,7 +68448,15 @@ var init_cleo = __esm({
68015
68448
  recordAssumption: (p) => recordAssumption(root, p),
68016
68449
  contextDrift: (p) => getContextDrift(root, p),
68017
68450
  decisionLog: (p) => getDecisionLog(root, { sessionId: p?.sessionId, taskId: p?.taskId }),
68018
- lastHandoff: (scope) => getLastHandoff(root, scope)
68451
+ lastHandoff: (scope) => getLastHandoff(root, scope),
68452
+ serialize: (p) => serializeSession(root, {
68453
+ sessionId: p?.sessionId,
68454
+ maxObservations: p?.maxObservations
68455
+ }),
68456
+ restore: (snapshot, p) => restoreSession(root, snapshot, {
68457
+ agent: p?.agent,
68458
+ activate: p?.activate
68459
+ })
68019
68460
  };
68020
68461
  }
68021
68462
  // === Memory ===
@@ -68371,6 +68812,7 @@ __export(src_exports, {
68371
68812
  remote: () => remote_exports,
68372
68813
  research: () => research_exports,
68373
68814
  resolveProjectPath: () => resolveProjectPath,
68815
+ restoreSession: () => restoreSession,
68374
68816
  resumeSession: () => resumeSession,
68375
68817
  roadmap: () => roadmap_exports,
68376
68818
  routing: () => routing_exports,
@@ -68387,6 +68829,7 @@ __export(src_exports, {
68387
68829
  selectSessionSchema: () => selectSessionSchema,
68388
68830
  selectTaskSchema: () => selectTaskSchema,
68389
68831
  sequence: () => sequence_exports,
68832
+ serializeSession: () => serializeSession,
68390
68833
  sessionStatus: () => sessionStatus,
68391
68834
  sessionStatusSchema: () => sessionStatusSchema,
68392
68835
  sessions: () => sessions_exports,
@@ -68497,6 +68940,7 @@ var init_src2 = __esm({
68497
68940
  init_migration();
68498
68941
  init_reconciliation();
68499
68942
  init_sessions();
68943
+ init_snapshot2();
68500
68944
  init_migrate();
68501
68945
  init_storage_preflight();
68502
68946
  init_task_work();
@@ -69671,6 +70115,14 @@ var init_protocol_enforcement = __esm({
69671
70115
  });
69672
70116
 
69673
70117
  // packages/core/src/hooks/types.ts
70118
+ import {
70119
+ buildHookMatrix,
70120
+ CANONICAL_HOOK_EVENTS,
70121
+ HOOK_CATEGORIES,
70122
+ supportsHook,
70123
+ toCanonical,
70124
+ toNative
70125
+ } from "@cleocode/caamp";
69674
70126
  import { getCommonHookEvents, getProvidersByHookEvent } from "@cleocode/caamp";
69675
70127
  function isProviderHookEvent(event) {
69676
70128
  return !INTERNAL_HOOK_EVENT_SET.has(event);
@@ -79745,6 +80197,7 @@ __export(internal_exports, {
79745
80197
  resolveTask: () => resolveTask,
79746
80198
  restoreBackup: () => restoreBackup,
79747
80199
  restoreFromBackup: () => restoreFromBackup,
80200
+ restoreSession: () => restoreSession,
79748
80201
  resumeSession: () => resumeSession,
79749
80202
  roadmap: () => roadmap_exports,
79750
80203
  rollbackRelease: () => rollbackRelease,
@@ -79772,6 +80225,7 @@ __export(internal_exports, {
79772
80225
  selectSessionSchema: () => selectSessionSchema,
79773
80226
  selectTaskSchema: () => selectTaskSchema,
79774
80227
  sequence: () => sequence_exports,
80228
+ serializeSession: () => serializeSession,
79775
80229
  sessionStatus: () => sessionStatus,
79776
80230
  sessionStatusSchema: () => sessionStatusSchema,
79777
80231
  sessions: () => sessions_exports,
@@ -83405,6 +83859,30 @@ var init_registry5 = __esm({
83405
83859
  sessionRequired: false,
83406
83860
  requiredParams: []
83407
83861
  },
83862
+ {
83863
+ gateway: "query",
83864
+ domain: "admin",
83865
+ operation: "hooks.matrix",
83866
+ description: "admin.hooks.matrix (query) \u2014 cross-provider hook support matrix using CAAMP canonical taxonomy",
83867
+ tier: 1,
83868
+ idempotent: true,
83869
+ sessionRequired: false,
83870
+ requiredParams: [],
83871
+ params: [
83872
+ {
83873
+ name: "providerIds",
83874
+ type: "string",
83875
+ required: false,
83876
+ description: "Limit matrix to specific provider IDs (default: all mapped providers)"
83877
+ },
83878
+ {
83879
+ name: "detectProvider",
83880
+ type: "boolean",
83881
+ required: false,
83882
+ description: "Detect current runtime provider (default: true)"
83883
+ }
83884
+ ]
83885
+ },
83408
83886
  {
83409
83887
  gateway: "query",
83410
83888
  domain: "admin",
@@ -84229,6 +84707,114 @@ var init_config_engine = __esm({
84229
84707
  }
84230
84708
  });
84231
84709
 
84710
+ // packages/cleo/src/dispatch/engines/hooks-engine.ts
84711
+ var hooks_engine_exports = {};
84712
+ __export(hooks_engine_exports, {
84713
+ queryCommonHooks: () => queryCommonHooks,
84714
+ queryHookProviders: () => queryHookProviders,
84715
+ systemHooksMatrix: () => systemHooksMatrix
84716
+ });
84717
+ async function queryHookProviders(event) {
84718
+ if (!isProviderHookEvent(event)) {
84719
+ return engineSuccess({
84720
+ event,
84721
+ providers: []
84722
+ });
84723
+ }
84724
+ const { getProvidersByHookEvent: getProvidersByHookEvent2 } = await import("@cleocode/caamp");
84725
+ const providers = getProvidersByHookEvent2(event);
84726
+ return engineSuccess({
84727
+ event,
84728
+ providers: providers.map((p) => ({
84729
+ id: p.id,
84730
+ name: p.name,
84731
+ supportedHooks: p.capabilities?.hooks?.supported ?? []
84732
+ }))
84733
+ });
84734
+ }
84735
+ async function queryCommonHooks(providerIds) {
84736
+ const { getCommonHookEvents: getCommonHookEvents2 } = await import("@cleocode/caamp");
84737
+ const commonEvents = getCommonHookEvents2(providerIds);
84738
+ return engineSuccess({
84739
+ providerIds,
84740
+ commonEvents
84741
+ });
84742
+ }
84743
+ async function systemHooksMatrix(params) {
84744
+ try {
84745
+ const { buildHookMatrix: buildHookMatrix2, getProviderSummary, getHookMappingsVersion, detectAllProviders: detectAllProviders3 } = await import("@cleocode/caamp");
84746
+ const caampVersion = getHookMappingsVersion();
84747
+ const raw = buildHookMatrix2(params?.providerIds);
84748
+ const boolMatrix = {};
84749
+ for (const event of raw.events) {
84750
+ boolMatrix[event] = {};
84751
+ for (const providerId of raw.providers) {
84752
+ const mapping = raw.matrix[event]?.[providerId];
84753
+ boolMatrix[event][providerId] = mapping?.supported ?? false;
84754
+ }
84755
+ }
84756
+ const summary = raw.providers.map((providerId) => {
84757
+ const provSummary = getProviderSummary(providerId);
84758
+ if (provSummary) {
84759
+ return {
84760
+ providerId,
84761
+ supportedCount: provSummary.supportedCount,
84762
+ totalCanonical: provSummary.totalCanonical,
84763
+ coverage: provSummary.coverage,
84764
+ supported: provSummary.supported,
84765
+ unsupported: provSummary.unsupported
84766
+ };
84767
+ }
84768
+ const supported = raw.events.filter((ev) => boolMatrix[ev]?.[providerId] === true);
84769
+ const unsupported = raw.events.filter((ev) => boolMatrix[ev]?.[providerId] !== true);
84770
+ const totalCanonical = raw.events.length;
84771
+ const coverage = totalCanonical > 0 ? Math.round(supported.length / totalCanonical * 100) : 0;
84772
+ return {
84773
+ providerId,
84774
+ supportedCount: supported.length,
84775
+ totalCanonical,
84776
+ coverage,
84777
+ supported,
84778
+ unsupported
84779
+ };
84780
+ });
84781
+ let detectedProvider = null;
84782
+ const shouldDetect = params?.detectProvider !== false;
84783
+ if (shouldDetect) {
84784
+ try {
84785
+ const detectionResults = detectAllProviders3();
84786
+ const detected = detectionResults.find((r) => r.installed && r.projectDetected);
84787
+ if (detected) {
84788
+ detectedProvider = detected.provider.id;
84789
+ } else {
84790
+ const anyInstalled = detectionResults.find((r) => r.installed);
84791
+ if (anyInstalled) {
84792
+ detectedProvider = anyInstalled.provider.id;
84793
+ }
84794
+ }
84795
+ } catch {
84796
+ }
84797
+ }
84798
+ return engineSuccess({
84799
+ caampVersion,
84800
+ events: raw.events,
84801
+ providers: raw.providers,
84802
+ matrix: boolMatrix,
84803
+ summary,
84804
+ detectedProvider
84805
+ });
84806
+ } catch (err) {
84807
+ return engineError("E_GENERAL", err.message);
84808
+ }
84809
+ }
84810
+ var init_hooks_engine = __esm({
84811
+ "packages/cleo/src/dispatch/engines/hooks-engine.ts"() {
84812
+ "use strict";
84813
+ init_internal();
84814
+ init_error();
84815
+ }
84816
+ });
84817
+
84232
84818
  // packages/cleo/src/dispatch/engines/init-engine.ts
84233
84819
  async function initProject2(projectRoot, options) {
84234
84820
  try {
@@ -87851,6 +88437,7 @@ var init_engine2 = __esm({
87851
88437
  init_internal();
87852
88438
  init_codebase_map_engine();
87853
88439
  init_config_engine();
88440
+ init_hooks_engine();
87854
88441
  init_init_engine();
87855
88442
  init_lifecycle_engine();
87856
88443
  init_memory_engine();
@@ -88379,6 +88966,13 @@ var init_admin2 = __esm({
88379
88966
  const result = await systemSmoke();
88380
88967
  return wrapResult(result, "query", "admin", operation, startTime);
88381
88968
  }
88969
+ case "hooks.matrix": {
88970
+ const result = await systemHooksMatrix({
88971
+ providerIds: params?.providerIds,
88972
+ detectProvider: params?.detectProvider !== false
88973
+ });
88974
+ return wrapResult(result, "query", "admin", operation, startTime);
88975
+ }
88382
88976
  default:
88383
88977
  return unsupportedOp("query", "admin", operation, startTime);
88384
88978
  }
@@ -88863,7 +89457,8 @@ var init_admin2 = __esm({
88863
89457
  "backup",
88864
89458
  "export",
88865
89459
  "map",
88866
- "smoke"
89460
+ "smoke",
89461
+ "hooks.matrix"
88867
89462
  ],
88868
89463
  mutate: [
88869
89464
  "init",
@@ -92595,46 +93190,6 @@ var init_tasks4 = __esm({
92595
93190
  }
92596
93191
  });
92597
93192
 
92598
- // packages/cleo/src/dispatch/engines/hooks-engine.ts
92599
- var hooks_engine_exports = {};
92600
- __export(hooks_engine_exports, {
92601
- queryCommonHooks: () => queryCommonHooks,
92602
- queryHookProviders: () => queryHookProviders
92603
- });
92604
- async function queryHookProviders(event) {
92605
- if (!isProviderHookEvent(event)) {
92606
- return engineSuccess({
92607
- event,
92608
- providers: []
92609
- });
92610
- }
92611
- const { getProvidersByHookEvent: getProvidersByHookEvent2 } = await import("@cleocode/caamp");
92612
- const providers = getProvidersByHookEvent2(event);
92613
- return engineSuccess({
92614
- event,
92615
- providers: providers.map((p) => ({
92616
- id: p.id,
92617
- name: p.name,
92618
- supportedHooks: p.capabilities?.hooks?.supported ?? []
92619
- }))
92620
- });
92621
- }
92622
- async function queryCommonHooks(providerIds) {
92623
- const { getCommonHookEvents: getCommonHookEvents2 } = await import("@cleocode/caamp");
92624
- const commonEvents = getCommonHookEvents2(providerIds);
92625
- return engineSuccess({
92626
- providerIds,
92627
- commonEvents
92628
- });
92629
- }
92630
- var init_hooks_engine = __esm({
92631
- "packages/cleo/src/dispatch/engines/hooks-engine.ts"() {
92632
- "use strict";
92633
- init_internal();
92634
- init_error();
92635
- }
92636
- });
92637
-
92638
93193
  // packages/cleo/src/dispatch/engines/tools-engine.ts
92639
93194
  import {
92640
93195
  buildInjectionContent,
@@ -94224,7 +94779,7 @@ async function dispatchFromCli(gateway, domain2, operation, params, outputOpts)
94224
94779
  const dispatcher = getCliDispatcher();
94225
94780
  const projectRoot = getProjectRoot();
94226
94781
  const dispatchStart = Date.now();
94227
- hooks.dispatch("onPromptSubmit", projectRoot, {
94782
+ hooks.dispatch("PromptSubmit", projectRoot, {
94228
94783
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94229
94784
  gateway,
94230
94785
  domain: domain2,
@@ -94240,7 +94795,7 @@ async function dispatchFromCli(gateway, domain2, operation, params, outputOpts)
94240
94795
  source: "cli",
94241
94796
  requestId: randomUUID10()
94242
94797
  });
94243
- hooks.dispatch("onResponseComplete", projectRoot, {
94798
+ hooks.dispatch("ResponseComplete", projectRoot, {
94244
94799
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94245
94800
  gateway,
94246
94801
  domain: domain2,
@@ -94297,7 +94852,7 @@ async function dispatchRaw(gateway, domain2, operation, params) {
94297
94852
  const dispatcher = getCliDispatcher();
94298
94853
  const projectRoot = getProjectRoot();
94299
94854
  const dispatchStart = Date.now();
94300
- hooks.dispatch("onPromptSubmit", projectRoot, {
94855
+ hooks.dispatch("PromptSubmit", projectRoot, {
94301
94856
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94302
94857
  gateway,
94303
94858
  domain: domain2,
@@ -94313,7 +94868,7 @@ async function dispatchRaw(gateway, domain2, operation, params) {
94313
94868
  source: "cli",
94314
94869
  requestId: randomUUID10()
94315
94870
  });
94316
- hooks.dispatch("onResponseComplete", projectRoot, {
94871
+ hooks.dispatch("ResponseComplete", projectRoot, {
94317
94872
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94318
94873
  gateway,
94319
94874
  domain: domain2,
@@ -96684,15 +97239,84 @@ function createUpgradeProgress(enabled) {
96684
97239
  }
96685
97240
 
96686
97241
  // packages/cleo/src/cli/commands/doctor.ts
97242
+ function renderHookMatrixHuman(data) {
97243
+ const { events, providers, matrix, summary, caampVersion, detectedProvider } = data;
97244
+ process.stdout.write(`
97245
+ Provider Hook Matrix (CAAMP ${caampVersion} canonical taxonomy)
97246
+
97247
+ `);
97248
+ if (detectedProvider) {
97249
+ process.stdout.write(`Detected provider: ${detectedProvider}
97250
+
97251
+ `);
97252
+ }
97253
+ if (providers.length === 0) {
97254
+ process.stdout.write("No providers found in CAAMP registry.\n");
97255
+ return;
97256
+ }
97257
+ const EVENT_COL = Math.max(...events.map((e) => e.length), "Event".length);
97258
+ const provCols = providers.map((p) => Math.max(p.length, 5));
97259
+ const headerParts = [
97260
+ "Event".padEnd(EVENT_COL),
97261
+ ...providers.map((p, i) => p.padEnd(provCols[i]))
97262
+ ];
97263
+ process.stdout.write(` ${headerParts.join(" ")}
97264
+ `);
97265
+ const sepParts = ["-".repeat(EVENT_COL), ...provCols.map((w) => "-".repeat(w))];
97266
+ process.stdout.write(` ${sepParts.join(" ")}
97267
+ `);
97268
+ for (const event of events) {
97269
+ const cells = providers.map((p, i) => {
97270
+ const supported = matrix[event]?.[p] === true;
97271
+ const symbol2 = supported ? "\u2713" : "-";
97272
+ return symbol2.padEnd(provCols[i]);
97273
+ });
97274
+ process.stdout.write(` ${event.padEnd(EVENT_COL)} ${cells.join(" ")}
97275
+ `);
97276
+ }
97277
+ process.stdout.write("\n");
97278
+ const coverageParts = summary.map(
97279
+ (s) => `${s.providerId} ${s.supportedCount}/${s.totalCanonical} (${s.coverage}%)`
97280
+ );
97281
+ process.stdout.write(`Coverage: ${coverageParts.join(", ")}
97282
+
97283
+ `);
97284
+ }
96687
97285
  function registerDoctorCommand(program) {
96688
- program.command("doctor").description("Run system diagnostics and health checks").option("--detailed", "Show detailed health check results").option("--comprehensive", "Run comprehensive doctor report").option("--full", "Run operational smoke tests across all domains").option("--fix", "Auto-fix failed checks").option("--coherence", "Run coherence check across task data").action(async (opts, command) => {
97286
+ program.command("doctor").description("Run system diagnostics and health checks").option("--detailed", "Show detailed health check results").option("--comprehensive", "Run comprehensive doctor report").option("--full", "Run operational smoke tests across all domains").option("--fix", "Auto-fix failed checks").option("--coherence", "Run coherence check across task data").option("--hooks", "Show cross-provider hook support matrix (CAAMP canonical taxonomy)").action(async (opts, command) => {
96689
97287
  const globalOpts = command.optsWithGlobals ? command.optsWithGlobals() : command.opts();
96690
97288
  const mergedOpts = { ...globalOpts, ...opts };
96691
97289
  const isHuman = mergedOpts["human"] === true || !!process.stdout.isTTY && mergedOpts["json"] !== true;
96692
97290
  const progress = createDoctorProgress(isHuman);
96693
97291
  progress.start();
96694
97292
  try {
96695
- if (mergedOpts["full"]) {
97293
+ if (mergedOpts["hooks"]) {
97294
+ progress.step(0, "Building provider hook matrix");
97295
+ if (isHuman) {
97296
+ const response = await dispatchRaw("query", "admin", "hooks.matrix", {
97297
+ detectProvider: true
97298
+ });
97299
+ progress.complete("Hook matrix complete");
97300
+ if (response.success && response.data) {
97301
+ renderHookMatrixHuman(response.data);
97302
+ } else {
97303
+ process.stderr.write(
97304
+ `Error: ${response.error?.message ?? "Failed to build hook matrix"}
97305
+ `
97306
+ );
97307
+ process.exitCode = 1;
97308
+ }
97309
+ } else {
97310
+ await dispatchFromCli(
97311
+ "query",
97312
+ "admin",
97313
+ "hooks.matrix",
97314
+ { detectProvider: true },
97315
+ { command: "doctor", operation: "admin.hooks.matrix" }
97316
+ );
97317
+ progress.complete("Hook matrix complete");
97318
+ }
97319
+ } else if (mergedOpts["full"]) {
96696
97320
  progress.step(0, "Running operational smoke tests");
96697
97321
  await dispatchFromCli(
96698
97322
  "query",