@cleocode/cleo 2026.3.72 → 2026.3.74

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
@@ -9,6 +9,55 @@ var __export = (target, all) => {
9
9
  __defProp(target, name2, { get: all[name2], enumerable: true });
10
10
  };
11
11
 
12
+ // packages/cleo/src/cli/field-context.ts
13
+ import {
14
+ resolveFieldExtraction
15
+ } from "@cleocode/lafs-protocol";
16
+ function setFieldContext(ctx) {
17
+ currentContext = ctx;
18
+ }
19
+ function getFieldContext() {
20
+ return currentContext;
21
+ }
22
+ function resolveFieldContext(opts) {
23
+ const input = {
24
+ fieldFlag: typeof opts["field"] === "string" ? opts["field"] : void 0,
25
+ fieldsFlag: typeof opts["fields"] === "string" ? opts["fields"] : void 0,
26
+ mviFlag: typeof opts["mvi"] === "string" ? opts["mvi"] : void 0
27
+ };
28
+ return resolveFieldExtraction(input);
29
+ }
30
+ var currentContext;
31
+ var init_field_context = __esm({
32
+ "packages/cleo/src/cli/field-context.ts"() {
33
+ "use strict";
34
+ currentContext = {
35
+ mvi: "standard",
36
+ mviSource: "default",
37
+ expectsCustomMvi: false
38
+ };
39
+ }
40
+ });
41
+
42
+ // packages/cleo/src/cli/format-context.ts
43
+ function setFormatContext(resolution) {
44
+ currentResolution = resolution;
45
+ }
46
+ function getFormatContext() {
47
+ return currentResolution;
48
+ }
49
+ var currentResolution;
50
+ var init_format_context = __esm({
51
+ "packages/cleo/src/cli/format-context.ts"() {
52
+ "use strict";
53
+ currentResolution = {
54
+ format: "json",
55
+ source: "default",
56
+ quiet: false
57
+ };
58
+ }
59
+ });
60
+
12
61
  // packages/contracts/src/errors.ts
13
62
  function normalizeError(error40, fallbackMessage = "An unexpected error occurred") {
14
63
  if (error40 instanceof Error) {
@@ -632,6 +681,13 @@ var init_discovery = __esm({
632
681
  });
633
682
 
634
683
  // packages/core/src/logger.ts
684
+ var logger_exports = {};
685
+ __export(logger_exports, {
686
+ closeLogger: () => closeLogger,
687
+ getLogDir: () => getLogDir,
688
+ getLogger: () => getLogger,
689
+ initLogger: () => initLogger
690
+ });
635
691
  import { existsSync as existsSync2 } from "node:fs";
636
692
  import { dirname, join as join3 } from "node:path";
637
693
  import pino from "pino";
@@ -733,22 +789,42 @@ __export(registry_exports, {
733
789
  HookRegistry: () => HookRegistry,
734
790
  hooks: () => hooks
735
791
  });
736
- var DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
792
+ var LEGACY_EVENT_MAP, DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
737
793
  var init_registry = __esm({
738
794
  "packages/core/src/hooks/registry.ts"() {
739
795
  "use strict";
740
796
  init_logger();
797
+ LEGACY_EVENT_MAP = {
798
+ onSessionStart: "SessionStart",
799
+ onSessionEnd: "SessionEnd",
800
+ onToolStart: "PreToolUse",
801
+ onToolComplete: "PostToolUse",
802
+ onFileChange: "Notification",
803
+ onError: "PostToolUseFailure",
804
+ onPromptSubmit: "PromptSubmit",
805
+ onResponseComplete: "ResponseComplete"
806
+ };
741
807
  DEFAULT_HOOK_CONFIG = {
742
808
  enabled: true,
743
809
  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,
810
+ // CAAMP canonical events (16)
811
+ SessionStart: true,
812
+ SessionEnd: true,
813
+ PromptSubmit: true,
814
+ ResponseComplete: true,
815
+ PreToolUse: true,
816
+ PostToolUse: true,
817
+ PostToolUseFailure: true,
818
+ PermissionRequest: true,
819
+ SubagentStart: true,
820
+ SubagentStop: true,
821
+ PreModel: true,
822
+ PostModel: true,
823
+ PreCompact: true,
824
+ PostCompact: true,
825
+ Notification: true,
826
+ ConfigChange: true,
827
+ // CLEO internal coordination events (5)
752
828
  onWorkAvailable: true,
753
829
  onAgentSpawn: true,
754
830
  onAgentComplete: true,
@@ -759,12 +835,33 @@ var init_registry = __esm({
759
835
  HookRegistry = class {
760
836
  handlers = /* @__PURE__ */ new Map();
761
837
  config = DEFAULT_HOOK_CONFIG;
838
+ /**
839
+ * Resolve a potentially-legacy event name to its canonical equivalent.
840
+ *
841
+ * If the event name matches a known legacy `on`-prefix name, it is
842
+ * remapped and a deprecation warning is logged. Unknown names pass through
843
+ * unchanged so callers using the new canonical names are unaffected.
844
+ */
845
+ resolveEvent(event) {
846
+ const canonical = LEGACY_EVENT_MAP[event];
847
+ if (canonical) {
848
+ getLogger("hooks").warn(
849
+ { legacyEvent: event, canonicalEvent: canonical },
850
+ `[DEPRECATED] Hook event '${event}' has been renamed to '${canonical}'. Update your handler registration.`
851
+ );
852
+ return canonical;
853
+ }
854
+ return event;
855
+ }
762
856
  /**
763
857
  * Register a hook handler for a specific event.
764
858
  *
765
859
  * Handlers are sorted by priority (highest first) and executed
766
860
  * in parallel when the event is dispatched.
767
861
  *
862
+ * Backward compatibility: legacy `on`-prefix event names are automatically
863
+ * remapped to their canonical equivalents.
864
+ *
768
865
  * @param registration - The hook registration containing event, handler, priority, and ID
769
866
  * @returns A function to unregister the handler
770
867
  *
@@ -772,7 +869,7 @@ var init_registry = __esm({
772
869
  * ```typescript
773
870
  * const unregister = hooks.register({
774
871
  * id: 'my-handler',
775
- * event: 'onSessionStart',
872
+ * event: 'SessionStart',
776
873
  * handler: async (root, payload) => { console.log('Session started'); },
777
874
  * priority: 100
778
875
  * });
@@ -781,12 +878,14 @@ var init_registry = __esm({
781
878
  * ```
782
879
  */
783
880
  register(registration) {
784
- const list = this.handlers.get(registration.event) || [];
785
- list.push(registration);
881
+ const resolvedEvent = this.resolveEvent(registration.event);
882
+ const resolvedRegistration = { ...registration, event: resolvedEvent };
883
+ const list = this.handlers.get(resolvedEvent) || [];
884
+ list.push(resolvedRegistration);
786
885
  list.sort((a, b) => b.priority - a.priority);
787
- this.handlers.set(registration.event, list);
886
+ this.handlers.set(resolvedEvent, list);
788
887
  return () => {
789
- const handlers = this.handlers.get(registration.event);
888
+ const handlers = this.handlers.get(resolvedEvent);
790
889
  if (handlers) {
791
890
  const idx = handlers.findIndex((h) => h.id === registration.id);
792
891
  if (idx !== -1) handlers.splice(idx, 1);
@@ -800,14 +899,17 @@ var init_registry = __esm({
800
899
  * execution. Errors in individual handlers are logged but do not block
801
900
  * other handlers or propagate to the caller.
802
901
  *
803
- * @param event - The CAAMP hook event to dispatch
902
+ * Backward compatibility: legacy `on`-prefix event names are automatically
903
+ * remapped to their canonical equivalents.
904
+ *
905
+ * @param event - The CAAMP canonical hook event to dispatch
804
906
  * @param projectRoot - The project root directory path
805
907
  * @param payload - The event payload (typed by event)
806
908
  * @returns Promise that resolves when all handlers have completed
807
909
  *
808
910
  * @example
809
911
  * ```typescript
810
- * await hooks.dispatch('onSessionStart', '/project', {
912
+ * await hooks.dispatch('SessionStart', '/project', {
811
913
  * timestamp: new Date().toISOString(),
812
914
  * sessionId: 'sess-123',
813
915
  * name: 'My Session',
@@ -817,15 +919,19 @@ var init_registry = __esm({
817
919
  */
818
920
  async dispatch(event, projectRoot, payload) {
819
921
  if (!this.config.enabled) return;
820
- if (!this.config.events[event]) return;
821
- const handlers = this.handlers.get(event);
922
+ const resolvedEvent = this.resolveEvent(event);
923
+ if (!this.config.events[resolvedEvent]) return;
924
+ const handlers = this.handlers.get(resolvedEvent);
822
925
  if (!handlers || handlers.length === 0) return;
823
926
  await Promise.allSettled(
824
927
  handlers.map(async (reg) => {
825
928
  try {
826
929
  await reg.handler(projectRoot, payload);
827
930
  } catch (error40) {
828
- getLogger("hooks").warn({ err: error40, hookId: reg.id, event }, "Hook handler failed");
931
+ getLogger("hooks").warn(
932
+ { err: error40, hookId: reg.id, event: resolvedEvent },
933
+ "Hook handler failed"
934
+ );
829
935
  }
830
936
  })
831
937
  );
@@ -834,12 +940,14 @@ var init_registry = __esm({
834
940
  * Check if a specific event is currently enabled.
835
941
  *
836
942
  * Both the global enabled flag and the per-event flag must be true.
943
+ * Automatically resolves legacy `on`-prefix event names.
837
944
  *
838
945
  * @param event - The CAAMP hook event to check
839
946
  * @returns True if the event is enabled
840
947
  */
841
948
  isEnabled(event) {
842
- return this.config.enabled && this.config.events[event];
949
+ const resolvedEvent = this.resolveEvent(event);
950
+ return this.config.enabled && this.config.events[resolvedEvent];
843
951
  }
844
952
  /**
845
953
  * Update the hook system configuration.
@@ -851,7 +959,7 @@ var init_registry = __esm({
851
959
  * @example
852
960
  * ```typescript
853
961
  * hooks.setConfig({ enabled: false }); // Disable all hooks
854
- * hooks.setConfig({ events: { onError: false } }); // Disable specific event
962
+ * hooks.setConfig({ events: { PostToolUseFailure: false } }); // Disable specific event
855
963
  * ```
856
964
  */
857
965
  setConfig(config2) {
@@ -869,12 +977,14 @@ var init_registry = __esm({
869
977
  * List all registered handlers for a specific event.
870
978
  *
871
979
  * Returns handlers in priority order (highest first).
980
+ * Automatically resolves legacy `on`-prefix event names.
872
981
  *
873
982
  * @param event - The CAAMP hook event
874
983
  * @returns Array of hook registrations
875
984
  */
876
985
  listHandlers(event) {
877
- return [...this.handlers.get(event) || []];
986
+ const resolvedEvent = this.resolveEvent(event);
987
+ return [...this.handlers.get(resolvedEvent) || []];
878
988
  }
879
989
  };
880
990
  hooks = new HookRegistry();
@@ -13695,7 +13805,7 @@ async function saveJson(filePath, data, options) {
13695
13805
  }
13696
13806
  await atomicWriteJson(filePath, data, { indent: options?.indent });
13697
13807
  Promise.resolve().then(() => (init_registry(), registry_exports)).then(
13698
- ({ hooks: h }) => h.dispatch("onFileChange", process.cwd(), {
13808
+ ({ hooks: h }) => h.dispatch("Notification", process.cwd(), {
13699
13809
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13700
13810
  filePath,
13701
13811
  changeType: "write"
@@ -20929,13 +21039,13 @@ var init_session_hooks = __esm({
20929
21039
  init_memory_bridge_refresh();
20930
21040
  hooks.register({
20931
21041
  id: "brain-session-start",
20932
- event: "onSessionStart",
21042
+ event: "SessionStart",
20933
21043
  handler: handleSessionStart,
20934
21044
  priority: 100
20935
21045
  });
20936
21046
  hooks.register({
20937
21047
  id: "brain-session-end",
20938
- event: "onSessionEnd",
21048
+ event: "SessionEnd",
20939
21049
  handler: handleSessionEnd,
20940
21050
  priority: 100
20941
21051
  });
@@ -20982,13 +21092,13 @@ var init_task_hooks = __esm({
20982
21092
  init_memory_bridge_refresh();
20983
21093
  hooks.register({
20984
21094
  id: "brain-tool-start",
20985
- event: "onToolStart",
21095
+ event: "PreToolUse",
20986
21096
  handler: handleToolStart,
20987
21097
  priority: 100
20988
21098
  });
20989
21099
  hooks.register({
20990
21100
  id: "brain-tool-complete",
20991
- event: "onToolComplete",
21101
+ event: "PostToolUse",
20992
21102
  handler: handleToolComplete,
20993
21103
  priority: 100
20994
21104
  });
@@ -21018,7 +21128,7 @@ var init_error_hooks = __esm({
21018
21128
  init_registry();
21019
21129
  hooks.register({
21020
21130
  id: "brain-error",
21021
- event: "onError",
21131
+ event: "PostToolUseFailure",
21022
21132
  handler: handleError,
21023
21133
  priority: 100
21024
21134
  });
@@ -21049,6 +21159,7 @@ async function isFileCaptureEnabled(projectRoot) {
21049
21159
  }
21050
21160
  }
21051
21161
  async function handleFileChange(projectRoot, payload) {
21162
+ if (!payload.filePath || !payload.changeType) return;
21052
21163
  if (!await isFileCaptureEnabled(projectRoot)) return;
21053
21164
  const now2 = Date.now();
21054
21165
  const lastWrite = recentWrites.get(payload.filePath);
@@ -21095,7 +21206,7 @@ var init_file_hooks = __esm({
21095
21206
  ];
21096
21207
  hooks.register({
21097
21208
  id: "brain-file-change",
21098
- event: "onFileChange",
21209
+ event: "Notification",
21099
21210
  handler: handleFileChange,
21100
21211
  priority: 100
21101
21212
  });
@@ -21149,22 +21260,51 @@ async function handleResponseComplete(projectRoot, payload) {
21149
21260
  if (!isMissingBrainSchemaError4(err)) throw err;
21150
21261
  }
21151
21262
  }
21263
+ async function handleSystemNotification(projectRoot, payload) {
21264
+ if (payload.filePath || payload.changeType) return;
21265
+ if (!payload.message) return;
21266
+ try {
21267
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21268
+ const config2 = await loadConfig4(projectRoot);
21269
+ if (!config2.brain?.autoCapture) return;
21270
+ } catch {
21271
+ return;
21272
+ }
21273
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21274
+ try {
21275
+ await observeBrain2(projectRoot, {
21276
+ text: `System notification: ${payload.message}`,
21277
+ title: `Notification: ${payload.message.slice(0, 60)}`,
21278
+ type: "discovery",
21279
+ sourceSessionId: payload.sessionId,
21280
+ sourceType: "agent"
21281
+ });
21282
+ } catch (err) {
21283
+ if (!isMissingBrainSchemaError4(err)) throw err;
21284
+ }
21285
+ }
21152
21286
  var init_mcp_hooks = __esm({
21153
21287
  "packages/core/src/hooks/handlers/mcp-hooks.ts"() {
21154
21288
  "use strict";
21155
21289
  init_registry();
21156
21290
  hooks.register({
21157
21291
  id: "brain-prompt-submit",
21158
- event: "onPromptSubmit",
21292
+ event: "PromptSubmit",
21159
21293
  handler: handlePromptSubmit,
21160
21294
  priority: 100
21161
21295
  });
21162
21296
  hooks.register({
21163
21297
  id: "brain-response-complete",
21164
- event: "onResponseComplete",
21298
+ event: "ResponseComplete",
21165
21299
  handler: handleResponseComplete,
21166
21300
  priority: 100
21167
21301
  });
21302
+ hooks.register({
21303
+ id: "brain-system-notification",
21304
+ event: "Notification",
21305
+ handler: handleSystemNotification,
21306
+ priority: 90
21307
+ });
21168
21308
  }
21169
21309
  });
21170
21310
 
@@ -21241,19 +21381,158 @@ var init_work_capture_hooks = __esm({
21241
21381
  ]);
21242
21382
  hooks.register({
21243
21383
  id: "work-capture-prompt-submit",
21244
- event: "onPromptSubmit",
21384
+ event: "PromptSubmit",
21245
21385
  handler: handleWorkPromptSubmit,
21246
21386
  priority: 90
21247
21387
  });
21248
21388
  hooks.register({
21249
21389
  id: "work-capture-response-complete",
21250
- event: "onResponseComplete",
21390
+ event: "ResponseComplete",
21251
21391
  handler: handleWorkResponseComplete,
21252
21392
  priority: 90
21253
21393
  });
21254
21394
  }
21255
21395
  });
21256
21396
 
21397
+ // packages/core/src/hooks/handlers/agent-hooks.ts
21398
+ function isMissingBrainSchemaError6(err) {
21399
+ if (!(err instanceof Error)) return false;
21400
+ const message = String(err.message || "").toLowerCase();
21401
+ return message.includes("no such table") && message.includes("brain_");
21402
+ }
21403
+ async function isAutoCaptureEnabled(projectRoot) {
21404
+ try {
21405
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21406
+ const config2 = await loadConfig4(projectRoot);
21407
+ return config2.brain?.autoCapture ?? false;
21408
+ } catch {
21409
+ return false;
21410
+ }
21411
+ }
21412
+ async function handleSubagentStart(projectRoot, payload) {
21413
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
21414
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21415
+ const rolePart = payload.role ? ` role=${payload.role}` : "";
21416
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
21417
+ try {
21418
+ await observeBrain2(projectRoot, {
21419
+ text: `Subagent spawned: ${payload.agentId}${rolePart}${taskPart}`,
21420
+ title: `Subagent start: ${payload.agentId}`,
21421
+ type: "discovery",
21422
+ sourceSessionId: payload.sessionId,
21423
+ sourceType: "agent"
21424
+ });
21425
+ } catch (err) {
21426
+ if (!isMissingBrainSchemaError6(err)) throw err;
21427
+ }
21428
+ }
21429
+ async function handleSubagentStop(projectRoot, payload) {
21430
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
21431
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21432
+ const statusPart = payload.status ? ` status=${payload.status}` : "";
21433
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
21434
+ const summaryPart = payload.summary ? `
21435
+ Summary: ${payload.summary}` : "";
21436
+ try {
21437
+ await observeBrain2(projectRoot, {
21438
+ text: `Subagent completed: ${payload.agentId}${statusPart}${taskPart}${summaryPart}`,
21439
+ title: `Subagent stop: ${payload.agentId}`,
21440
+ type: "change",
21441
+ sourceSessionId: payload.sessionId,
21442
+ sourceType: "agent"
21443
+ });
21444
+ } catch (err) {
21445
+ if (!isMissingBrainSchemaError6(err)) throw err;
21446
+ }
21447
+ }
21448
+ var init_agent_hooks = __esm({
21449
+ "packages/core/src/hooks/handlers/agent-hooks.ts"() {
21450
+ "use strict";
21451
+ init_registry();
21452
+ hooks.register({
21453
+ id: "brain-subagent-start",
21454
+ event: "SubagentStart",
21455
+ handler: handleSubagentStart,
21456
+ priority: 100
21457
+ });
21458
+ hooks.register({
21459
+ id: "brain-subagent-stop",
21460
+ event: "SubagentStop",
21461
+ handler: handleSubagentStop,
21462
+ priority: 100
21463
+ });
21464
+ }
21465
+ });
21466
+
21467
+ // packages/core/src/hooks/handlers/context-hooks.ts
21468
+ function isMissingBrainSchemaError7(err) {
21469
+ if (!(err instanceof Error)) return false;
21470
+ const message = String(err.message || "").toLowerCase();
21471
+ return message.includes("no such table") && message.includes("brain_");
21472
+ }
21473
+ async function isAutoCaptureEnabled2(projectRoot) {
21474
+ try {
21475
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21476
+ const config2 = await loadConfig4(projectRoot);
21477
+ return config2.brain?.autoCapture ?? false;
21478
+ } catch {
21479
+ return false;
21480
+ }
21481
+ }
21482
+ async function handlePreCompact(projectRoot, payload) {
21483
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
21484
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21485
+ const tokensPart = payload.tokensBefore != null ? ` (~${payload.tokensBefore.toLocaleString()} tokens)` : "";
21486
+ const reasonPart = payload.reason ? ` Reason: ${payload.reason}` : "";
21487
+ try {
21488
+ await observeBrain2(projectRoot, {
21489
+ text: `Context compaction about to begin${tokensPart}.${reasonPart}`,
21490
+ title: "Pre-compaction context snapshot",
21491
+ type: "discovery",
21492
+ sourceSessionId: payload.sessionId,
21493
+ sourceType: "agent"
21494
+ });
21495
+ } catch (err) {
21496
+ if (!isMissingBrainSchemaError7(err)) throw err;
21497
+ }
21498
+ }
21499
+ async function handlePostCompact(projectRoot, payload) {
21500
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
21501
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21502
+ const statusPart = payload.success ? "succeeded" : "failed";
21503
+ const beforePart = payload.tokensBefore != null ? ` before=${payload.tokensBefore.toLocaleString()}` : "";
21504
+ const afterPart = payload.tokensAfter != null ? ` after=${payload.tokensAfter.toLocaleString()}` : "";
21505
+ try {
21506
+ await observeBrain2(projectRoot, {
21507
+ text: `Context compaction ${statusPart}${beforePart}${afterPart}`,
21508
+ title: "Post-compaction record",
21509
+ type: "change",
21510
+ sourceSessionId: payload.sessionId,
21511
+ sourceType: "agent"
21512
+ });
21513
+ } catch (err) {
21514
+ if (!isMissingBrainSchemaError7(err)) throw err;
21515
+ }
21516
+ }
21517
+ var init_context_hooks = __esm({
21518
+ "packages/core/src/hooks/handlers/context-hooks.ts"() {
21519
+ "use strict";
21520
+ init_registry();
21521
+ hooks.register({
21522
+ id: "brain-pre-compact",
21523
+ event: "PreCompact",
21524
+ handler: handlePreCompact,
21525
+ priority: 100
21526
+ });
21527
+ hooks.register({
21528
+ id: "brain-post-compact",
21529
+ event: "PostCompact",
21530
+ handler: handlePostCompact,
21531
+ priority: 100
21532
+ });
21533
+ }
21534
+ });
21535
+
21257
21536
  // packages/core/src/hooks/handlers/index.ts
21258
21537
  var init_handlers = __esm({
21259
21538
  "packages/core/src/hooks/handlers/index.ts"() {
@@ -21264,6 +21543,10 @@ var init_handlers = __esm({
21264
21543
  init_file_hooks();
21265
21544
  init_mcp_hooks();
21266
21545
  init_work_capture_hooks();
21546
+ init_agent_hooks();
21547
+ init_context_hooks();
21548
+ init_agent_hooks();
21549
+ init_context_hooks();
21267
21550
  init_error_hooks();
21268
21551
  init_file_hooks();
21269
21552
  init_mcp_hooks();
@@ -23114,7 +23397,7 @@ async function startSession(options, cwd, accessor) {
23114
23397
  });
23115
23398
  }
23116
23399
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
23117
- hooks2.dispatch("onSessionStart", cwd ?? process.cwd(), {
23400
+ hooks2.dispatch("SessionStart", cwd ?? process.cwd(), {
23118
23401
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23119
23402
  sessionId: session.id,
23120
23403
  name: options.name,
@@ -23152,7 +23435,7 @@ async function endSession(options = {}, cwd, accessor) {
23152
23435
  session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
23153
23436
  const duration3 = Math.floor((Date.now() - new Date(session.startedAt).getTime()) / 1e3);
23154
23437
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
23155
- hooks2.dispatch("onSessionEnd", cwd ?? process.cwd(), {
23438
+ hooks2.dispatch("SessionEnd", cwd ?? process.cwd(), {
23156
23439
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23157
23440
  sessionId: session.id,
23158
23441
  duration: duration3,
@@ -35888,7 +36171,7 @@ function validatePayload(event, payload) {
35888
36171
  const errors = result.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`);
35889
36172
  return { valid: false, errors };
35890
36173
  }
35891
- var HookPayloadSchema, OnSessionStartPayloadSchema, OnSessionEndPayloadSchema, OnToolStartPayloadSchema, OnToolCompletePayloadSchema, OnFileChangePayloadSchema, OnErrorPayloadSchema, OnPromptSubmitPayloadSchema, OnResponseCompletePayloadSchema, OnWorkAvailablePayloadSchema, OnAgentSpawnPayloadSchema, OnAgentCompletePayloadSchema, OnCascadeStartPayloadSchema, OnPatrolPayloadSchema, EVENT_SCHEMA_MAP;
36174
+ 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
36175
  var init_payload_schemas = __esm({
35893
36176
  "packages/core/src/hooks/payload-schemas.ts"() {
35894
36177
  "use strict";
@@ -35900,33 +36183,42 @@ var init_payload_schemas = __esm({
35900
36183
  providerId: external_exports.string().optional(),
35901
36184
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35902
36185
  });
35903
- OnSessionStartPayloadSchema = HookPayloadSchema.extend({
36186
+ SessionStartPayloadSchema = HookPayloadSchema.extend({
35904
36187
  sessionId: external_exports.string(),
35905
36188
  name: external_exports.string(),
35906
36189
  scope: external_exports.string(),
35907
36190
  agent: external_exports.string().optional()
35908
36191
  });
35909
- OnSessionEndPayloadSchema = HookPayloadSchema.extend({
36192
+ OnSessionStartPayloadSchema = SessionStartPayloadSchema;
36193
+ SessionEndPayloadSchema = HookPayloadSchema.extend({
35910
36194
  sessionId: external_exports.string(),
35911
36195
  duration: external_exports.number(),
35912
36196
  tasksCompleted: external_exports.array(external_exports.string())
35913
36197
  });
35914
- OnToolStartPayloadSchema = HookPayloadSchema.extend({
36198
+ OnSessionEndPayloadSchema = SessionEndPayloadSchema;
36199
+ PreToolUsePayloadSchema = HookPayloadSchema.extend({
35915
36200
  taskId: external_exports.string(),
35916
36201
  taskTitle: external_exports.string(),
35917
- previousTask: external_exports.string().optional()
36202
+ previousTask: external_exports.string().optional(),
36203
+ toolName: external_exports.string().optional(),
36204
+ toolInput: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35918
36205
  });
35919
- OnToolCompletePayloadSchema = HookPayloadSchema.extend({
36206
+ OnToolStartPayloadSchema = PreToolUsePayloadSchema;
36207
+ PostToolUsePayloadSchema = HookPayloadSchema.extend({
35920
36208
  taskId: external_exports.string(),
35921
36209
  taskTitle: external_exports.string(),
35922
- status: external_exports.enum(["done", "archived", "cancelled"])
36210
+ status: external_exports.enum(["done", "archived", "cancelled"]),
36211
+ toolResult: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35923
36212
  });
35924
- OnFileChangePayloadSchema = HookPayloadSchema.extend({
35925
- filePath: external_exports.string(),
35926
- changeType: external_exports.enum(["write", "create", "delete"]),
35927
- sizeBytes: external_exports.number().optional()
36213
+ OnToolCompletePayloadSchema = PostToolUsePayloadSchema;
36214
+ NotificationPayloadSchema = HookPayloadSchema.extend({
36215
+ filePath: external_exports.string().optional(),
36216
+ changeType: external_exports.enum(["write", "create", "delete"]).optional(),
36217
+ sizeBytes: external_exports.number().optional(),
36218
+ message: external_exports.string().optional()
35928
36219
  });
35929
- OnErrorPayloadSchema = HookPayloadSchema.extend({
36220
+ OnFileChangePayloadSchema = NotificationPayloadSchema;
36221
+ PostToolUseFailurePayloadSchema = HookPayloadSchema.extend({
35930
36222
  errorCode: external_exports.union([external_exports.number(), external_exports.string()]),
35931
36223
  message: external_exports.string(),
35932
36224
  domain: external_exports.string().optional(),
@@ -35934,13 +36226,15 @@ var init_payload_schemas = __esm({
35934
36226
  gateway: external_exports.string().optional(),
35935
36227
  stack: external_exports.string().optional()
35936
36228
  });
35937
- OnPromptSubmitPayloadSchema = HookPayloadSchema.extend({
36229
+ OnErrorPayloadSchema = PostToolUseFailurePayloadSchema;
36230
+ PromptSubmitPayloadSchema = HookPayloadSchema.extend({
35938
36231
  gateway: external_exports.string(),
35939
36232
  domain: external_exports.string(),
35940
36233
  operation: external_exports.string(),
35941
36234
  source: external_exports.string().optional()
35942
36235
  });
35943
- OnResponseCompletePayloadSchema = HookPayloadSchema.extend({
36236
+ OnPromptSubmitPayloadSchema = PromptSubmitPayloadSchema;
36237
+ ResponseCompletePayloadSchema = HookPayloadSchema.extend({
35944
36238
  gateway: external_exports.string(),
35945
36239
  domain: external_exports.string(),
35946
36240
  operation: external_exports.string(),
@@ -35948,6 +36242,32 @@ var init_payload_schemas = __esm({
35948
36242
  durationMs: external_exports.number().optional(),
35949
36243
  errorCode: external_exports.string().optional()
35950
36244
  });
36245
+ OnResponseCompletePayloadSchema = ResponseCompletePayloadSchema;
36246
+ SubagentStartPayloadSchema = HookPayloadSchema.extend({
36247
+ agentId: external_exports.string(),
36248
+ role: external_exports.string().optional(),
36249
+ taskId: external_exports.string().optional()
36250
+ });
36251
+ SubagentStopPayloadSchema = HookPayloadSchema.extend({
36252
+ agentId: external_exports.string(),
36253
+ status: external_exports.enum(["complete", "partial", "blocked", "failed"]).optional(),
36254
+ taskId: external_exports.string().optional(),
36255
+ summary: external_exports.string().optional()
36256
+ });
36257
+ PreCompactPayloadSchema = HookPayloadSchema.extend({
36258
+ tokensBefore: external_exports.number().optional(),
36259
+ reason: external_exports.string().optional()
36260
+ });
36261
+ PostCompactPayloadSchema = HookPayloadSchema.extend({
36262
+ tokensBefore: external_exports.number().optional(),
36263
+ tokensAfter: external_exports.number().optional(),
36264
+ success: external_exports.boolean()
36265
+ });
36266
+ ConfigChangePayloadSchema = HookPayloadSchema.extend({
36267
+ key: external_exports.string(),
36268
+ previousValue: external_exports.unknown().optional(),
36269
+ newValue: external_exports.unknown().optional()
36270
+ });
35951
36271
  OnWorkAvailablePayloadSchema = HookPayloadSchema.extend({
35952
36272
  taskIds: external_exports.array(external_exports.string()),
35953
36273
  epicId: external_exports.string().optional(),
@@ -35979,14 +36299,21 @@ var init_payload_schemas = __esm({
35979
36299
  scope: external_exports.string().optional()
35980
36300
  });
35981
36301
  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,
36302
+ // CAAMP canonical events (16)
36303
+ SessionStart: SessionStartPayloadSchema,
36304
+ SessionEnd: SessionEndPayloadSchema,
36305
+ PreToolUse: PreToolUsePayloadSchema,
36306
+ PostToolUse: PostToolUsePayloadSchema,
36307
+ Notification: NotificationPayloadSchema,
36308
+ PostToolUseFailure: PostToolUseFailurePayloadSchema,
36309
+ PromptSubmit: PromptSubmitPayloadSchema,
36310
+ ResponseComplete: ResponseCompletePayloadSchema,
36311
+ SubagentStart: SubagentStartPayloadSchema,
36312
+ SubagentStop: SubagentStopPayloadSchema,
36313
+ PreCompact: PreCompactPayloadSchema,
36314
+ PostCompact: PostCompactPayloadSchema,
36315
+ ConfigChange: ConfigChangePayloadSchema,
36316
+ // CLEO internal coordination events (5)
35990
36317
  onWorkAvailable: OnWorkAvailablePayloadSchema,
35991
36318
  onAgentSpawn: OnAgentSpawnPayloadSchema,
35992
36319
  onAgentComplete: OnAgentCompletePayloadSchema,
@@ -36016,10 +36343,15 @@ __export(hooks_exports, {
36016
36343
  OnWorkAvailablePayloadSchema: () => OnWorkAvailablePayloadSchema,
36017
36344
  handleError: () => handleError,
36018
36345
  handleFileChange: () => handleFileChange,
36346
+ handlePostCompact: () => handlePostCompact,
36347
+ handlePreCompact: () => handlePreCompact,
36019
36348
  handlePromptSubmit: () => handlePromptSubmit,
36020
36349
  handleResponseComplete: () => handleResponseComplete,
36021
36350
  handleSessionEnd: () => handleSessionEnd,
36022
36351
  handleSessionStart: () => handleSessionStart,
36352
+ handleSubagentStart: () => handleSubagentStart,
36353
+ handleSubagentStop: () => handleSubagentStop,
36354
+ handleSystemNotification: () => handleSystemNotification,
36023
36355
  handleToolComplete: () => handleToolComplete,
36024
36356
  handleToolStart: () => handleToolStart,
36025
36357
  handleWorkPromptSubmit: () => handleWorkPromptSubmit,
@@ -44975,8 +45307,8 @@ var init_checksum = __esm({
44975
45307
  });
44976
45308
 
44977
45309
  // packages/core/src/migration/logger.ts
44978
- var logger_exports = {};
44979
- __export(logger_exports, {
45310
+ var logger_exports2 = {};
45311
+ __export(logger_exports2, {
44980
45312
  MigrationLogger: () => MigrationLogger,
44981
45313
  createMigrationLogger: () => createMigrationLogger,
44982
45314
  getLatestMigrationLog: () => getLatestMigrationLog,
@@ -47929,6 +48261,903 @@ var init_transfer = __esm({
47929
48261
  }
47930
48262
  });
47931
48263
 
48264
+ // packages/core/src/task-work/index.ts
48265
+ var task_work_exports = {};
48266
+ __export(task_work_exports, {
48267
+ currentTask: () => currentTask,
48268
+ getTaskHistory: () => getTaskHistory,
48269
+ getWorkHistory: () => getWorkHistory,
48270
+ startTask: () => startTask,
48271
+ stopTask: () => stopTask
48272
+ });
48273
+ async function currentTask(cwd, accessor) {
48274
+ const acc = accessor ?? await getAccessor(cwd);
48275
+ const focus = await acc.getMetaValue("focus_state");
48276
+ return {
48277
+ currentTask: focus?.currentTask ?? null,
48278
+ currentPhase: focus?.currentPhase ?? null,
48279
+ sessionNote: focus?.sessionNote ?? null,
48280
+ nextAction: focus?.nextAction ?? null
48281
+ };
48282
+ }
48283
+ async function startTask(taskId, cwd, accessor) {
48284
+ if (!taskId) {
48285
+ throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
48286
+ }
48287
+ const acc = accessor ?? await getAccessor(cwd);
48288
+ const task = await acc.loadSingleTask(taskId);
48289
+ if (!task) {
48290
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
48291
+ fix: `Use 'cleo find "${taskId}"' to search`
48292
+ });
48293
+ }
48294
+ const { tasks: allTasks } = await acc.queryTasks({});
48295
+ const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
48296
+ if (unresolvedDeps.length > 0) {
48297
+ throw new CleoError(
48298
+ 5 /* DEPENDENCY_ERROR */,
48299
+ `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
48300
+ {
48301
+ fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
48302
+ }
48303
+ );
48304
+ }
48305
+ const focus = await acc.getMetaValue("focus_state") ?? {};
48306
+ const previousTask = focus.currentTask ?? null;
48307
+ focus.currentTask = taskId;
48308
+ focus.currentPhase = task.phase ?? null;
48309
+ const noteEntry = {
48310
+ note: `Started work on ${taskId}: ${task.title}`,
48311
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
48312
+ };
48313
+ if (!focus.sessionNotes) {
48314
+ focus.sessionNotes = [];
48315
+ }
48316
+ focus.sessionNotes.push(noteEntry);
48317
+ await acc.setMetaValue("focus_state", focus);
48318
+ await logOperation(
48319
+ "task_start",
48320
+ taskId,
48321
+ {
48322
+ previousTask,
48323
+ title: task.title
48324
+ },
48325
+ accessor
48326
+ );
48327
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
48328
+ hooks2.dispatch("PreToolUse", cwd ?? process.cwd(), {
48329
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48330
+ taskId,
48331
+ taskTitle: task.title
48332
+ }).catch(() => {
48333
+ });
48334
+ return {
48335
+ taskId,
48336
+ taskTitle: task.title,
48337
+ previousTask
48338
+ };
48339
+ }
48340
+ async function stopTask(cwd, accessor) {
48341
+ const acc = accessor ?? await getAccessor(cwd);
48342
+ const focus = await acc.getMetaValue("focus_state");
48343
+ const previousTask = focus?.currentTask ?? null;
48344
+ if (!focus) {
48345
+ return { previousTask: null };
48346
+ }
48347
+ const taskId = focus.currentTask;
48348
+ const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
48349
+ focus.currentTask = null;
48350
+ focus.nextAction = null;
48351
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
48352
+ if (taskId && task) {
48353
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
48354
+ hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
48355
+ timestamp: now2,
48356
+ taskId,
48357
+ taskTitle: task.title,
48358
+ status: "done"
48359
+ }).catch(() => {
48360
+ });
48361
+ }
48362
+ await acc.setMetaValue("focus_state", focus);
48363
+ await logOperation(
48364
+ "task_stop",
48365
+ previousTask ?? "none",
48366
+ {
48367
+ previousTask
48368
+ },
48369
+ accessor
48370
+ );
48371
+ return { previousTask };
48372
+ }
48373
+ async function getWorkHistory(cwd, accessor) {
48374
+ const acc = accessor ?? await getAccessor(cwd);
48375
+ const focus = await acc.getMetaValue("focus_state");
48376
+ const notes = focus?.sessionNotes ?? [];
48377
+ const history = [];
48378
+ for (const note of notes) {
48379
+ const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
48380
+ if (match) {
48381
+ history.push({
48382
+ taskId: match[1],
48383
+ timestamp: note.timestamp
48384
+ });
48385
+ }
48386
+ }
48387
+ return history.reverse();
48388
+ }
48389
+ var getTaskHistory;
48390
+ var init_task_work = __esm({
48391
+ "packages/core/src/task-work/index.ts"() {
48392
+ "use strict";
48393
+ init_src();
48394
+ init_errors3();
48395
+ init_data_accessor();
48396
+ init_add();
48397
+ init_dependency_check();
48398
+ init_handlers();
48399
+ getTaskHistory = getWorkHistory;
48400
+ }
48401
+ });
48402
+
48403
+ // packages/core/src/tasks/complete.ts
48404
+ var complete_exports = {};
48405
+ __export(complete_exports, {
48406
+ completeTask: () => completeTask
48407
+ });
48408
+ function isVerificationGate(value) {
48409
+ return VERIFICATION_GATES.has(value);
48410
+ }
48411
+ async function loadCompletionEnforcement(cwd) {
48412
+ const isTest = !!process.env.VITEST;
48413
+ const config2 = await loadConfig(cwd);
48414
+ const acceptance = config2.enforcement?.acceptance;
48415
+ const verificationCfg = config2.verification;
48416
+ const acceptanceMode = acceptance?.mode ?? (isTest ? "off" : "block");
48417
+ const acceptanceRequiredForPriorities = acceptance?.requiredForPriorities ?? (isTest ? [] : ["critical", "high", "medium", "low"]);
48418
+ const rawVerificationEnabled = await getRawConfigValue("verification.enabled", cwd);
48419
+ const verificationEnabled = rawVerificationEnabled !== void 0 ? rawVerificationEnabled : !isTest;
48420
+ const verificationRequiredGates = (verificationCfg?.requiredGates ?? []).filter(isVerificationGate).length > 0 ? (verificationCfg?.requiredGates ?? []).filter(isVerificationGate) : DEFAULT_VERIFICATION_REQUIRED_GATES;
48421
+ const verificationMaxRounds = verificationCfg?.maxRounds ?? 5;
48422
+ const lifecycleMode = config2.lifecycle?.mode ?? (isTest ? "off" : "strict");
48423
+ return {
48424
+ acceptanceMode,
48425
+ acceptanceRequiredForPriorities,
48426
+ verificationEnabled,
48427
+ verificationRequiredGates,
48428
+ verificationMaxRounds,
48429
+ lifecycleMode
48430
+ };
48431
+ }
48432
+ async function completeTask(options, cwd, accessor) {
48433
+ const acc = accessor ?? await getAccessor(cwd);
48434
+ const task = await acc.loadSingleTask(options.taskId);
48435
+ if (!task) {
48436
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
48437
+ fix: `Use 'cleo find "${options.taskId}"' to search`
48438
+ });
48439
+ }
48440
+ await requireActiveSession("tasks.complete", cwd);
48441
+ const enforcement = await loadCompletionEnforcement(cwd);
48442
+ if (task.status === "done") {
48443
+ throw new CleoError(17 /* TASK_COMPLETED */, `Task ${options.taskId} is already completed`);
48444
+ }
48445
+ if (task.depends?.length) {
48446
+ const deps = await acc.loadTasks(task.depends);
48447
+ const incompleteDeps = deps.filter((d) => d.status !== "done" && d.status !== "cancelled").map((d) => d.id);
48448
+ if (incompleteDeps.length > 0) {
48449
+ throw new CleoError(
48450
+ 5 /* DEPENDENCY_ERROR */,
48451
+ `Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
48452
+ {
48453
+ fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}`
48454
+ }
48455
+ );
48456
+ }
48457
+ }
48458
+ const acceptanceEnforcement = await createAcceptanceEnforcement(cwd);
48459
+ const completionValidation = acceptanceEnforcement.validateCompletion(task);
48460
+ if (!completionValidation.valid) {
48461
+ throw new CleoError(
48462
+ completionValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
48463
+ completionValidation.error,
48464
+ { fix: completionValidation.fix }
48465
+ );
48466
+ }
48467
+ if (enforcement.verificationEnabled && task.type !== "epic") {
48468
+ if (!task.verification) {
48469
+ throw new CleoError(
48470
+ 40 /* VERIFICATION_INIT_FAILED */,
48471
+ `Task ${options.taskId} is missing verification metadata`,
48472
+ {
48473
+ fix: `Initialize verification for ${options.taskId} before completion`
48474
+ }
48475
+ );
48476
+ }
48477
+ if (task.verification.round > enforcement.verificationMaxRounds) {
48478
+ throw new CleoError(
48479
+ 44 /* MAX_ROUNDS_EXCEEDED */,
48480
+ `Task ${options.taskId} exceeded verification max rounds (${enforcement.verificationMaxRounds})`,
48481
+ {
48482
+ fix: `Review failure log and resolve blockers before retrying completion`
48483
+ }
48484
+ );
48485
+ }
48486
+ const missingRequiredGates = enforcement.verificationRequiredGates.filter(
48487
+ (gate) => task.verification?.gates?.[gate] !== true
48488
+ );
48489
+ if (missingRequiredGates.length > 0 || task.verification.passed !== true) {
48490
+ const exitCode = enforcement.lifecycleMode === "strict" ? 80 /* LIFECYCLE_GATE_FAILED */ : 45 /* GATE_DEPENDENCY */;
48491
+ throw new CleoError(
48492
+ exitCode,
48493
+ `Task ${options.taskId} failed verification gates: ${missingRequiredGates.join(", ") || "verification.passed=false"}`,
48494
+ {
48495
+ fix: `Set required verification gates before completion: ${enforcement.verificationRequiredGates.join(", ")}`
48496
+ }
48497
+ );
48498
+ }
48499
+ }
48500
+ const children = await acc.getChildren(options.taskId);
48501
+ const incompleteChildren = children.filter(
48502
+ (c) => c.status !== "done" && c.status !== "cancelled"
48503
+ );
48504
+ if (incompleteChildren.length > 0 && task.type === "epic") {
48505
+ if (!task.noAutoComplete) {
48506
+ throw new CleoError(
48507
+ 16 /* HAS_CHILDREN */,
48508
+ `Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
48509
+ {
48510
+ fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'`
48511
+ }
48512
+ );
48513
+ }
48514
+ }
48515
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
48516
+ const before = { ...task };
48517
+ task.status = "done";
48518
+ task.completedAt = now2;
48519
+ task.updatedAt = now2;
48520
+ if (options.notes) {
48521
+ const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
48522
+ if (!task.notes) task.notes = [];
48523
+ task.notes.push(timestampedNote);
48524
+ }
48525
+ if (options.changeset) {
48526
+ if (!task.notes) task.notes = [];
48527
+ task.notes.push(`Changeset: ${options.changeset}`);
48528
+ }
48529
+ const autoCompleted = [];
48530
+ const autoCompletedTasks = [];
48531
+ if (task.parentId) {
48532
+ const parent = await acc.loadSingleTask(task.parentId);
48533
+ if (parent && parent.type === "epic" && !parent.noAutoComplete) {
48534
+ const siblings = await acc.getChildren(parent.id);
48535
+ const allDone = siblings.every(
48536
+ (c) => c.id === task.id || c.status === "done" || c.status === "cancelled"
48537
+ );
48538
+ if (allDone) {
48539
+ parent.status = "done";
48540
+ parent.completedAt = now2;
48541
+ parent.updatedAt = now2;
48542
+ autoCompleted.push(parent.id);
48543
+ autoCompletedTasks.push(parent);
48544
+ }
48545
+ }
48546
+ }
48547
+ await acc.transaction(async (tx) => {
48548
+ await tx.upsertSingleTask(task);
48549
+ for (const parentTask of autoCompletedTasks) {
48550
+ await tx.upsertSingleTask(parentTask);
48551
+ }
48552
+ await tx.appendLog({
48553
+ id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
48554
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48555
+ action: "task_completed",
48556
+ taskId: options.taskId,
48557
+ actor: "system",
48558
+ details: { title: task.title, previousStatus: before.status },
48559
+ before: null,
48560
+ after: { title: task.title, previousStatus: before.status }
48561
+ });
48562
+ });
48563
+ const dependents = await acc.getDependents(options.taskId);
48564
+ const unblockedTasks = [];
48565
+ for (const dep of dependents) {
48566
+ if (dep.status === "done" || dep.status === "cancelled") continue;
48567
+ if (dep.depends?.length) {
48568
+ const depDeps = await acc.loadTasks(dep.depends);
48569
+ const stillUnresolved = depDeps.filter(
48570
+ (d) => d.id !== options.taskId && d.status !== "done" && d.status !== "cancelled"
48571
+ );
48572
+ if (stillUnresolved.length === 0) {
48573
+ unblockedTasks.push({ id: dep.id, title: dep.title });
48574
+ }
48575
+ } else {
48576
+ unblockedTasks.push({ id: dep.id, title: dep.title });
48577
+ }
48578
+ }
48579
+ Promise.resolve().then(() => (init_auto_extract(), auto_extract_exports)).then(
48580
+ ({ extractTaskCompletionMemory: extractTaskCompletionMemory2 }) => extractTaskCompletionMemory2(cwd ?? process.cwd(), task)
48581
+ ).catch(() => {
48582
+ });
48583
+ return {
48584
+ task,
48585
+ ...autoCompleted.length > 0 && { autoCompleted },
48586
+ ...unblockedTasks.length > 0 && { unblockedTasks }
48587
+ };
48588
+ }
48589
+ var DEFAULT_VERIFICATION_REQUIRED_GATES, VERIFICATION_GATES;
48590
+ var init_complete = __esm({
48591
+ "packages/core/src/tasks/complete.ts"() {
48592
+ "use strict";
48593
+ init_src();
48594
+ init_config();
48595
+ init_errors3();
48596
+ init_session_enforcement();
48597
+ init_data_accessor();
48598
+ init_enforcement();
48599
+ DEFAULT_VERIFICATION_REQUIRED_GATES = [
48600
+ "implemented",
48601
+ "testsPassed",
48602
+ "qaPassed",
48603
+ "securityPassed",
48604
+ "documented"
48605
+ ];
48606
+ VERIFICATION_GATES = /* @__PURE__ */ new Set([
48607
+ "implemented",
48608
+ "testsPassed",
48609
+ "qaPassed",
48610
+ "cleanupDone",
48611
+ "securityPassed",
48612
+ "documented"
48613
+ ]);
48614
+ }
48615
+ });
48616
+
48617
+ // packages/core/src/tasks/update.ts
48618
+ var update_exports = {};
48619
+ __export(update_exports, {
48620
+ updateTask: () => updateTask
48621
+ });
48622
+ function hasNonStatusDoneFields(options) {
48623
+ return NON_STATUS_DONE_FIELDS.some((field) => options[field] !== void 0);
48624
+ }
48625
+ async function updateTask(options, cwd, accessor) {
48626
+ const acc = accessor ?? await getAccessor(cwd);
48627
+ const task = await acc.loadSingleTask(options.taskId);
48628
+ if (!task) {
48629
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
48630
+ fix: `Use 'cleo find "${options.taskId}"' to search`
48631
+ });
48632
+ }
48633
+ await requireActiveSession("tasks.update", cwd);
48634
+ const changes = [];
48635
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
48636
+ const isStatusOnlyDoneTransition = options.status === "done" && task.status !== "done" && !hasNonStatusDoneFields(options);
48637
+ if (isStatusOnlyDoneTransition) {
48638
+ const result = await completeTask({ taskId: options.taskId }, cwd, accessor);
48639
+ return { task: result.task, changes: ["status"] };
48640
+ }
48641
+ if (options.status === "done" && task.status !== "done") {
48642
+ throw new CleoError(
48643
+ 6 /* VALIDATION_ERROR */,
48644
+ "status=done must use complete flow; do not combine with other update fields",
48645
+ {
48646
+ fix: `Run 'cleo complete ${options.taskId}' first, then apply additional updates with 'cleo update ${options.taskId} ...'`
48647
+ }
48648
+ );
48649
+ }
48650
+ const enforcement = await createAcceptanceEnforcement(cwd);
48651
+ const updateValidation = enforcement.validateUpdate(task, { acceptance: options.acceptance });
48652
+ if (!updateValidation.valid) {
48653
+ throw new CleoError(
48654
+ updateValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
48655
+ updateValidation.error,
48656
+ { fix: updateValidation.fix }
48657
+ );
48658
+ }
48659
+ if (options.title !== void 0) {
48660
+ validateTitle(options.title);
48661
+ task.title = options.title;
48662
+ changes.push("title");
48663
+ }
48664
+ if (options.status !== void 0) {
48665
+ validateStatus(options.status);
48666
+ const oldStatus = task.status;
48667
+ task.status = options.status;
48668
+ changes.push("status");
48669
+ if (options.status === "done" && oldStatus !== "done") {
48670
+ task.completedAt = now2;
48671
+ }
48672
+ if (options.status === "cancelled" && oldStatus !== "cancelled") {
48673
+ task.cancelledAt = now2;
48674
+ }
48675
+ }
48676
+ if (options.priority !== void 0) {
48677
+ const normalizedPriority = normalizePriority(options.priority);
48678
+ task.priority = normalizedPriority;
48679
+ changes.push("priority");
48680
+ }
48681
+ if (options.type !== void 0) {
48682
+ validateTaskType(options.type);
48683
+ task.type = options.type;
48684
+ changes.push("type");
48685
+ }
48686
+ if (options.size !== void 0) {
48687
+ validateSize(options.size);
48688
+ task.size = options.size;
48689
+ changes.push("size");
48690
+ }
48691
+ if (options.phase !== void 0) {
48692
+ task.phase = options.phase;
48693
+ changes.push("phase");
48694
+ }
48695
+ if (options.description !== void 0) {
48696
+ task.description = options.description;
48697
+ changes.push("description");
48698
+ }
48699
+ if (options.labels !== void 0) {
48700
+ if (options.labels.length) validateLabels(options.labels);
48701
+ task.labels = options.labels;
48702
+ changes.push("labels");
48703
+ }
48704
+ if (options.addLabels?.length) {
48705
+ validateLabels(options.addLabels);
48706
+ const existing = new Set(task.labels ?? []);
48707
+ for (const l of options.addLabels) existing.add(l.trim());
48708
+ task.labels = [...existing];
48709
+ changes.push("labels");
48710
+ }
48711
+ if (options.removeLabels?.length) {
48712
+ const toRemove = new Set(options.removeLabels.map((l) => l.trim()));
48713
+ task.labels = (task.labels ?? []).filter((l) => !toRemove.has(l));
48714
+ changes.push("labels");
48715
+ }
48716
+ if (options.depends !== void 0) {
48717
+ task.depends = options.depends;
48718
+ changes.push("depends");
48719
+ }
48720
+ if (options.addDepends?.length) {
48721
+ const existing = new Set(task.depends ?? []);
48722
+ for (const d of options.addDepends) existing.add(d.trim());
48723
+ task.depends = [...existing];
48724
+ changes.push("depends");
48725
+ }
48726
+ if (options.removeDepends?.length) {
48727
+ const toRemove = new Set(options.removeDepends.map((d) => d.trim()));
48728
+ task.depends = (task.depends ?? []).filter((d) => !toRemove.has(d));
48729
+ changes.push("depends");
48730
+ }
48731
+ if (options.notes !== void 0) {
48732
+ const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
48733
+ if (!task.notes) task.notes = [];
48734
+ task.notes.push(timestampedNote);
48735
+ changes.push("notes");
48736
+ }
48737
+ if (options.acceptance !== void 0) {
48738
+ task.acceptance = options.acceptance;
48739
+ changes.push("acceptance");
48740
+ }
48741
+ if (options.files !== void 0) {
48742
+ task.files = options.files;
48743
+ changes.push("files");
48744
+ }
48745
+ if (options.blockedBy !== void 0) {
48746
+ task.blockedBy = options.blockedBy;
48747
+ changes.push("blockedBy");
48748
+ }
48749
+ if (options.noAutoComplete !== void 0) {
48750
+ task.noAutoComplete = options.noAutoComplete;
48751
+ changes.push("noAutoComplete");
48752
+ }
48753
+ if (options.pipelineStage !== void 0) {
48754
+ validatePipelineTransition(task.pipelineStage, options.pipelineStage);
48755
+ if (task.type === "epic" && task.pipelineStage) {
48756
+ await validateEpicStageAdvancement(
48757
+ {
48758
+ epicId: task.id,
48759
+ currentStage: task.pipelineStage,
48760
+ newStage: options.pipelineStage
48761
+ },
48762
+ acc,
48763
+ cwd
48764
+ );
48765
+ }
48766
+ if (task.type !== "epic") {
48767
+ const epicAncestor = task.parentId ? await findEpicAncestor(task.parentId, acc) : null;
48768
+ const directParent = task.parentId ? await acc.loadSingleTask(task.parentId) : null;
48769
+ const epicToCheck = directParent?.type === "epic" ? directParent : epicAncestor;
48770
+ if (epicToCheck) {
48771
+ await validateChildStageCeiling(
48772
+ { childStage: options.pipelineStage, epicId: epicToCheck.id },
48773
+ acc,
48774
+ cwd
48775
+ );
48776
+ }
48777
+ }
48778
+ task.pipelineStage = options.pipelineStage;
48779
+ changes.push("pipelineStage");
48780
+ }
48781
+ if (options.parentId !== void 0) {
48782
+ const newParentId = options.parentId || null;
48783
+ const currentParentId = task.parentId ?? null;
48784
+ if (newParentId !== currentParentId) {
48785
+ const originalType = task.type;
48786
+ if (!newParentId) {
48787
+ task.parentId = null;
48788
+ if (task.type === "subtask") task.type = "task";
48789
+ changes.push("parentId");
48790
+ if (task.type !== originalType) changes.push("type");
48791
+ } else {
48792
+ const newParent = await acc.loadSingleTask(newParentId);
48793
+ if (!newParent) {
48794
+ throw new CleoError(10 /* PARENT_NOT_FOUND */, `Parent task ${newParentId} not found`);
48795
+ }
48796
+ if (newParent.type === "subtask") {
48797
+ throw new CleoError(
48798
+ 13 /* INVALID_PARENT_TYPE */,
48799
+ `Cannot parent under subtask '${newParentId}'`
48800
+ );
48801
+ }
48802
+ const subtree = await acc.getSubtree(options.taskId);
48803
+ if (subtree.some((t) => t.id === newParentId)) {
48804
+ throw new CleoError(
48805
+ 14 /* CIRCULAR_REFERENCE */,
48806
+ `Moving '${options.taskId}' under '${newParentId}' would create a circular reference`
48807
+ );
48808
+ }
48809
+ const ancestors = await acc.getAncestorChain(newParentId);
48810
+ const parentDepth = ancestors.length;
48811
+ const config2 = await loadConfig(cwd);
48812
+ const policy = resolveHierarchyPolicy(config2);
48813
+ if (parentDepth + 1 >= policy.maxDepth) {
48814
+ throw new CleoError(
48815
+ 11 /* DEPTH_EXCEEDED */,
48816
+ `Maximum nesting depth ${policy.maxDepth} would be exceeded`
48817
+ );
48818
+ }
48819
+ task.parentId = newParentId;
48820
+ const newDepth = parentDepth + 1;
48821
+ if (newDepth === 1) task.type = "task";
48822
+ else if (newDepth >= 2) task.type = "subtask";
48823
+ changes.push("parentId");
48824
+ if (task.type !== originalType) changes.push("type");
48825
+ }
48826
+ }
48827
+ }
48828
+ if (changes.length === 0) {
48829
+ throw new CleoError(102 /* NO_CHANGE */, "No changes specified");
48830
+ }
48831
+ task.updatedAt = now2;
48832
+ await acc.transaction(async (tx) => {
48833
+ await tx.upsertSingleTask(task);
48834
+ await tx.appendLog({
48835
+ id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
48836
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48837
+ action: "task_updated",
48838
+ taskId: options.taskId,
48839
+ actor: "system",
48840
+ details: { changes, title: task.title },
48841
+ before: null,
48842
+ after: { changes, title: task.title }
48843
+ });
48844
+ });
48845
+ return { task, changes };
48846
+ }
48847
+ var NON_STATUS_DONE_FIELDS;
48848
+ var init_update2 = __esm({
48849
+ "packages/core/src/tasks/update.ts"() {
48850
+ "use strict";
48851
+ init_src();
48852
+ init_config();
48853
+ init_errors3();
48854
+ init_session_enforcement();
48855
+ init_data_accessor();
48856
+ init_add();
48857
+ init_complete();
48858
+ init_enforcement();
48859
+ init_epic_enforcement();
48860
+ init_hierarchy_policy();
48861
+ init_pipeline_stage();
48862
+ NON_STATUS_DONE_FIELDS = [
48863
+ "title",
48864
+ "priority",
48865
+ "type",
48866
+ "size",
48867
+ "phase",
48868
+ "description",
48869
+ "labels",
48870
+ "addLabels",
48871
+ "removeLabels",
48872
+ "depends",
48873
+ "addDepends",
48874
+ "removeDepends",
48875
+ "notes",
48876
+ "acceptance",
48877
+ "files",
48878
+ "blockedBy",
48879
+ "parentId",
48880
+ "noAutoComplete",
48881
+ "pipelineStage"
48882
+ ];
48883
+ }
48884
+ });
48885
+
48886
+ // packages/core/src/nexus/workspace.ts
48887
+ function checkRateLimit(agentId) {
48888
+ const now2 = Date.now();
48889
+ const entry = rateLimitCounters.get(agentId);
48890
+ if (!entry || now2 - entry.windowStart > RATE_LIMIT_WINDOW_MS) {
48891
+ rateLimitCounters.set(agentId, { count: 1, windowStart: now2 });
48892
+ return;
48893
+ }
48894
+ entry.count++;
48895
+ if (entry.count > RATE_LIMIT_MAX_OPS) {
48896
+ throw new CleoError(
48897
+ 1 /* GENERAL_ERROR */,
48898
+ `Agent '${agentId}' exceeded rate limit: ${RATE_LIMIT_MAX_OPS} routing ops per ${RATE_LIMIT_WINDOW_MS / 1e3}s`
48899
+ );
48900
+ }
48901
+ }
48902
+ async function loadProjectACL(projectPath) {
48903
+ try {
48904
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
48905
+ const config2 = await loadConfig4(projectPath);
48906
+ const agents = config2?.authorizedAgents;
48907
+ if (Array.isArray(agents) && agents.length > 0) {
48908
+ return { authorizedAgents: agents };
48909
+ }
48910
+ } catch {
48911
+ }
48912
+ return DEFAULT_ACL;
48913
+ }
48914
+ function isAuthorized(acl, agentId) {
48915
+ if (acl.authorizedAgents.includes("*")) return true;
48916
+ return acl.authorizedAgents.includes(agentId);
48917
+ }
48918
+ function parseDirective(message) {
48919
+ const content = message.content;
48920
+ const verbMatch = content.match(/^\/(\w+)/);
48921
+ if (!verbMatch) return null;
48922
+ const verb = verbMatch[1];
48923
+ const taskRefs = [];
48924
+ const pattern = new RegExp(TASK_REF_PATTERN.source, "g");
48925
+ for (const m of content.matchAll(pattern)) {
48926
+ taskRefs.push(`T${m[1]}`);
48927
+ }
48928
+ const metaRefs = message.metadata?.taskRefs;
48929
+ if (Array.isArray(metaRefs)) {
48930
+ for (const ref of metaRefs) {
48931
+ if (typeof ref === "string" && !taskRefs.includes(ref)) {
48932
+ taskRefs.push(ref);
48933
+ }
48934
+ }
48935
+ }
48936
+ if (taskRefs.length === 0) return null;
48937
+ return {
48938
+ verb,
48939
+ taskRefs,
48940
+ agentId: message.from,
48941
+ messageId: message.id,
48942
+ timestamp: message.timestamp
48943
+ };
48944
+ }
48945
+ async function routeDirective(directive) {
48946
+ checkRateLimit(directive.agentId);
48947
+ const results = [];
48948
+ const operation = VERB_TO_OPERATION[directive.verb];
48949
+ if (!operation) {
48950
+ return results;
48951
+ }
48952
+ const projects = await nexusList();
48953
+ for (const taskRef of directive.taskRefs) {
48954
+ const result = await routeSingleTask(taskRef, directive, operation, projects);
48955
+ results.push(result);
48956
+ }
48957
+ return results;
48958
+ }
48959
+ async function routeSingleTask(taskId, directive, operation, projects) {
48960
+ let targetProject = null;
48961
+ let targetAccessor = null;
48962
+ for (const project of projects) {
48963
+ try {
48964
+ const acc = await getAccessor(project.path);
48965
+ const { tasks: tasks2 } = await acc.queryTasks({});
48966
+ const task = tasks2.find((t) => t.id === taskId);
48967
+ if (task) {
48968
+ targetProject = project;
48969
+ targetAccessor = acc;
48970
+ break;
48971
+ }
48972
+ } catch {
48973
+ }
48974
+ }
48975
+ if (!targetProject || !targetAccessor) {
48976
+ return {
48977
+ success: false,
48978
+ project: "unknown",
48979
+ projectPath: "",
48980
+ taskId,
48981
+ operation,
48982
+ error: `Task ${taskId} not found in any registered project`
48983
+ };
48984
+ }
48985
+ const acl = await loadProjectACL(targetProject.path);
48986
+ if (!isAuthorized(acl, directive.agentId)) {
48987
+ return {
48988
+ success: false,
48989
+ project: targetProject.name,
48990
+ projectPath: targetProject.path,
48991
+ taskId,
48992
+ operation,
48993
+ error: `Agent '${directive.agentId}' not authorized to mutate project '${targetProject.name}'`
48994
+ };
48995
+ }
48996
+ try {
48997
+ await executeOperation(operation, taskId, targetProject.path, targetAccessor, directive);
48998
+ await logRouteAudit(directive, targetProject.name, taskId, operation, true);
48999
+ return {
49000
+ success: true,
49001
+ project: targetProject.name,
49002
+ projectPath: targetProject.path,
49003
+ taskId,
49004
+ operation
49005
+ };
49006
+ } catch (err) {
49007
+ const errorMsg = err instanceof Error ? err.message : String(err);
49008
+ await logRouteAudit(directive, targetProject.name, taskId, operation, false, errorMsg);
49009
+ return {
49010
+ success: false,
49011
+ project: targetProject.name,
49012
+ projectPath: targetProject.path,
49013
+ taskId,
49014
+ operation,
49015
+ error: errorMsg
49016
+ };
49017
+ }
49018
+ }
49019
+ async function executeOperation(operation, taskId, projectPath, accessor, directive) {
49020
+ switch (operation) {
49021
+ case "tasks.start": {
49022
+ const { startTask: startTask3 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
49023
+ await startTask3(taskId, projectPath, accessor);
49024
+ break;
49025
+ }
49026
+ case "tasks.complete": {
49027
+ const { completeTask: completeTask2 } = await Promise.resolve().then(() => (init_complete(), complete_exports));
49028
+ await completeTask2(
49029
+ { taskId, notes: `Completed via Conduit directive from ${directive.agentId}` },
49030
+ projectPath,
49031
+ accessor
49032
+ );
49033
+ break;
49034
+ }
49035
+ case "tasks.stop": {
49036
+ const { stopTask: stopTask3 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
49037
+ await stopTask3(projectPath, accessor);
49038
+ break;
49039
+ }
49040
+ case "tasks.update": {
49041
+ const { updateTask: updateTask3 } = await Promise.resolve().then(() => (init_update2(), update_exports));
49042
+ await updateTask3(
49043
+ { taskId, notes: `Marked blocked via Conduit directive from ${directive.agentId}` },
49044
+ projectPath,
49045
+ accessor
49046
+ );
49047
+ break;
49048
+ }
49049
+ }
49050
+ }
49051
+ async function logRouteAudit(directive, projectName, taskId, operation, success2, error40) {
49052
+ try {
49053
+ const { getLogger: getLogger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
49054
+ const log11 = getLogger2("nexus.route");
49055
+ const level = success2 ? "info" : "warn";
49056
+ log11[level](
49057
+ {
49058
+ directive: directive.verb,
49059
+ agentId: directive.agentId,
49060
+ messageId: directive.messageId,
49061
+ project: projectName,
49062
+ taskId,
49063
+ operation,
49064
+ success: success2,
49065
+ error: error40
49066
+ },
49067
+ `Conduit directive routed: ${directive.verb} ${taskId} \u2192 ${projectName} (${success2 ? "OK" : "FAILED"})`
49068
+ );
49069
+ } catch {
49070
+ }
49071
+ }
49072
+ async function workspaceStatus() {
49073
+ const projects = await nexusList();
49074
+ const summaries = [];
49075
+ const totals = { pending: 0, active: 0, done: 0, total: 0 };
49076
+ for (const project of projects) {
49077
+ try {
49078
+ const acc = await getAccessor(project.path);
49079
+ const { tasks: tasks2 } = await acc.queryTasks({});
49080
+ const counts2 = {
49081
+ pending: tasks2.filter((t) => t.status === "pending").length,
49082
+ active: tasks2.filter((t) => t.status === "active").length,
49083
+ done: tasks2.filter((t) => t.status === "done").length,
49084
+ total: tasks2.length
49085
+ };
49086
+ summaries.push({
49087
+ name: project.name,
49088
+ path: project.path,
49089
+ counts: counts2,
49090
+ health: project.healthStatus,
49091
+ lastSync: project.lastSync
49092
+ });
49093
+ totals.pending += counts2.pending;
49094
+ totals.active += counts2.active;
49095
+ totals.done += counts2.done;
49096
+ totals.total += counts2.total;
49097
+ } catch {
49098
+ summaries.push({
49099
+ name: project.name,
49100
+ path: project.path,
49101
+ counts: { pending: 0, active: 0, done: 0, total: 0 },
49102
+ health: "unreachable",
49103
+ lastSync: project.lastSync
49104
+ });
49105
+ }
49106
+ }
49107
+ return {
49108
+ projectCount: projects.length,
49109
+ projects: summaries,
49110
+ totals,
49111
+ computedAt: (/* @__PURE__ */ new Date()).toISOString()
49112
+ };
49113
+ }
49114
+ async function workspaceAgents() {
49115
+ const projects = await nexusList();
49116
+ const agents = [];
49117
+ for (const project of projects) {
49118
+ try {
49119
+ const { listAgentInstances: listAgentInstances2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports2));
49120
+ const instances = await listAgentInstances2(void 0, project.path);
49121
+ for (const inst of instances) {
49122
+ agents.push({
49123
+ agentId: inst.id,
49124
+ agentType: inst.agentType,
49125
+ status: inst.status,
49126
+ project: project.name,
49127
+ taskId: inst.taskId ?? null,
49128
+ lastHeartbeat: inst.lastHeartbeat
49129
+ });
49130
+ }
49131
+ } catch {
49132
+ }
49133
+ }
49134
+ return agents;
49135
+ }
49136
+ var RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_OPS, rateLimitCounters, DEFAULT_ACL, TASK_REF_PATTERN, VERB_TO_OPERATION;
49137
+ var init_workspace = __esm({
49138
+ "packages/core/src/nexus/workspace.ts"() {
49139
+ "use strict";
49140
+ init_src();
49141
+ init_errors3();
49142
+ init_data_accessor();
49143
+ init_registry3();
49144
+ RATE_LIMIT_WINDOW_MS = 6e4;
49145
+ RATE_LIMIT_MAX_OPS = 100;
49146
+ rateLimitCounters = /* @__PURE__ */ new Map();
49147
+ DEFAULT_ACL = { authorizedAgents: ["*"] };
49148
+ TASK_REF_PATTERN = /\bT(\d+)\b/g;
49149
+ VERB_TO_OPERATION = {
49150
+ claim: "tasks.start",
49151
+ done: "tasks.complete",
49152
+ complete: "tasks.complete",
49153
+ blocked: "tasks.update",
49154
+ // Update status to blocked
49155
+ start: "tasks.start",
49156
+ stop: "tasks.stop"
49157
+ };
49158
+ }
49159
+ });
49160
+
47932
49161
  // packages/core/src/nexus/index.ts
47933
49162
  var nexus_exports = {};
47934
49163
  __export(nexus_exports, {
@@ -47963,6 +49192,7 @@ __export(nexus_exports, {
47963
49192
  nexusSyncAll: () => nexusSyncAll,
47964
49193
  nexusUnregister: () => nexusUnregister,
47965
49194
  orphanDetection: () => orphanDetection,
49195
+ parseDirective: () => parseDirective,
47966
49196
  parseQuery: () => parseQuery,
47967
49197
  permissionLevel: () => permissionLevel,
47968
49198
  previewTransfer: () => previewTransfer,
@@ -47973,10 +49203,13 @@ __export(nexus_exports, {
47973
49203
  resolveCrossDeps: () => resolveCrossDeps,
47974
49204
  resolveProjectPath: () => resolveProjectPath2,
47975
49205
  resolveTask: () => resolveTask,
49206
+ routeDirective: () => routeDirective,
47976
49207
  searchAcrossProjects: () => searchAcrossProjects,
47977
49208
  setPermission: () => setPermission,
47978
49209
  syncGitignore: () => syncGitignore,
47979
- validateSyntax: () => validateSyntax
49210
+ validateSyntax: () => validateSyntax,
49211
+ workspaceAgents: () => workspaceAgents,
49212
+ workspaceStatus: () => workspaceStatus
47980
49213
  });
47981
49214
  var init_nexus = __esm({
47982
49215
  "packages/core/src/nexus/index.ts"() {
@@ -47989,6 +49222,7 @@ var init_nexus = __esm({
47989
49222
  init_registry3();
47990
49223
  init_sharing();
47991
49224
  init_transfer();
49225
+ init_workspace();
47992
49226
  }
47993
49227
  });
47994
49228
 
@@ -49524,485 +50758,6 @@ var init_pipeline2 = __esm({
49524
50758
  }
49525
50759
  });
49526
50760
 
49527
- // packages/core/src/tasks/complete.ts
49528
- function isVerificationGate(value) {
49529
- return VERIFICATION_GATES.has(value);
49530
- }
49531
- async function loadCompletionEnforcement(cwd) {
49532
- const isTest = !!process.env.VITEST;
49533
- const config2 = await loadConfig(cwd);
49534
- const acceptance = config2.enforcement?.acceptance;
49535
- const verificationCfg = config2.verification;
49536
- const acceptanceMode = acceptance?.mode ?? (isTest ? "off" : "block");
49537
- const acceptanceRequiredForPriorities = acceptance?.requiredForPriorities ?? (isTest ? [] : ["critical", "high", "medium", "low"]);
49538
- const rawVerificationEnabled = await getRawConfigValue("verification.enabled", cwd);
49539
- const verificationEnabled = rawVerificationEnabled !== void 0 ? rawVerificationEnabled : !isTest;
49540
- const verificationRequiredGates = (verificationCfg?.requiredGates ?? []).filter(isVerificationGate).length > 0 ? (verificationCfg?.requiredGates ?? []).filter(isVerificationGate) : DEFAULT_VERIFICATION_REQUIRED_GATES;
49541
- const verificationMaxRounds = verificationCfg?.maxRounds ?? 5;
49542
- const lifecycleMode = config2.lifecycle?.mode ?? (isTest ? "off" : "strict");
49543
- return {
49544
- acceptanceMode,
49545
- acceptanceRequiredForPriorities,
49546
- verificationEnabled,
49547
- verificationRequiredGates,
49548
- verificationMaxRounds,
49549
- lifecycleMode
49550
- };
49551
- }
49552
- async function completeTask(options, cwd, accessor) {
49553
- const acc = accessor ?? await getAccessor(cwd);
49554
- const task = await acc.loadSingleTask(options.taskId);
49555
- if (!task) {
49556
- throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
49557
- fix: `Use 'cleo find "${options.taskId}"' to search`
49558
- });
49559
- }
49560
- await requireActiveSession("tasks.complete", cwd);
49561
- const enforcement = await loadCompletionEnforcement(cwd);
49562
- if (task.status === "done") {
49563
- throw new CleoError(17 /* TASK_COMPLETED */, `Task ${options.taskId} is already completed`);
49564
- }
49565
- if (task.depends?.length) {
49566
- const deps = await acc.loadTasks(task.depends);
49567
- const incompleteDeps = deps.filter((d) => d.status !== "done" && d.status !== "cancelled").map((d) => d.id);
49568
- if (incompleteDeps.length > 0) {
49569
- throw new CleoError(
49570
- 5 /* DEPENDENCY_ERROR */,
49571
- `Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
49572
- {
49573
- fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}`
49574
- }
49575
- );
49576
- }
49577
- }
49578
- const acceptanceEnforcement = await createAcceptanceEnforcement(cwd);
49579
- const completionValidation = acceptanceEnforcement.validateCompletion(task);
49580
- if (!completionValidation.valid) {
49581
- throw new CleoError(
49582
- completionValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
49583
- completionValidation.error,
49584
- { fix: completionValidation.fix }
49585
- );
49586
- }
49587
- if (enforcement.verificationEnabled && task.type !== "epic") {
49588
- if (!task.verification) {
49589
- throw new CleoError(
49590
- 40 /* VERIFICATION_INIT_FAILED */,
49591
- `Task ${options.taskId} is missing verification metadata`,
49592
- {
49593
- fix: `Initialize verification for ${options.taskId} before completion`
49594
- }
49595
- );
49596
- }
49597
- if (task.verification.round > enforcement.verificationMaxRounds) {
49598
- throw new CleoError(
49599
- 44 /* MAX_ROUNDS_EXCEEDED */,
49600
- `Task ${options.taskId} exceeded verification max rounds (${enforcement.verificationMaxRounds})`,
49601
- {
49602
- fix: `Review failure log and resolve blockers before retrying completion`
49603
- }
49604
- );
49605
- }
49606
- const missingRequiredGates = enforcement.verificationRequiredGates.filter(
49607
- (gate) => task.verification?.gates?.[gate] !== true
49608
- );
49609
- if (missingRequiredGates.length > 0 || task.verification.passed !== true) {
49610
- const exitCode = enforcement.lifecycleMode === "strict" ? 80 /* LIFECYCLE_GATE_FAILED */ : 45 /* GATE_DEPENDENCY */;
49611
- throw new CleoError(
49612
- exitCode,
49613
- `Task ${options.taskId} failed verification gates: ${missingRequiredGates.join(", ") || "verification.passed=false"}`,
49614
- {
49615
- fix: `Set required verification gates before completion: ${enforcement.verificationRequiredGates.join(", ")}`
49616
- }
49617
- );
49618
- }
49619
- }
49620
- const children = await acc.getChildren(options.taskId);
49621
- const incompleteChildren = children.filter(
49622
- (c) => c.status !== "done" && c.status !== "cancelled"
49623
- );
49624
- if (incompleteChildren.length > 0 && task.type === "epic") {
49625
- if (!task.noAutoComplete) {
49626
- throw new CleoError(
49627
- 16 /* HAS_CHILDREN */,
49628
- `Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
49629
- {
49630
- fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'`
49631
- }
49632
- );
49633
- }
49634
- }
49635
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
49636
- const before = { ...task };
49637
- task.status = "done";
49638
- task.completedAt = now2;
49639
- task.updatedAt = now2;
49640
- if (options.notes) {
49641
- const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
49642
- if (!task.notes) task.notes = [];
49643
- task.notes.push(timestampedNote);
49644
- }
49645
- if (options.changeset) {
49646
- if (!task.notes) task.notes = [];
49647
- task.notes.push(`Changeset: ${options.changeset}`);
49648
- }
49649
- const autoCompleted = [];
49650
- const autoCompletedTasks = [];
49651
- if (task.parentId) {
49652
- const parent = await acc.loadSingleTask(task.parentId);
49653
- if (parent && parent.type === "epic" && !parent.noAutoComplete) {
49654
- const siblings = await acc.getChildren(parent.id);
49655
- const allDone = siblings.every(
49656
- (c) => c.id === task.id || c.status === "done" || c.status === "cancelled"
49657
- );
49658
- if (allDone) {
49659
- parent.status = "done";
49660
- parent.completedAt = now2;
49661
- parent.updatedAt = now2;
49662
- autoCompleted.push(parent.id);
49663
- autoCompletedTasks.push(parent);
49664
- }
49665
- }
49666
- }
49667
- await acc.transaction(async (tx) => {
49668
- await tx.upsertSingleTask(task);
49669
- for (const parentTask of autoCompletedTasks) {
49670
- await tx.upsertSingleTask(parentTask);
49671
- }
49672
- await tx.appendLog({
49673
- id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
49674
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
49675
- action: "task_completed",
49676
- taskId: options.taskId,
49677
- actor: "system",
49678
- details: { title: task.title, previousStatus: before.status },
49679
- before: null,
49680
- after: { title: task.title, previousStatus: before.status }
49681
- });
49682
- });
49683
- const dependents = await acc.getDependents(options.taskId);
49684
- const unblockedTasks = [];
49685
- for (const dep of dependents) {
49686
- if (dep.status === "done" || dep.status === "cancelled") continue;
49687
- if (dep.depends?.length) {
49688
- const depDeps = await acc.loadTasks(dep.depends);
49689
- const stillUnresolved = depDeps.filter(
49690
- (d) => d.id !== options.taskId && d.status !== "done" && d.status !== "cancelled"
49691
- );
49692
- if (stillUnresolved.length === 0) {
49693
- unblockedTasks.push({ id: dep.id, title: dep.title });
49694
- }
49695
- } else {
49696
- unblockedTasks.push({ id: dep.id, title: dep.title });
49697
- }
49698
- }
49699
- Promise.resolve().then(() => (init_auto_extract(), auto_extract_exports)).then(
49700
- ({ extractTaskCompletionMemory: extractTaskCompletionMemory2 }) => extractTaskCompletionMemory2(cwd ?? process.cwd(), task)
49701
- ).catch(() => {
49702
- });
49703
- return {
49704
- task,
49705
- ...autoCompleted.length > 0 && { autoCompleted },
49706
- ...unblockedTasks.length > 0 && { unblockedTasks }
49707
- };
49708
- }
49709
- var DEFAULT_VERIFICATION_REQUIRED_GATES, VERIFICATION_GATES;
49710
- var init_complete = __esm({
49711
- "packages/core/src/tasks/complete.ts"() {
49712
- "use strict";
49713
- init_src();
49714
- init_config();
49715
- init_errors3();
49716
- init_session_enforcement();
49717
- init_data_accessor();
49718
- init_enforcement();
49719
- DEFAULT_VERIFICATION_REQUIRED_GATES = [
49720
- "implemented",
49721
- "testsPassed",
49722
- "qaPassed",
49723
- "securityPassed",
49724
- "documented"
49725
- ];
49726
- VERIFICATION_GATES = /* @__PURE__ */ new Set([
49727
- "implemented",
49728
- "testsPassed",
49729
- "qaPassed",
49730
- "cleanupDone",
49731
- "securityPassed",
49732
- "documented"
49733
- ]);
49734
- }
49735
- });
49736
-
49737
- // packages/core/src/tasks/update.ts
49738
- var update_exports = {};
49739
- __export(update_exports, {
49740
- updateTask: () => updateTask
49741
- });
49742
- function hasNonStatusDoneFields(options) {
49743
- return NON_STATUS_DONE_FIELDS.some((field) => options[field] !== void 0);
49744
- }
49745
- async function updateTask(options, cwd, accessor) {
49746
- const acc = accessor ?? await getAccessor(cwd);
49747
- const task = await acc.loadSingleTask(options.taskId);
49748
- if (!task) {
49749
- throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
49750
- fix: `Use 'cleo find "${options.taskId}"' to search`
49751
- });
49752
- }
49753
- await requireActiveSession("tasks.update", cwd);
49754
- const changes = [];
49755
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
49756
- const isStatusOnlyDoneTransition = options.status === "done" && task.status !== "done" && !hasNonStatusDoneFields(options);
49757
- if (isStatusOnlyDoneTransition) {
49758
- const result = await completeTask({ taskId: options.taskId }, cwd, accessor);
49759
- return { task: result.task, changes: ["status"] };
49760
- }
49761
- if (options.status === "done" && task.status !== "done") {
49762
- throw new CleoError(
49763
- 6 /* VALIDATION_ERROR */,
49764
- "status=done must use complete flow; do not combine with other update fields",
49765
- {
49766
- fix: `Run 'cleo complete ${options.taskId}' first, then apply additional updates with 'cleo update ${options.taskId} ...'`
49767
- }
49768
- );
49769
- }
49770
- const enforcement = await createAcceptanceEnforcement(cwd);
49771
- const updateValidation = enforcement.validateUpdate(task, { acceptance: options.acceptance });
49772
- if (!updateValidation.valid) {
49773
- throw new CleoError(
49774
- updateValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
49775
- updateValidation.error,
49776
- { fix: updateValidation.fix }
49777
- );
49778
- }
49779
- if (options.title !== void 0) {
49780
- validateTitle(options.title);
49781
- task.title = options.title;
49782
- changes.push("title");
49783
- }
49784
- if (options.status !== void 0) {
49785
- validateStatus(options.status);
49786
- const oldStatus = task.status;
49787
- task.status = options.status;
49788
- changes.push("status");
49789
- if (options.status === "done" && oldStatus !== "done") {
49790
- task.completedAt = now2;
49791
- }
49792
- if (options.status === "cancelled" && oldStatus !== "cancelled") {
49793
- task.cancelledAt = now2;
49794
- }
49795
- }
49796
- if (options.priority !== void 0) {
49797
- const normalizedPriority = normalizePriority(options.priority);
49798
- task.priority = normalizedPriority;
49799
- changes.push("priority");
49800
- }
49801
- if (options.type !== void 0) {
49802
- validateTaskType(options.type);
49803
- task.type = options.type;
49804
- changes.push("type");
49805
- }
49806
- if (options.size !== void 0) {
49807
- validateSize(options.size);
49808
- task.size = options.size;
49809
- changes.push("size");
49810
- }
49811
- if (options.phase !== void 0) {
49812
- task.phase = options.phase;
49813
- changes.push("phase");
49814
- }
49815
- if (options.description !== void 0) {
49816
- task.description = options.description;
49817
- changes.push("description");
49818
- }
49819
- if (options.labels !== void 0) {
49820
- if (options.labels.length) validateLabels(options.labels);
49821
- task.labels = options.labels;
49822
- changes.push("labels");
49823
- }
49824
- if (options.addLabels?.length) {
49825
- validateLabels(options.addLabels);
49826
- const existing = new Set(task.labels ?? []);
49827
- for (const l of options.addLabels) existing.add(l.trim());
49828
- task.labels = [...existing];
49829
- changes.push("labels");
49830
- }
49831
- if (options.removeLabels?.length) {
49832
- const toRemove = new Set(options.removeLabels.map((l) => l.trim()));
49833
- task.labels = (task.labels ?? []).filter((l) => !toRemove.has(l));
49834
- changes.push("labels");
49835
- }
49836
- if (options.depends !== void 0) {
49837
- task.depends = options.depends;
49838
- changes.push("depends");
49839
- }
49840
- if (options.addDepends?.length) {
49841
- const existing = new Set(task.depends ?? []);
49842
- for (const d of options.addDepends) existing.add(d.trim());
49843
- task.depends = [...existing];
49844
- changes.push("depends");
49845
- }
49846
- if (options.removeDepends?.length) {
49847
- const toRemove = new Set(options.removeDepends.map((d) => d.trim()));
49848
- task.depends = (task.depends ?? []).filter((d) => !toRemove.has(d));
49849
- changes.push("depends");
49850
- }
49851
- if (options.notes !== void 0) {
49852
- const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
49853
- if (!task.notes) task.notes = [];
49854
- task.notes.push(timestampedNote);
49855
- changes.push("notes");
49856
- }
49857
- if (options.acceptance !== void 0) {
49858
- task.acceptance = options.acceptance;
49859
- changes.push("acceptance");
49860
- }
49861
- if (options.files !== void 0) {
49862
- task.files = options.files;
49863
- changes.push("files");
49864
- }
49865
- if (options.blockedBy !== void 0) {
49866
- task.blockedBy = options.blockedBy;
49867
- changes.push("blockedBy");
49868
- }
49869
- if (options.noAutoComplete !== void 0) {
49870
- task.noAutoComplete = options.noAutoComplete;
49871
- changes.push("noAutoComplete");
49872
- }
49873
- if (options.pipelineStage !== void 0) {
49874
- validatePipelineTransition(task.pipelineStage, options.pipelineStage);
49875
- if (task.type === "epic" && task.pipelineStage) {
49876
- await validateEpicStageAdvancement(
49877
- {
49878
- epicId: task.id,
49879
- currentStage: task.pipelineStage,
49880
- newStage: options.pipelineStage
49881
- },
49882
- acc,
49883
- cwd
49884
- );
49885
- }
49886
- if (task.type !== "epic") {
49887
- const epicAncestor = task.parentId ? await findEpicAncestor(task.parentId, acc) : null;
49888
- const directParent = task.parentId ? await acc.loadSingleTask(task.parentId) : null;
49889
- const epicToCheck = directParent?.type === "epic" ? directParent : epicAncestor;
49890
- if (epicToCheck) {
49891
- await validateChildStageCeiling(
49892
- { childStage: options.pipelineStage, epicId: epicToCheck.id },
49893
- acc,
49894
- cwd
49895
- );
49896
- }
49897
- }
49898
- task.pipelineStage = options.pipelineStage;
49899
- changes.push("pipelineStage");
49900
- }
49901
- if (options.parentId !== void 0) {
49902
- const newParentId = options.parentId || null;
49903
- const currentParentId = task.parentId ?? null;
49904
- if (newParentId !== currentParentId) {
49905
- const originalType = task.type;
49906
- if (!newParentId) {
49907
- task.parentId = null;
49908
- if (task.type === "subtask") task.type = "task";
49909
- changes.push("parentId");
49910
- if (task.type !== originalType) changes.push("type");
49911
- } else {
49912
- const newParent = await acc.loadSingleTask(newParentId);
49913
- if (!newParent) {
49914
- throw new CleoError(10 /* PARENT_NOT_FOUND */, `Parent task ${newParentId} not found`);
49915
- }
49916
- if (newParent.type === "subtask") {
49917
- throw new CleoError(
49918
- 13 /* INVALID_PARENT_TYPE */,
49919
- `Cannot parent under subtask '${newParentId}'`
49920
- );
49921
- }
49922
- const subtree = await acc.getSubtree(options.taskId);
49923
- if (subtree.some((t) => t.id === newParentId)) {
49924
- throw new CleoError(
49925
- 14 /* CIRCULAR_REFERENCE */,
49926
- `Moving '${options.taskId}' under '${newParentId}' would create a circular reference`
49927
- );
49928
- }
49929
- const ancestors = await acc.getAncestorChain(newParentId);
49930
- const parentDepth = ancestors.length;
49931
- const config2 = await loadConfig(cwd);
49932
- const policy = resolveHierarchyPolicy(config2);
49933
- if (parentDepth + 1 >= policy.maxDepth) {
49934
- throw new CleoError(
49935
- 11 /* DEPTH_EXCEEDED */,
49936
- `Maximum nesting depth ${policy.maxDepth} would be exceeded`
49937
- );
49938
- }
49939
- task.parentId = newParentId;
49940
- const newDepth = parentDepth + 1;
49941
- if (newDepth === 1) task.type = "task";
49942
- else if (newDepth >= 2) task.type = "subtask";
49943
- changes.push("parentId");
49944
- if (task.type !== originalType) changes.push("type");
49945
- }
49946
- }
49947
- }
49948
- if (changes.length === 0) {
49949
- throw new CleoError(102 /* NO_CHANGE */, "No changes specified");
49950
- }
49951
- task.updatedAt = now2;
49952
- await acc.transaction(async (tx) => {
49953
- await tx.upsertSingleTask(task);
49954
- await tx.appendLog({
49955
- id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
49956
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
49957
- action: "task_updated",
49958
- taskId: options.taskId,
49959
- actor: "system",
49960
- details: { changes, title: task.title },
49961
- before: null,
49962
- after: { changes, title: task.title }
49963
- });
49964
- });
49965
- return { task, changes };
49966
- }
49967
- var NON_STATUS_DONE_FIELDS;
49968
- var init_update2 = __esm({
49969
- "packages/core/src/tasks/update.ts"() {
49970
- "use strict";
49971
- init_src();
49972
- init_config();
49973
- init_errors3();
49974
- init_session_enforcement();
49975
- init_data_accessor();
49976
- init_add();
49977
- init_complete();
49978
- init_enforcement();
49979
- init_epic_enforcement();
49980
- init_hierarchy_policy();
49981
- init_pipeline_stage();
49982
- NON_STATUS_DONE_FIELDS = [
49983
- "title",
49984
- "priority",
49985
- "type",
49986
- "size",
49987
- "phase",
49988
- "description",
49989
- "labels",
49990
- "addLabels",
49991
- "removeLabels",
49992
- "depends",
49993
- "addDepends",
49994
- "removeDepends",
49995
- "notes",
49996
- "acceptance",
49997
- "files",
49998
- "blockedBy",
49999
- "parentId",
50000
- "noAutoComplete",
50001
- "pipelineStage"
50002
- ];
50003
- }
50004
- });
50005
-
50006
50761
  // packages/core/src/reconciliation/reconciliation-engine.ts
50007
50762
  function buildTaskMap(tasks2) {
50008
50763
  const map2 = /* @__PURE__ */ new Map();
@@ -62387,145 +63142,6 @@ var init_system2 = __esm({
62387
63142
  }
62388
63143
  });
62389
63144
 
62390
- // packages/core/src/task-work/index.ts
62391
- var task_work_exports = {};
62392
- __export(task_work_exports, {
62393
- currentTask: () => currentTask,
62394
- getTaskHistory: () => getTaskHistory,
62395
- getWorkHistory: () => getWorkHistory,
62396
- startTask: () => startTask,
62397
- stopTask: () => stopTask
62398
- });
62399
- async function currentTask(cwd, accessor) {
62400
- const acc = accessor ?? await getAccessor(cwd);
62401
- const focus = await acc.getMetaValue("focus_state");
62402
- return {
62403
- currentTask: focus?.currentTask ?? null,
62404
- currentPhase: focus?.currentPhase ?? null,
62405
- sessionNote: focus?.sessionNote ?? null,
62406
- nextAction: focus?.nextAction ?? null
62407
- };
62408
- }
62409
- async function startTask(taskId, cwd, accessor) {
62410
- if (!taskId) {
62411
- throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
62412
- }
62413
- const acc = accessor ?? await getAccessor(cwd);
62414
- const task = await acc.loadSingleTask(taskId);
62415
- if (!task) {
62416
- throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
62417
- fix: `Use 'cleo find "${taskId}"' to search`
62418
- });
62419
- }
62420
- const { tasks: allTasks } = await acc.queryTasks({});
62421
- const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
62422
- if (unresolvedDeps.length > 0) {
62423
- throw new CleoError(
62424
- 5 /* DEPENDENCY_ERROR */,
62425
- `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
62426
- {
62427
- fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
62428
- }
62429
- );
62430
- }
62431
- const focus = await acc.getMetaValue("focus_state") ?? {};
62432
- const previousTask = focus.currentTask ?? null;
62433
- focus.currentTask = taskId;
62434
- focus.currentPhase = task.phase ?? null;
62435
- const noteEntry = {
62436
- note: `Started work on ${taskId}: ${task.title}`,
62437
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
62438
- };
62439
- if (!focus.sessionNotes) {
62440
- focus.sessionNotes = [];
62441
- }
62442
- focus.sessionNotes.push(noteEntry);
62443
- await acc.setMetaValue("focus_state", focus);
62444
- await logOperation(
62445
- "task_start",
62446
- taskId,
62447
- {
62448
- previousTask,
62449
- title: task.title
62450
- },
62451
- accessor
62452
- );
62453
- const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
62454
- hooks2.dispatch("onToolStart", cwd ?? process.cwd(), {
62455
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
62456
- taskId,
62457
- taskTitle: task.title
62458
- }).catch(() => {
62459
- });
62460
- return {
62461
- taskId,
62462
- taskTitle: task.title,
62463
- previousTask
62464
- };
62465
- }
62466
- async function stopTask(cwd, accessor) {
62467
- const acc = accessor ?? await getAccessor(cwd);
62468
- const focus = await acc.getMetaValue("focus_state");
62469
- const previousTask = focus?.currentTask ?? null;
62470
- if (!focus) {
62471
- return { previousTask: null };
62472
- }
62473
- const taskId = focus.currentTask;
62474
- const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
62475
- focus.currentTask = null;
62476
- focus.nextAction = null;
62477
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
62478
- if (taskId && task) {
62479
- const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
62480
- hooks2.dispatch("onToolComplete", cwd ?? process.cwd(), {
62481
- timestamp: now2,
62482
- taskId,
62483
- taskTitle: task.title,
62484
- status: "done"
62485
- }).catch(() => {
62486
- });
62487
- }
62488
- await acc.setMetaValue("focus_state", focus);
62489
- await logOperation(
62490
- "task_stop",
62491
- previousTask ?? "none",
62492
- {
62493
- previousTask
62494
- },
62495
- accessor
62496
- );
62497
- return { previousTask };
62498
- }
62499
- async function getWorkHistory(cwd, accessor) {
62500
- const acc = accessor ?? await getAccessor(cwd);
62501
- const focus = await acc.getMetaValue("focus_state");
62502
- const notes = focus?.sessionNotes ?? [];
62503
- const history = [];
62504
- for (const note of notes) {
62505
- const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
62506
- if (match) {
62507
- history.push({
62508
- taskId: match[1],
62509
- timestamp: note.timestamp
62510
- });
62511
- }
62512
- }
62513
- return history.reverse();
62514
- }
62515
- var getTaskHistory;
62516
- var init_task_work = __esm({
62517
- "packages/core/src/task-work/index.ts"() {
62518
- "use strict";
62519
- init_src();
62520
- init_errors3();
62521
- init_data_accessor();
62522
- init_add();
62523
- init_dependency_check();
62524
- init_handlers();
62525
- getTaskHistory = getWorkHistory;
62526
- }
62527
- });
62528
-
62529
63145
  // packages/core/src/tasks/archive.ts
62530
63146
  async function archiveTasks(options = {}, cwd, accessor) {
62531
63147
  const acc = accessor ?? await getAccessor(cwd);
@@ -63688,12 +64304,12 @@ function parseCommonFlags(args) {
63688
64304
  flags.remaining = remaining;
63689
64305
  return flags;
63690
64306
  }
63691
- function resolveFormat(flagFormat) {
64307
+ function resolveFormat2(flagFormat) {
63692
64308
  if (flagFormat === "json" || flagFormat === "human") return flagFormat;
63693
64309
  return process.stdout.isTTY ? "human" : "json";
63694
64310
  }
63695
64311
  function isJsonOutput(flags) {
63696
- return resolveFormat(flags.format) === "json";
64312
+ return resolveFormat2(flags.format) === "json";
63697
64313
  }
63698
64314
  var init_flags = __esm({
63699
64315
  "packages/core/src/ui/flags.ts"() {
@@ -63728,7 +64344,7 @@ __export(ui_exports, {
63728
64344
  parseCommandHeader: () => parseCommandHeader,
63729
64345
  parseCommonFlags: () => parseCommonFlags,
63730
64346
  removeAliases: () => removeAliases,
63731
- resolveFormat: () => resolveFormat,
64347
+ resolveFormat: () => resolveFormat2,
63732
64348
  scanAllCommands: () => scanAllCommands,
63733
64349
  validateHeader: () => validateHeader,
63734
64350
  writeChangelogFile: () => writeChangelogFile
@@ -68014,7 +68630,7 @@ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
68014
68630
  await acc.upsertSingleSession(restoredSession);
68015
68631
  try {
68016
68632
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
68017
- await hooks2.dispatch("onSessionStart", projectRoot, {
68633
+ await hooks2.dispatch("SessionStart", projectRoot, {
68018
68634
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
68019
68635
  sessionId: restoredSession.id,
68020
68636
  name: restoredSession.name,
@@ -68057,6 +68673,7 @@ var init_cleo = __esm({
68057
68673
  init_permissions();
68058
68674
  init_registry3();
68059
68675
  init_sharing();
68676
+ init_workspace();
68060
68677
  init_orchestration();
68061
68678
  init_reconciliation();
68062
68679
  init_link_store();
@@ -68295,7 +68912,14 @@ var init_cleo = __esm({
68295
68912
  discover: (p) => discoverRelated(p.query, p.method, p.limit),
68296
68913
  search: (p) => searchAcrossProjects(p.pattern, p.project, p.limit),
68297
68914
  setPermission: (p) => setPermission(p.name, p.level),
68298
- sharingStatus: () => getSharingStatus()
68915
+ sharingStatus: () => getSharingStatus(),
68916
+ route: (message) => {
68917
+ const directive = parseDirective(message);
68918
+ if (!directive) return Promise.resolve([]);
68919
+ return routeDirective(directive);
68920
+ },
68921
+ workspaceStatus: () => workspaceStatus(),
68922
+ workspaceAgents: () => workspaceAgents()
68299
68923
  };
68300
68924
  }
68301
68925
  // === Agents ===
@@ -69839,6 +70463,14 @@ var init_protocol_enforcement = __esm({
69839
70463
  });
69840
70464
 
69841
70465
  // packages/core/src/hooks/types.ts
70466
+ import {
70467
+ buildHookMatrix,
70468
+ CANONICAL_HOOK_EVENTS,
70469
+ HOOK_CATEGORIES,
70470
+ supportsHook,
70471
+ toCanonical,
70472
+ toNative
70473
+ } from "@cleocode/caamp";
69842
70474
  import { getCommonHookEvents, getProvidersByHookEvent } from "@cleocode/caamp";
69843
70475
  function isProviderHookEvent(event) {
69844
70476
  return !INTERNAL_HOOK_EVENT_SET.has(event);
@@ -75649,7 +76281,7 @@ async function runUpgrade(options = {}) {
75649
76281
  return { success: false, upToDate: false, dryRun: isDryRun, actions, applied: 0, errors };
75650
76282
  }
75651
76283
  await forceCheckpointBeforeOperation("storage-migration", options.cwd);
75652
- const { MigrationLogger: MigrationLogger2 } = await Promise.resolve().then(() => (init_logger3(), logger_exports));
76284
+ const { MigrationLogger: MigrationLogger2 } = await Promise.resolve().then(() => (init_logger3(), logger_exports2));
75653
76285
  const {
75654
76286
  createMigrationState: createMigrationState2,
75655
76287
  updateMigrationPhase: updateMigrationPhase2,
@@ -80203,41 +80835,6 @@ var init_internal = __esm({
80203
80835
  }
80204
80836
  });
80205
80837
 
80206
- // packages/cleo/src/cli/field-context.ts
80207
- import {
80208
- resolveFieldExtraction
80209
- } from "@cleocode/lafs-protocol";
80210
- function getFieldContext() {
80211
- return currentContext;
80212
- }
80213
- var currentContext;
80214
- var init_field_context = __esm({
80215
- "packages/cleo/src/cli/field-context.ts"() {
80216
- "use strict";
80217
- currentContext = {
80218
- mvi: "standard",
80219
- mviSource: "default",
80220
- expectsCustomMvi: false
80221
- };
80222
- }
80223
- });
80224
-
80225
- // packages/cleo/src/cli/format-context.ts
80226
- function getFormatContext() {
80227
- return currentResolution;
80228
- }
80229
- var currentResolution;
80230
- var init_format_context = __esm({
80231
- "packages/cleo/src/cli/format-context.ts"() {
80232
- "use strict";
80233
- currentResolution = {
80234
- format: "json",
80235
- source: "default",
80236
- quiet: false
80237
- };
80238
- }
80239
- });
80240
-
80241
80838
  // packages/cleo/src/cli/renderers/normalizer.ts
80242
80839
  function normalizeForHuman(command, data) {
80243
80840
  switch (command) {
@@ -83575,6 +84172,30 @@ var init_registry5 = __esm({
83575
84172
  sessionRequired: false,
83576
84173
  requiredParams: []
83577
84174
  },
84175
+ {
84176
+ gateway: "query",
84177
+ domain: "admin",
84178
+ operation: "hooks.matrix",
84179
+ description: "admin.hooks.matrix (query) \u2014 cross-provider hook support matrix using CAAMP canonical taxonomy",
84180
+ tier: 1,
84181
+ idempotent: true,
84182
+ sessionRequired: false,
84183
+ requiredParams: [],
84184
+ params: [
84185
+ {
84186
+ name: "providerIds",
84187
+ type: "string",
84188
+ required: false,
84189
+ description: "Limit matrix to specific provider IDs (default: all mapped providers)"
84190
+ },
84191
+ {
84192
+ name: "detectProvider",
84193
+ type: "boolean",
84194
+ required: false,
84195
+ description: "Detect current runtime provider (default: true)"
84196
+ }
84197
+ ]
84198
+ },
83578
84199
  {
83579
84200
  gateway: "query",
83580
84201
  domain: "admin",
@@ -84399,6 +85020,114 @@ var init_config_engine = __esm({
84399
85020
  }
84400
85021
  });
84401
85022
 
85023
+ // packages/cleo/src/dispatch/engines/hooks-engine.ts
85024
+ var hooks_engine_exports = {};
85025
+ __export(hooks_engine_exports, {
85026
+ queryCommonHooks: () => queryCommonHooks,
85027
+ queryHookProviders: () => queryHookProviders,
85028
+ systemHooksMatrix: () => systemHooksMatrix
85029
+ });
85030
+ async function queryHookProviders(event) {
85031
+ if (!isProviderHookEvent(event)) {
85032
+ return engineSuccess({
85033
+ event,
85034
+ providers: []
85035
+ });
85036
+ }
85037
+ const { getProvidersByHookEvent: getProvidersByHookEvent2 } = await import("@cleocode/caamp");
85038
+ const providers = getProvidersByHookEvent2(event);
85039
+ return engineSuccess({
85040
+ event,
85041
+ providers: providers.map((p) => ({
85042
+ id: p.id,
85043
+ name: p.name,
85044
+ supportedHooks: p.capabilities?.hooks?.supported ?? []
85045
+ }))
85046
+ });
85047
+ }
85048
+ async function queryCommonHooks(providerIds) {
85049
+ const { getCommonHookEvents: getCommonHookEvents2 } = await import("@cleocode/caamp");
85050
+ const commonEvents = getCommonHookEvents2(providerIds);
85051
+ return engineSuccess({
85052
+ providerIds,
85053
+ commonEvents
85054
+ });
85055
+ }
85056
+ async function systemHooksMatrix(params) {
85057
+ try {
85058
+ const { buildHookMatrix: buildHookMatrix2, getProviderSummary, getHookMappingsVersion, detectAllProviders: detectAllProviders3 } = await import("@cleocode/caamp");
85059
+ const caampVersion = getHookMappingsVersion();
85060
+ const raw = buildHookMatrix2(params?.providerIds);
85061
+ const boolMatrix = {};
85062
+ for (const event of raw.events) {
85063
+ boolMatrix[event] = {};
85064
+ for (const providerId of raw.providers) {
85065
+ const mapping = raw.matrix[event]?.[providerId];
85066
+ boolMatrix[event][providerId] = mapping?.supported ?? false;
85067
+ }
85068
+ }
85069
+ const summary = raw.providers.map((providerId) => {
85070
+ const provSummary = getProviderSummary(providerId);
85071
+ if (provSummary) {
85072
+ return {
85073
+ providerId,
85074
+ supportedCount: provSummary.supportedCount,
85075
+ totalCanonical: provSummary.totalCanonical,
85076
+ coverage: provSummary.coverage,
85077
+ supported: provSummary.supported,
85078
+ unsupported: provSummary.unsupported
85079
+ };
85080
+ }
85081
+ const supported = raw.events.filter((ev) => boolMatrix[ev]?.[providerId] === true);
85082
+ const unsupported = raw.events.filter((ev) => boolMatrix[ev]?.[providerId] !== true);
85083
+ const totalCanonical = raw.events.length;
85084
+ const coverage = totalCanonical > 0 ? Math.round(supported.length / totalCanonical * 100) : 0;
85085
+ return {
85086
+ providerId,
85087
+ supportedCount: supported.length,
85088
+ totalCanonical,
85089
+ coverage,
85090
+ supported,
85091
+ unsupported
85092
+ };
85093
+ });
85094
+ let detectedProvider = null;
85095
+ const shouldDetect = params?.detectProvider !== false;
85096
+ if (shouldDetect) {
85097
+ try {
85098
+ const detectionResults = detectAllProviders3();
85099
+ const detected = detectionResults.find((r) => r.installed && r.projectDetected);
85100
+ if (detected) {
85101
+ detectedProvider = detected.provider.id;
85102
+ } else {
85103
+ const anyInstalled = detectionResults.find((r) => r.installed);
85104
+ if (anyInstalled) {
85105
+ detectedProvider = anyInstalled.provider.id;
85106
+ }
85107
+ }
85108
+ } catch {
85109
+ }
85110
+ }
85111
+ return engineSuccess({
85112
+ caampVersion,
85113
+ events: raw.events,
85114
+ providers: raw.providers,
85115
+ matrix: boolMatrix,
85116
+ summary,
85117
+ detectedProvider
85118
+ });
85119
+ } catch (err) {
85120
+ return engineError("E_GENERAL", err.message);
85121
+ }
85122
+ }
85123
+ var init_hooks_engine = __esm({
85124
+ "packages/cleo/src/dispatch/engines/hooks-engine.ts"() {
85125
+ "use strict";
85126
+ init_internal();
85127
+ init_error();
85128
+ }
85129
+ });
85130
+
84402
85131
  // packages/cleo/src/dispatch/engines/init-engine.ts
84403
85132
  async function initProject2(projectRoot, options) {
84404
85133
  try {
@@ -88021,6 +88750,7 @@ var init_engine2 = __esm({
88021
88750
  init_internal();
88022
88751
  init_codebase_map_engine();
88023
88752
  init_config_engine();
88753
+ init_hooks_engine();
88024
88754
  init_init_engine();
88025
88755
  init_lifecycle_engine();
88026
88756
  init_memory_engine();
@@ -88549,6 +89279,13 @@ var init_admin2 = __esm({
88549
89279
  const result = await systemSmoke();
88550
89280
  return wrapResult(result, "query", "admin", operation, startTime);
88551
89281
  }
89282
+ case "hooks.matrix": {
89283
+ const result = await systemHooksMatrix({
89284
+ providerIds: params?.providerIds,
89285
+ detectProvider: params?.detectProvider !== false
89286
+ });
89287
+ return wrapResult(result, "query", "admin", operation, startTime);
89288
+ }
88552
89289
  default:
88553
89290
  return unsupportedOp("query", "admin", operation, startTime);
88554
89291
  }
@@ -89033,7 +89770,8 @@ var init_admin2 = __esm({
89033
89770
  "backup",
89034
89771
  "export",
89035
89772
  "map",
89036
- "smoke"
89773
+ "smoke",
89774
+ "hooks.matrix"
89037
89775
  ],
89038
89776
  mutate: [
89039
89777
  "init",
@@ -92765,46 +93503,6 @@ var init_tasks4 = __esm({
92765
93503
  }
92766
93504
  });
92767
93505
 
92768
- // packages/cleo/src/dispatch/engines/hooks-engine.ts
92769
- var hooks_engine_exports = {};
92770
- __export(hooks_engine_exports, {
92771
- queryCommonHooks: () => queryCommonHooks,
92772
- queryHookProviders: () => queryHookProviders
92773
- });
92774
- async function queryHookProviders(event) {
92775
- if (!isProviderHookEvent(event)) {
92776
- return engineSuccess({
92777
- event,
92778
- providers: []
92779
- });
92780
- }
92781
- const { getProvidersByHookEvent: getProvidersByHookEvent2 } = await import("@cleocode/caamp");
92782
- const providers = getProvidersByHookEvent2(event);
92783
- return engineSuccess({
92784
- event,
92785
- providers: providers.map((p) => ({
92786
- id: p.id,
92787
- name: p.name,
92788
- supportedHooks: p.capabilities?.hooks?.supported ?? []
92789
- }))
92790
- });
92791
- }
92792
- async function queryCommonHooks(providerIds) {
92793
- const { getCommonHookEvents: getCommonHookEvents2 } = await import("@cleocode/caamp");
92794
- const commonEvents = getCommonHookEvents2(providerIds);
92795
- return engineSuccess({
92796
- providerIds,
92797
- commonEvents
92798
- });
92799
- }
92800
- var init_hooks_engine = __esm({
92801
- "packages/cleo/src/dispatch/engines/hooks-engine.ts"() {
92802
- "use strict";
92803
- init_internal();
92804
- init_error();
92805
- }
92806
- });
92807
-
92808
93506
  // packages/cleo/src/dispatch/engines/tools-engine.ts
92809
93507
  import {
92810
93508
  buildInjectionContent,
@@ -94394,7 +95092,7 @@ async function dispatchFromCli(gateway, domain2, operation, params, outputOpts)
94394
95092
  const dispatcher = getCliDispatcher();
94395
95093
  const projectRoot = getProjectRoot();
94396
95094
  const dispatchStart = Date.now();
94397
- hooks.dispatch("onPromptSubmit", projectRoot, {
95095
+ hooks.dispatch("PromptSubmit", projectRoot, {
94398
95096
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94399
95097
  gateway,
94400
95098
  domain: domain2,
@@ -94410,7 +95108,7 @@ async function dispatchFromCli(gateway, domain2, operation, params, outputOpts)
94410
95108
  source: "cli",
94411
95109
  requestId: randomUUID10()
94412
95110
  });
94413
- hooks.dispatch("onResponseComplete", projectRoot, {
95111
+ hooks.dispatch("ResponseComplete", projectRoot, {
94414
95112
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94415
95113
  gateway,
94416
95114
  domain: domain2,
@@ -94467,7 +95165,7 @@ async function dispatchRaw(gateway, domain2, operation, params) {
94467
95165
  const dispatcher = getCliDispatcher();
94468
95166
  const projectRoot = getProjectRoot();
94469
95167
  const dispatchStart = Date.now();
94470
- hooks.dispatch("onPromptSubmit", projectRoot, {
95168
+ hooks.dispatch("PromptSubmit", projectRoot, {
94471
95169
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94472
95170
  gateway,
94473
95171
  domain: domain2,
@@ -94483,7 +95181,7 @@ async function dispatchRaw(gateway, domain2, operation, params) {
94483
95181
  source: "cli",
94484
95182
  requestId: randomUUID10()
94485
95183
  });
94486
- hooks.dispatch("onResponseComplete", projectRoot, {
95184
+ hooks.dispatch("ResponseComplete", projectRoot, {
94487
95185
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
94488
95186
  gateway,
94489
95187
  domain: domain2,
@@ -95072,6 +95770,23 @@ function camelToKebab(str) {
95072
95770
  return str.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
95073
95771
  }
95074
95772
 
95773
+ // packages/cleo/src/cli/index.ts
95774
+ init_field_context();
95775
+ init_format_context();
95776
+
95777
+ // packages/cleo/src/cli/middleware/output-format.ts
95778
+ import { resolveOutputFormat } from "@cleocode/lafs-protocol";
95779
+ function resolveFormat(opts, defaults) {
95780
+ const input = {
95781
+ jsonFlag: opts["json"] === true,
95782
+ humanFlag: opts["human"] === true,
95783
+ quiet: opts["quiet"] === true,
95784
+ projectDefault: defaults?.projectDefault,
95785
+ userDefault: defaults?.userDefault
95786
+ };
95787
+ return resolveOutputFormat(input);
95788
+ }
95789
+
95075
95790
  // packages/cleo/src/cli/commands/add.ts
95076
95791
  init_cli();
95077
95792
  init_renderers();
@@ -96854,15 +97569,84 @@ function createUpgradeProgress(enabled) {
96854
97569
  }
96855
97570
 
96856
97571
  // packages/cleo/src/cli/commands/doctor.ts
97572
+ function renderHookMatrixHuman(data) {
97573
+ const { events, providers, matrix, summary, caampVersion, detectedProvider } = data;
97574
+ process.stdout.write(`
97575
+ Provider Hook Matrix (CAAMP ${caampVersion} canonical taxonomy)
97576
+
97577
+ `);
97578
+ if (detectedProvider) {
97579
+ process.stdout.write(`Detected provider: ${detectedProvider}
97580
+
97581
+ `);
97582
+ }
97583
+ if (providers.length === 0) {
97584
+ process.stdout.write("No providers found in CAAMP registry.\n");
97585
+ return;
97586
+ }
97587
+ const EVENT_COL = Math.max(...events.map((e) => e.length), "Event".length);
97588
+ const provCols = providers.map((p) => Math.max(p.length, 5));
97589
+ const headerParts = [
97590
+ "Event".padEnd(EVENT_COL),
97591
+ ...providers.map((p, i) => p.padEnd(provCols[i]))
97592
+ ];
97593
+ process.stdout.write(` ${headerParts.join(" ")}
97594
+ `);
97595
+ const sepParts = ["-".repeat(EVENT_COL), ...provCols.map((w) => "-".repeat(w))];
97596
+ process.stdout.write(` ${sepParts.join(" ")}
97597
+ `);
97598
+ for (const event of events) {
97599
+ const cells = providers.map((p, i) => {
97600
+ const supported = matrix[event]?.[p] === true;
97601
+ const symbol2 = supported ? "\u2713" : "-";
97602
+ return symbol2.padEnd(provCols[i]);
97603
+ });
97604
+ process.stdout.write(` ${event.padEnd(EVENT_COL)} ${cells.join(" ")}
97605
+ `);
97606
+ }
97607
+ process.stdout.write("\n");
97608
+ const coverageParts = summary.map(
97609
+ (s) => `${s.providerId} ${s.supportedCount}/${s.totalCanonical} (${s.coverage}%)`
97610
+ );
97611
+ process.stdout.write(`Coverage: ${coverageParts.join(", ")}
97612
+
97613
+ `);
97614
+ }
96857
97615
  function registerDoctorCommand(program) {
96858
- 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) => {
97616
+ 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) => {
96859
97617
  const globalOpts = command.optsWithGlobals ? command.optsWithGlobals() : command.opts();
96860
97618
  const mergedOpts = { ...globalOpts, ...opts };
96861
97619
  const isHuman = mergedOpts["human"] === true || !!process.stdout.isTTY && mergedOpts["json"] !== true;
96862
97620
  const progress = createDoctorProgress(isHuman);
96863
97621
  progress.start();
96864
97622
  try {
96865
- if (mergedOpts["full"]) {
97623
+ if (mergedOpts["hooks"]) {
97624
+ progress.step(0, "Building provider hook matrix");
97625
+ if (isHuman) {
97626
+ const response = await dispatchRaw("query", "admin", "hooks.matrix", {
97627
+ detectProvider: true
97628
+ });
97629
+ progress.complete("Hook matrix complete");
97630
+ if (response.success && response.data) {
97631
+ renderHookMatrixHuman(response.data);
97632
+ } else {
97633
+ process.stderr.write(
97634
+ `Error: ${response.error?.message ?? "Failed to build hook matrix"}
97635
+ `
97636
+ );
97637
+ process.exitCode = 1;
97638
+ }
97639
+ } else {
97640
+ await dispatchFromCli(
97641
+ "query",
97642
+ "admin",
97643
+ "hooks.matrix",
97644
+ { detectProvider: true },
97645
+ { command: "doctor", operation: "admin.hooks.matrix" }
97646
+ );
97647
+ progress.complete("Hook matrix complete");
97648
+ }
97649
+ } else if (mergedOpts["full"]) {
96866
97650
  progress.step(0, "Running operational smoke tests");
96867
97651
  await dispatchFromCli(
96868
97652
  "query",
@@ -100940,6 +101724,28 @@ for (const shim of rootShim._subcommands) {
100940
101724
  subCommands[alias] = shimToCitty(shim);
100941
101725
  }
100942
101726
  }
101727
+ {
101728
+ const argv = process.argv.slice(2);
101729
+ const rawOpts = {};
101730
+ for (let i = 0; i < argv.length; i++) {
101731
+ const arg = argv[i];
101732
+ if (arg === "--json") rawOpts["json"] = true;
101733
+ else if (arg === "--human") rawOpts["human"] = true;
101734
+ else if (arg === "--quiet") rawOpts["quiet"] = true;
101735
+ else if (arg === "--field" && i + 1 < argv.length) rawOpts["field"] = argv[++i];
101736
+ else if (arg === "--fields" && i + 1 < argv.length) rawOpts["fields"] = argv[++i];
101737
+ else if (arg === "--mvi" && i + 1 < argv.length) rawOpts["mvi"] = argv[++i];
101738
+ }
101739
+ const formatResolution = resolveFormat(rawOpts);
101740
+ setFormatContext(formatResolution);
101741
+ const fieldResolution = resolveFieldContext(rawOpts);
101742
+ setFieldContext(fieldResolution);
101743
+ if (argv[0] === "-V") {
101744
+ const { cliOutput: cliOutput2 } = await Promise.resolve().then(() => (init_renderers(), renderers_exports));
101745
+ cliOutput2({ version: CLI_VERSION }, { command: "version" });
101746
+ process.exit(0);
101747
+ }
101748
+ }
100943
101749
  if (process.argv[2] === "mcp") {
100944
101750
  const mcpPath = join119(import.meta.dirname ?? "", "..", "mcp", "index.js");
100945
101751
  const { spawn: spawn3 } = await import("node:child_process");