@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/mcp/index.js CHANGED
@@ -632,6 +632,13 @@ var init_discovery = __esm({
632
632
  });
633
633
 
634
634
  // packages/core/src/logger.ts
635
+ var logger_exports = {};
636
+ __export(logger_exports, {
637
+ closeLogger: () => closeLogger,
638
+ getLogDir: () => getLogDir,
639
+ getLogger: () => getLogger,
640
+ initLogger: () => initLogger
641
+ });
635
642
  import { existsSync as existsSync2 } from "node:fs";
636
643
  import { dirname, join as join3 } from "node:path";
637
644
  import pino from "pino";
@@ -733,22 +740,42 @@ __export(registry_exports, {
733
740
  HookRegistry: () => HookRegistry,
734
741
  hooks: () => hooks
735
742
  });
736
- var DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
743
+ var LEGACY_EVENT_MAP, DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
737
744
  var init_registry = __esm({
738
745
  "packages/core/src/hooks/registry.ts"() {
739
746
  "use strict";
740
747
  init_logger();
748
+ LEGACY_EVENT_MAP = {
749
+ onSessionStart: "SessionStart",
750
+ onSessionEnd: "SessionEnd",
751
+ onToolStart: "PreToolUse",
752
+ onToolComplete: "PostToolUse",
753
+ onFileChange: "Notification",
754
+ onError: "PostToolUseFailure",
755
+ onPromptSubmit: "PromptSubmit",
756
+ onResponseComplete: "ResponseComplete"
757
+ };
741
758
  DEFAULT_HOOK_CONFIG = {
742
759
  enabled: true,
743
760
  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,
761
+ // CAAMP canonical events (16)
762
+ SessionStart: true,
763
+ SessionEnd: true,
764
+ PromptSubmit: true,
765
+ ResponseComplete: true,
766
+ PreToolUse: true,
767
+ PostToolUse: true,
768
+ PostToolUseFailure: true,
769
+ PermissionRequest: true,
770
+ SubagentStart: true,
771
+ SubagentStop: true,
772
+ PreModel: true,
773
+ PostModel: true,
774
+ PreCompact: true,
775
+ PostCompact: true,
776
+ Notification: true,
777
+ ConfigChange: true,
778
+ // CLEO internal coordination events (5)
752
779
  onWorkAvailable: true,
753
780
  onAgentSpawn: true,
754
781
  onAgentComplete: true,
@@ -759,12 +786,33 @@ var init_registry = __esm({
759
786
  HookRegistry = class {
760
787
  handlers = /* @__PURE__ */ new Map();
761
788
  config = DEFAULT_HOOK_CONFIG;
789
+ /**
790
+ * Resolve a potentially-legacy event name to its canonical equivalent.
791
+ *
792
+ * If the event name matches a known legacy `on`-prefix name, it is
793
+ * remapped and a deprecation warning is logged. Unknown names pass through
794
+ * unchanged so callers using the new canonical names are unaffected.
795
+ */
796
+ resolveEvent(event) {
797
+ const canonical = LEGACY_EVENT_MAP[event];
798
+ if (canonical) {
799
+ getLogger("hooks").warn(
800
+ { legacyEvent: event, canonicalEvent: canonical },
801
+ `[DEPRECATED] Hook event '${event}' has been renamed to '${canonical}'. Update your handler registration.`
802
+ );
803
+ return canonical;
804
+ }
805
+ return event;
806
+ }
762
807
  /**
763
808
  * Register a hook handler for a specific event.
764
809
  *
765
810
  * Handlers are sorted by priority (highest first) and executed
766
811
  * in parallel when the event is dispatched.
767
812
  *
813
+ * Backward compatibility: legacy `on`-prefix event names are automatically
814
+ * remapped to their canonical equivalents.
815
+ *
768
816
  * @param registration - The hook registration containing event, handler, priority, and ID
769
817
  * @returns A function to unregister the handler
770
818
  *
@@ -772,7 +820,7 @@ var init_registry = __esm({
772
820
  * ```typescript
773
821
  * const unregister = hooks.register({
774
822
  * id: 'my-handler',
775
- * event: 'onSessionStart',
823
+ * event: 'SessionStart',
776
824
  * handler: async (root, payload) => { console.log('Session started'); },
777
825
  * priority: 100
778
826
  * });
@@ -781,12 +829,14 @@ var init_registry = __esm({
781
829
  * ```
782
830
  */
783
831
  register(registration) {
784
- const list = this.handlers.get(registration.event) || [];
785
- list.push(registration);
832
+ const resolvedEvent = this.resolveEvent(registration.event);
833
+ const resolvedRegistration = { ...registration, event: resolvedEvent };
834
+ const list = this.handlers.get(resolvedEvent) || [];
835
+ list.push(resolvedRegistration);
786
836
  list.sort((a, b) => b.priority - a.priority);
787
- this.handlers.set(registration.event, list);
837
+ this.handlers.set(resolvedEvent, list);
788
838
  return () => {
789
- const handlers = this.handlers.get(registration.event);
839
+ const handlers = this.handlers.get(resolvedEvent);
790
840
  if (handlers) {
791
841
  const idx = handlers.findIndex((h) => h.id === registration.id);
792
842
  if (idx !== -1) handlers.splice(idx, 1);
@@ -800,14 +850,17 @@ var init_registry = __esm({
800
850
  * execution. Errors in individual handlers are logged but do not block
801
851
  * other handlers or propagate to the caller.
802
852
  *
803
- * @param event - The CAAMP hook event to dispatch
853
+ * Backward compatibility: legacy `on`-prefix event names are automatically
854
+ * remapped to their canonical equivalents.
855
+ *
856
+ * @param event - The CAAMP canonical hook event to dispatch
804
857
  * @param projectRoot - The project root directory path
805
858
  * @param payload - The event payload (typed by event)
806
859
  * @returns Promise that resolves when all handlers have completed
807
860
  *
808
861
  * @example
809
862
  * ```typescript
810
- * await hooks.dispatch('onSessionStart', '/project', {
863
+ * await hooks.dispatch('SessionStart', '/project', {
811
864
  * timestamp: new Date().toISOString(),
812
865
  * sessionId: 'sess-123',
813
866
  * name: 'My Session',
@@ -817,15 +870,19 @@ var init_registry = __esm({
817
870
  */
818
871
  async dispatch(event, projectRoot, payload) {
819
872
  if (!this.config.enabled) return;
820
- if (!this.config.events[event]) return;
821
- const handlers = this.handlers.get(event);
873
+ const resolvedEvent = this.resolveEvent(event);
874
+ if (!this.config.events[resolvedEvent]) return;
875
+ const handlers = this.handlers.get(resolvedEvent);
822
876
  if (!handlers || handlers.length === 0) return;
823
877
  await Promise.allSettled(
824
878
  handlers.map(async (reg) => {
825
879
  try {
826
880
  await reg.handler(projectRoot, payload);
827
881
  } catch (error40) {
828
- getLogger("hooks").warn({ err: error40, hookId: reg.id, event }, "Hook handler failed");
882
+ getLogger("hooks").warn(
883
+ { err: error40, hookId: reg.id, event: resolvedEvent },
884
+ "Hook handler failed"
885
+ );
829
886
  }
830
887
  })
831
888
  );
@@ -834,12 +891,14 @@ var init_registry = __esm({
834
891
  * Check if a specific event is currently enabled.
835
892
  *
836
893
  * Both the global enabled flag and the per-event flag must be true.
894
+ * Automatically resolves legacy `on`-prefix event names.
837
895
  *
838
896
  * @param event - The CAAMP hook event to check
839
897
  * @returns True if the event is enabled
840
898
  */
841
899
  isEnabled(event) {
842
- return this.config.enabled && this.config.events[event];
900
+ const resolvedEvent = this.resolveEvent(event);
901
+ return this.config.enabled && this.config.events[resolvedEvent];
843
902
  }
844
903
  /**
845
904
  * Update the hook system configuration.
@@ -851,7 +910,7 @@ var init_registry = __esm({
851
910
  * @example
852
911
  * ```typescript
853
912
  * hooks.setConfig({ enabled: false }); // Disable all hooks
854
- * hooks.setConfig({ events: { onError: false } }); // Disable specific event
913
+ * hooks.setConfig({ events: { PostToolUseFailure: false } }); // Disable specific event
855
914
  * ```
856
915
  */
857
916
  setConfig(config2) {
@@ -869,12 +928,14 @@ var init_registry = __esm({
869
928
  * List all registered handlers for a specific event.
870
929
  *
871
930
  * Returns handlers in priority order (highest first).
931
+ * Automatically resolves legacy `on`-prefix event names.
872
932
  *
873
933
  * @param event - The CAAMP hook event
874
934
  * @returns Array of hook registrations
875
935
  */
876
936
  listHandlers(event) {
877
- return [...this.handlers.get(event) || []];
937
+ const resolvedEvent = this.resolveEvent(event);
938
+ return [...this.handlers.get(resolvedEvent) || []];
878
939
  }
879
940
  };
880
941
  hooks = new HookRegistry();
@@ -13695,7 +13756,7 @@ async function saveJson(filePath, data, options) {
13695
13756
  }
13696
13757
  await atomicWriteJson(filePath, data, { indent: options?.indent });
13697
13758
  Promise.resolve().then(() => (init_registry(), registry_exports)).then(
13698
- ({ hooks: h }) => h.dispatch("onFileChange", process.cwd(), {
13759
+ ({ hooks: h }) => h.dispatch("Notification", process.cwd(), {
13699
13760
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13700
13761
  filePath,
13701
13762
  changeType: "write"
@@ -20929,13 +20990,13 @@ var init_session_hooks = __esm({
20929
20990
  init_memory_bridge_refresh();
20930
20991
  hooks.register({
20931
20992
  id: "brain-session-start",
20932
- event: "onSessionStart",
20993
+ event: "SessionStart",
20933
20994
  handler: handleSessionStart,
20934
20995
  priority: 100
20935
20996
  });
20936
20997
  hooks.register({
20937
20998
  id: "brain-session-end",
20938
- event: "onSessionEnd",
20999
+ event: "SessionEnd",
20939
21000
  handler: handleSessionEnd,
20940
21001
  priority: 100
20941
21002
  });
@@ -20982,13 +21043,13 @@ var init_task_hooks = __esm({
20982
21043
  init_memory_bridge_refresh();
20983
21044
  hooks.register({
20984
21045
  id: "brain-tool-start",
20985
- event: "onToolStart",
21046
+ event: "PreToolUse",
20986
21047
  handler: handleToolStart,
20987
21048
  priority: 100
20988
21049
  });
20989
21050
  hooks.register({
20990
21051
  id: "brain-tool-complete",
20991
- event: "onToolComplete",
21052
+ event: "PostToolUse",
20992
21053
  handler: handleToolComplete,
20993
21054
  priority: 100
20994
21055
  });
@@ -21018,7 +21079,7 @@ var init_error_hooks = __esm({
21018
21079
  init_registry();
21019
21080
  hooks.register({
21020
21081
  id: "brain-error",
21021
- event: "onError",
21082
+ event: "PostToolUseFailure",
21022
21083
  handler: handleError,
21023
21084
  priority: 100
21024
21085
  });
@@ -21049,6 +21110,7 @@ async function isFileCaptureEnabled(projectRoot) {
21049
21110
  }
21050
21111
  }
21051
21112
  async function handleFileChange(projectRoot, payload) {
21113
+ if (!payload.filePath || !payload.changeType) return;
21052
21114
  if (!await isFileCaptureEnabled(projectRoot)) return;
21053
21115
  const now2 = Date.now();
21054
21116
  const lastWrite = recentWrites.get(payload.filePath);
@@ -21095,7 +21157,7 @@ var init_file_hooks = __esm({
21095
21157
  ];
21096
21158
  hooks.register({
21097
21159
  id: "brain-file-change",
21098
- event: "onFileChange",
21160
+ event: "Notification",
21099
21161
  handler: handleFileChange,
21100
21162
  priority: 100
21101
21163
  });
@@ -21149,22 +21211,51 @@ async function handleResponseComplete(projectRoot, payload) {
21149
21211
  if (!isMissingBrainSchemaError4(err)) throw err;
21150
21212
  }
21151
21213
  }
21214
+ async function handleSystemNotification(projectRoot, payload) {
21215
+ if (payload.filePath || payload.changeType) return;
21216
+ if (!payload.message) return;
21217
+ try {
21218
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21219
+ const config2 = await loadConfig4(projectRoot);
21220
+ if (!config2.brain?.autoCapture) return;
21221
+ } catch {
21222
+ return;
21223
+ }
21224
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21225
+ try {
21226
+ await observeBrain2(projectRoot, {
21227
+ text: `System notification: ${payload.message}`,
21228
+ title: `Notification: ${payload.message.slice(0, 60)}`,
21229
+ type: "discovery",
21230
+ sourceSessionId: payload.sessionId,
21231
+ sourceType: "agent"
21232
+ });
21233
+ } catch (err) {
21234
+ if (!isMissingBrainSchemaError4(err)) throw err;
21235
+ }
21236
+ }
21152
21237
  var init_mcp_hooks = __esm({
21153
21238
  "packages/core/src/hooks/handlers/mcp-hooks.ts"() {
21154
21239
  "use strict";
21155
21240
  init_registry();
21156
21241
  hooks.register({
21157
21242
  id: "brain-prompt-submit",
21158
- event: "onPromptSubmit",
21243
+ event: "PromptSubmit",
21159
21244
  handler: handlePromptSubmit,
21160
21245
  priority: 100
21161
21246
  });
21162
21247
  hooks.register({
21163
21248
  id: "brain-response-complete",
21164
- event: "onResponseComplete",
21249
+ event: "ResponseComplete",
21165
21250
  handler: handleResponseComplete,
21166
21251
  priority: 100
21167
21252
  });
21253
+ hooks.register({
21254
+ id: "brain-system-notification",
21255
+ event: "Notification",
21256
+ handler: handleSystemNotification,
21257
+ priority: 90
21258
+ });
21168
21259
  }
21169
21260
  });
21170
21261
 
@@ -21241,19 +21332,158 @@ var init_work_capture_hooks = __esm({
21241
21332
  ]);
21242
21333
  hooks.register({
21243
21334
  id: "work-capture-prompt-submit",
21244
- event: "onPromptSubmit",
21335
+ event: "PromptSubmit",
21245
21336
  handler: handleWorkPromptSubmit,
21246
21337
  priority: 90
21247
21338
  });
21248
21339
  hooks.register({
21249
21340
  id: "work-capture-response-complete",
21250
- event: "onResponseComplete",
21341
+ event: "ResponseComplete",
21251
21342
  handler: handleWorkResponseComplete,
21252
21343
  priority: 90
21253
21344
  });
21254
21345
  }
21255
21346
  });
21256
21347
 
21348
+ // packages/core/src/hooks/handlers/agent-hooks.ts
21349
+ function isMissingBrainSchemaError6(err) {
21350
+ if (!(err instanceof Error)) return false;
21351
+ const message = String(err.message || "").toLowerCase();
21352
+ return message.includes("no such table") && message.includes("brain_");
21353
+ }
21354
+ async function isAutoCaptureEnabled(projectRoot) {
21355
+ try {
21356
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21357
+ const config2 = await loadConfig4(projectRoot);
21358
+ return config2.brain?.autoCapture ?? false;
21359
+ } catch {
21360
+ return false;
21361
+ }
21362
+ }
21363
+ async function handleSubagentStart(projectRoot, payload) {
21364
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
21365
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21366
+ const rolePart = payload.role ? ` role=${payload.role}` : "";
21367
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
21368
+ try {
21369
+ await observeBrain2(projectRoot, {
21370
+ text: `Subagent spawned: ${payload.agentId}${rolePart}${taskPart}`,
21371
+ title: `Subagent start: ${payload.agentId}`,
21372
+ type: "discovery",
21373
+ sourceSessionId: payload.sessionId,
21374
+ sourceType: "agent"
21375
+ });
21376
+ } catch (err) {
21377
+ if (!isMissingBrainSchemaError6(err)) throw err;
21378
+ }
21379
+ }
21380
+ async function handleSubagentStop(projectRoot, payload) {
21381
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
21382
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21383
+ const statusPart = payload.status ? ` status=${payload.status}` : "";
21384
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
21385
+ const summaryPart = payload.summary ? `
21386
+ Summary: ${payload.summary}` : "";
21387
+ try {
21388
+ await observeBrain2(projectRoot, {
21389
+ text: `Subagent completed: ${payload.agentId}${statusPart}${taskPart}${summaryPart}`,
21390
+ title: `Subagent stop: ${payload.agentId}`,
21391
+ type: "change",
21392
+ sourceSessionId: payload.sessionId,
21393
+ sourceType: "agent"
21394
+ });
21395
+ } catch (err) {
21396
+ if (!isMissingBrainSchemaError6(err)) throw err;
21397
+ }
21398
+ }
21399
+ var init_agent_hooks = __esm({
21400
+ "packages/core/src/hooks/handlers/agent-hooks.ts"() {
21401
+ "use strict";
21402
+ init_registry();
21403
+ hooks.register({
21404
+ id: "brain-subagent-start",
21405
+ event: "SubagentStart",
21406
+ handler: handleSubagentStart,
21407
+ priority: 100
21408
+ });
21409
+ hooks.register({
21410
+ id: "brain-subagent-stop",
21411
+ event: "SubagentStop",
21412
+ handler: handleSubagentStop,
21413
+ priority: 100
21414
+ });
21415
+ }
21416
+ });
21417
+
21418
+ // packages/core/src/hooks/handlers/context-hooks.ts
21419
+ function isMissingBrainSchemaError7(err) {
21420
+ if (!(err instanceof Error)) return false;
21421
+ const message = String(err.message || "").toLowerCase();
21422
+ return message.includes("no such table") && message.includes("brain_");
21423
+ }
21424
+ async function isAutoCaptureEnabled2(projectRoot) {
21425
+ try {
21426
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
21427
+ const config2 = await loadConfig4(projectRoot);
21428
+ return config2.brain?.autoCapture ?? false;
21429
+ } catch {
21430
+ return false;
21431
+ }
21432
+ }
21433
+ async function handlePreCompact(projectRoot, payload) {
21434
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
21435
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21436
+ const tokensPart = payload.tokensBefore != null ? ` (~${payload.tokensBefore.toLocaleString()} tokens)` : "";
21437
+ const reasonPart = payload.reason ? ` Reason: ${payload.reason}` : "";
21438
+ try {
21439
+ await observeBrain2(projectRoot, {
21440
+ text: `Context compaction about to begin${tokensPart}.${reasonPart}`,
21441
+ title: "Pre-compaction context snapshot",
21442
+ type: "discovery",
21443
+ sourceSessionId: payload.sessionId,
21444
+ sourceType: "agent"
21445
+ });
21446
+ } catch (err) {
21447
+ if (!isMissingBrainSchemaError7(err)) throw err;
21448
+ }
21449
+ }
21450
+ async function handlePostCompact(projectRoot, payload) {
21451
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
21452
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
21453
+ const statusPart = payload.success ? "succeeded" : "failed";
21454
+ const beforePart = payload.tokensBefore != null ? ` before=${payload.tokensBefore.toLocaleString()}` : "";
21455
+ const afterPart = payload.tokensAfter != null ? ` after=${payload.tokensAfter.toLocaleString()}` : "";
21456
+ try {
21457
+ await observeBrain2(projectRoot, {
21458
+ text: `Context compaction ${statusPart}${beforePart}${afterPart}`,
21459
+ title: "Post-compaction record",
21460
+ type: "change",
21461
+ sourceSessionId: payload.sessionId,
21462
+ sourceType: "agent"
21463
+ });
21464
+ } catch (err) {
21465
+ if (!isMissingBrainSchemaError7(err)) throw err;
21466
+ }
21467
+ }
21468
+ var init_context_hooks = __esm({
21469
+ "packages/core/src/hooks/handlers/context-hooks.ts"() {
21470
+ "use strict";
21471
+ init_registry();
21472
+ hooks.register({
21473
+ id: "brain-pre-compact",
21474
+ event: "PreCompact",
21475
+ handler: handlePreCompact,
21476
+ priority: 100
21477
+ });
21478
+ hooks.register({
21479
+ id: "brain-post-compact",
21480
+ event: "PostCompact",
21481
+ handler: handlePostCompact,
21482
+ priority: 100
21483
+ });
21484
+ }
21485
+ });
21486
+
21257
21487
  // packages/core/src/hooks/handlers/index.ts
21258
21488
  var init_handlers = __esm({
21259
21489
  "packages/core/src/hooks/handlers/index.ts"() {
@@ -21264,6 +21494,10 @@ var init_handlers = __esm({
21264
21494
  init_file_hooks();
21265
21495
  init_mcp_hooks();
21266
21496
  init_work_capture_hooks();
21497
+ init_agent_hooks();
21498
+ init_context_hooks();
21499
+ init_agent_hooks();
21500
+ init_context_hooks();
21267
21501
  init_error_hooks();
21268
21502
  init_file_hooks();
21269
21503
  init_mcp_hooks();
@@ -23114,7 +23348,7 @@ async function startSession(options, cwd, accessor) {
23114
23348
  });
23115
23349
  }
23116
23350
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
23117
- hooks2.dispatch("onSessionStart", cwd ?? process.cwd(), {
23351
+ hooks2.dispatch("SessionStart", cwd ?? process.cwd(), {
23118
23352
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23119
23353
  sessionId: session.id,
23120
23354
  name: options.name,
@@ -23152,7 +23386,7 @@ async function endSession(options = {}, cwd, accessor) {
23152
23386
  session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
23153
23387
  const duration3 = Math.floor((Date.now() - new Date(session.startedAt).getTime()) / 1e3);
23154
23388
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
23155
- hooks2.dispatch("onSessionEnd", cwd ?? process.cwd(), {
23389
+ hooks2.dispatch("SessionEnd", cwd ?? process.cwd(), {
23156
23390
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23157
23391
  sessionId: session.id,
23158
23392
  duration: duration3,
@@ -35888,7 +36122,7 @@ function validatePayload(event, payload) {
35888
36122
  const errors = result.error.issues.map((issue2) => `${issue2.path.join(".")}: ${issue2.message}`);
35889
36123
  return { valid: false, errors };
35890
36124
  }
35891
- var HookPayloadSchema, OnSessionStartPayloadSchema, OnSessionEndPayloadSchema, OnToolStartPayloadSchema, OnToolCompletePayloadSchema, OnFileChangePayloadSchema, OnErrorPayloadSchema, OnPromptSubmitPayloadSchema, OnResponseCompletePayloadSchema, OnWorkAvailablePayloadSchema, OnAgentSpawnPayloadSchema, OnAgentCompletePayloadSchema, OnCascadeStartPayloadSchema, OnPatrolPayloadSchema, EVENT_SCHEMA_MAP;
36125
+ 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
36126
  var init_payload_schemas = __esm({
35893
36127
  "packages/core/src/hooks/payload-schemas.ts"() {
35894
36128
  "use strict";
@@ -35900,33 +36134,42 @@ var init_payload_schemas = __esm({
35900
36134
  providerId: external_exports.string().optional(),
35901
36135
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35902
36136
  });
35903
- OnSessionStartPayloadSchema = HookPayloadSchema.extend({
36137
+ SessionStartPayloadSchema = HookPayloadSchema.extend({
35904
36138
  sessionId: external_exports.string(),
35905
36139
  name: external_exports.string(),
35906
36140
  scope: external_exports.string(),
35907
36141
  agent: external_exports.string().optional()
35908
36142
  });
35909
- OnSessionEndPayloadSchema = HookPayloadSchema.extend({
36143
+ OnSessionStartPayloadSchema = SessionStartPayloadSchema;
36144
+ SessionEndPayloadSchema = HookPayloadSchema.extend({
35910
36145
  sessionId: external_exports.string(),
35911
36146
  duration: external_exports.number(),
35912
36147
  tasksCompleted: external_exports.array(external_exports.string())
35913
36148
  });
35914
- OnToolStartPayloadSchema = HookPayloadSchema.extend({
36149
+ OnSessionEndPayloadSchema = SessionEndPayloadSchema;
36150
+ PreToolUsePayloadSchema = HookPayloadSchema.extend({
35915
36151
  taskId: external_exports.string(),
35916
36152
  taskTitle: external_exports.string(),
35917
- previousTask: external_exports.string().optional()
36153
+ previousTask: external_exports.string().optional(),
36154
+ toolName: external_exports.string().optional(),
36155
+ toolInput: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35918
36156
  });
35919
- OnToolCompletePayloadSchema = HookPayloadSchema.extend({
36157
+ OnToolStartPayloadSchema = PreToolUsePayloadSchema;
36158
+ PostToolUsePayloadSchema = HookPayloadSchema.extend({
35920
36159
  taskId: external_exports.string(),
35921
36160
  taskTitle: external_exports.string(),
35922
- status: external_exports.enum(["done", "archived", "cancelled"])
36161
+ status: external_exports.enum(["done", "archived", "cancelled"]),
36162
+ toolResult: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
35923
36163
  });
35924
- OnFileChangePayloadSchema = HookPayloadSchema.extend({
35925
- filePath: external_exports.string(),
35926
- changeType: external_exports.enum(["write", "create", "delete"]),
35927
- sizeBytes: external_exports.number().optional()
36164
+ OnToolCompletePayloadSchema = PostToolUsePayloadSchema;
36165
+ NotificationPayloadSchema = HookPayloadSchema.extend({
36166
+ filePath: external_exports.string().optional(),
36167
+ changeType: external_exports.enum(["write", "create", "delete"]).optional(),
36168
+ sizeBytes: external_exports.number().optional(),
36169
+ message: external_exports.string().optional()
35928
36170
  });
35929
- OnErrorPayloadSchema = HookPayloadSchema.extend({
36171
+ OnFileChangePayloadSchema = NotificationPayloadSchema;
36172
+ PostToolUseFailurePayloadSchema = HookPayloadSchema.extend({
35930
36173
  errorCode: external_exports.union([external_exports.number(), external_exports.string()]),
35931
36174
  message: external_exports.string(),
35932
36175
  domain: external_exports.string().optional(),
@@ -35934,13 +36177,15 @@ var init_payload_schemas = __esm({
35934
36177
  gateway: external_exports.string().optional(),
35935
36178
  stack: external_exports.string().optional()
35936
36179
  });
35937
- OnPromptSubmitPayloadSchema = HookPayloadSchema.extend({
36180
+ OnErrorPayloadSchema = PostToolUseFailurePayloadSchema;
36181
+ PromptSubmitPayloadSchema = HookPayloadSchema.extend({
35938
36182
  gateway: external_exports.string(),
35939
36183
  domain: external_exports.string(),
35940
36184
  operation: external_exports.string(),
35941
36185
  source: external_exports.string().optional()
35942
36186
  });
35943
- OnResponseCompletePayloadSchema = HookPayloadSchema.extend({
36187
+ OnPromptSubmitPayloadSchema = PromptSubmitPayloadSchema;
36188
+ ResponseCompletePayloadSchema = HookPayloadSchema.extend({
35944
36189
  gateway: external_exports.string(),
35945
36190
  domain: external_exports.string(),
35946
36191
  operation: external_exports.string(),
@@ -35948,6 +36193,32 @@ var init_payload_schemas = __esm({
35948
36193
  durationMs: external_exports.number().optional(),
35949
36194
  errorCode: external_exports.string().optional()
35950
36195
  });
36196
+ OnResponseCompletePayloadSchema = ResponseCompletePayloadSchema;
36197
+ SubagentStartPayloadSchema = HookPayloadSchema.extend({
36198
+ agentId: external_exports.string(),
36199
+ role: external_exports.string().optional(),
36200
+ taskId: external_exports.string().optional()
36201
+ });
36202
+ SubagentStopPayloadSchema = HookPayloadSchema.extend({
36203
+ agentId: external_exports.string(),
36204
+ status: external_exports.enum(["complete", "partial", "blocked", "failed"]).optional(),
36205
+ taskId: external_exports.string().optional(),
36206
+ summary: external_exports.string().optional()
36207
+ });
36208
+ PreCompactPayloadSchema = HookPayloadSchema.extend({
36209
+ tokensBefore: external_exports.number().optional(),
36210
+ reason: external_exports.string().optional()
36211
+ });
36212
+ PostCompactPayloadSchema = HookPayloadSchema.extend({
36213
+ tokensBefore: external_exports.number().optional(),
36214
+ tokensAfter: external_exports.number().optional(),
36215
+ success: external_exports.boolean()
36216
+ });
36217
+ ConfigChangePayloadSchema = HookPayloadSchema.extend({
36218
+ key: external_exports.string(),
36219
+ previousValue: external_exports.unknown().optional(),
36220
+ newValue: external_exports.unknown().optional()
36221
+ });
35951
36222
  OnWorkAvailablePayloadSchema = HookPayloadSchema.extend({
35952
36223
  taskIds: external_exports.array(external_exports.string()),
35953
36224
  epicId: external_exports.string().optional(),
@@ -35979,14 +36250,21 @@ var init_payload_schemas = __esm({
35979
36250
  scope: external_exports.string().optional()
35980
36251
  });
35981
36252
  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,
36253
+ // CAAMP canonical events (16)
36254
+ SessionStart: SessionStartPayloadSchema,
36255
+ SessionEnd: SessionEndPayloadSchema,
36256
+ PreToolUse: PreToolUsePayloadSchema,
36257
+ PostToolUse: PostToolUsePayloadSchema,
36258
+ Notification: NotificationPayloadSchema,
36259
+ PostToolUseFailure: PostToolUseFailurePayloadSchema,
36260
+ PromptSubmit: PromptSubmitPayloadSchema,
36261
+ ResponseComplete: ResponseCompletePayloadSchema,
36262
+ SubagentStart: SubagentStartPayloadSchema,
36263
+ SubagentStop: SubagentStopPayloadSchema,
36264
+ PreCompact: PreCompactPayloadSchema,
36265
+ PostCompact: PostCompactPayloadSchema,
36266
+ ConfigChange: ConfigChangePayloadSchema,
36267
+ // CLEO internal coordination events (5)
35990
36268
  onWorkAvailable: OnWorkAvailablePayloadSchema,
35991
36269
  onAgentSpawn: OnAgentSpawnPayloadSchema,
35992
36270
  onAgentComplete: OnAgentCompletePayloadSchema,
@@ -36016,10 +36294,15 @@ __export(hooks_exports, {
36016
36294
  OnWorkAvailablePayloadSchema: () => OnWorkAvailablePayloadSchema,
36017
36295
  handleError: () => handleError,
36018
36296
  handleFileChange: () => handleFileChange,
36297
+ handlePostCompact: () => handlePostCompact,
36298
+ handlePreCompact: () => handlePreCompact,
36019
36299
  handlePromptSubmit: () => handlePromptSubmit,
36020
36300
  handleResponseComplete: () => handleResponseComplete,
36021
36301
  handleSessionEnd: () => handleSessionEnd,
36022
36302
  handleSessionStart: () => handleSessionStart,
36303
+ handleSubagentStart: () => handleSubagentStart,
36304
+ handleSubagentStop: () => handleSubagentStop,
36305
+ handleSystemNotification: () => handleSystemNotification,
36023
36306
  handleToolComplete: () => handleToolComplete,
36024
36307
  handleToolStart: () => handleToolStart,
36025
36308
  handleWorkPromptSubmit: () => handleWorkPromptSubmit,
@@ -44975,8 +45258,8 @@ var init_checksum = __esm({
44975
45258
  });
44976
45259
 
44977
45260
  // packages/core/src/migration/logger.ts
44978
- var logger_exports = {};
44979
- __export(logger_exports, {
45261
+ var logger_exports2 = {};
45262
+ __export(logger_exports2, {
44980
45263
  MigrationLogger: () => MigrationLogger,
44981
45264
  createMigrationLogger: () => createMigrationLogger,
44982
45265
  getLatestMigrationLog: () => getLatestMigrationLog,
@@ -47929,6 +48212,903 @@ var init_transfer = __esm({
47929
48212
  }
47930
48213
  });
47931
48214
 
48215
+ // packages/core/src/task-work/index.ts
48216
+ var task_work_exports = {};
48217
+ __export(task_work_exports, {
48218
+ currentTask: () => currentTask,
48219
+ getTaskHistory: () => getTaskHistory,
48220
+ getWorkHistory: () => getWorkHistory,
48221
+ startTask: () => startTask,
48222
+ stopTask: () => stopTask
48223
+ });
48224
+ async function currentTask(cwd, accessor) {
48225
+ const acc = accessor ?? await getAccessor(cwd);
48226
+ const focus = await acc.getMetaValue("focus_state");
48227
+ return {
48228
+ currentTask: focus?.currentTask ?? null,
48229
+ currentPhase: focus?.currentPhase ?? null,
48230
+ sessionNote: focus?.sessionNote ?? null,
48231
+ nextAction: focus?.nextAction ?? null
48232
+ };
48233
+ }
48234
+ async function startTask(taskId, cwd, accessor) {
48235
+ if (!taskId) {
48236
+ throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
48237
+ }
48238
+ const acc = accessor ?? await getAccessor(cwd);
48239
+ const task = await acc.loadSingleTask(taskId);
48240
+ if (!task) {
48241
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
48242
+ fix: `Use 'cleo find "${taskId}"' to search`
48243
+ });
48244
+ }
48245
+ const { tasks: allTasks } = await acc.queryTasks({});
48246
+ const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
48247
+ if (unresolvedDeps.length > 0) {
48248
+ throw new CleoError(
48249
+ 5 /* DEPENDENCY_ERROR */,
48250
+ `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
48251
+ {
48252
+ fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
48253
+ }
48254
+ );
48255
+ }
48256
+ const focus = await acc.getMetaValue("focus_state") ?? {};
48257
+ const previousTask = focus.currentTask ?? null;
48258
+ focus.currentTask = taskId;
48259
+ focus.currentPhase = task.phase ?? null;
48260
+ const noteEntry = {
48261
+ note: `Started work on ${taskId}: ${task.title}`,
48262
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
48263
+ };
48264
+ if (!focus.sessionNotes) {
48265
+ focus.sessionNotes = [];
48266
+ }
48267
+ focus.sessionNotes.push(noteEntry);
48268
+ await acc.setMetaValue("focus_state", focus);
48269
+ await logOperation(
48270
+ "task_start",
48271
+ taskId,
48272
+ {
48273
+ previousTask,
48274
+ title: task.title
48275
+ },
48276
+ accessor
48277
+ );
48278
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
48279
+ hooks2.dispatch("PreToolUse", cwd ?? process.cwd(), {
48280
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48281
+ taskId,
48282
+ taskTitle: task.title
48283
+ }).catch(() => {
48284
+ });
48285
+ return {
48286
+ taskId,
48287
+ taskTitle: task.title,
48288
+ previousTask
48289
+ };
48290
+ }
48291
+ async function stopTask(cwd, accessor) {
48292
+ const acc = accessor ?? await getAccessor(cwd);
48293
+ const focus = await acc.getMetaValue("focus_state");
48294
+ const previousTask = focus?.currentTask ?? null;
48295
+ if (!focus) {
48296
+ return { previousTask: null };
48297
+ }
48298
+ const taskId = focus.currentTask;
48299
+ const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
48300
+ focus.currentTask = null;
48301
+ focus.nextAction = null;
48302
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
48303
+ if (taskId && task) {
48304
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
48305
+ hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
48306
+ timestamp: now2,
48307
+ taskId,
48308
+ taskTitle: task.title,
48309
+ status: "done"
48310
+ }).catch(() => {
48311
+ });
48312
+ }
48313
+ await acc.setMetaValue("focus_state", focus);
48314
+ await logOperation(
48315
+ "task_stop",
48316
+ previousTask ?? "none",
48317
+ {
48318
+ previousTask
48319
+ },
48320
+ accessor
48321
+ );
48322
+ return { previousTask };
48323
+ }
48324
+ async function getWorkHistory(cwd, accessor) {
48325
+ const acc = accessor ?? await getAccessor(cwd);
48326
+ const focus = await acc.getMetaValue("focus_state");
48327
+ const notes = focus?.sessionNotes ?? [];
48328
+ const history = [];
48329
+ for (const note of notes) {
48330
+ const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
48331
+ if (match) {
48332
+ history.push({
48333
+ taskId: match[1],
48334
+ timestamp: note.timestamp
48335
+ });
48336
+ }
48337
+ }
48338
+ return history.reverse();
48339
+ }
48340
+ var getTaskHistory;
48341
+ var init_task_work = __esm({
48342
+ "packages/core/src/task-work/index.ts"() {
48343
+ "use strict";
48344
+ init_src();
48345
+ init_errors3();
48346
+ init_data_accessor();
48347
+ init_add();
48348
+ init_dependency_check();
48349
+ init_handlers();
48350
+ getTaskHistory = getWorkHistory;
48351
+ }
48352
+ });
48353
+
48354
+ // packages/core/src/tasks/complete.ts
48355
+ var complete_exports = {};
48356
+ __export(complete_exports, {
48357
+ completeTask: () => completeTask
48358
+ });
48359
+ function isVerificationGate(value) {
48360
+ return VERIFICATION_GATES.has(value);
48361
+ }
48362
+ async function loadCompletionEnforcement(cwd) {
48363
+ const isTest = !!process.env.VITEST;
48364
+ const config2 = await loadConfig(cwd);
48365
+ const acceptance = config2.enforcement?.acceptance;
48366
+ const verificationCfg = config2.verification;
48367
+ const acceptanceMode = acceptance?.mode ?? (isTest ? "off" : "block");
48368
+ const acceptanceRequiredForPriorities = acceptance?.requiredForPriorities ?? (isTest ? [] : ["critical", "high", "medium", "low"]);
48369
+ const rawVerificationEnabled = await getRawConfigValue("verification.enabled", cwd);
48370
+ const verificationEnabled = rawVerificationEnabled !== void 0 ? rawVerificationEnabled : !isTest;
48371
+ const verificationRequiredGates = (verificationCfg?.requiredGates ?? []).filter(isVerificationGate).length > 0 ? (verificationCfg?.requiredGates ?? []).filter(isVerificationGate) : DEFAULT_VERIFICATION_REQUIRED_GATES;
48372
+ const verificationMaxRounds = verificationCfg?.maxRounds ?? 5;
48373
+ const lifecycleMode = config2.lifecycle?.mode ?? (isTest ? "off" : "strict");
48374
+ return {
48375
+ acceptanceMode,
48376
+ acceptanceRequiredForPriorities,
48377
+ verificationEnabled,
48378
+ verificationRequiredGates,
48379
+ verificationMaxRounds,
48380
+ lifecycleMode
48381
+ };
48382
+ }
48383
+ async function completeTask(options, cwd, accessor) {
48384
+ const acc = accessor ?? await getAccessor(cwd);
48385
+ const task = await acc.loadSingleTask(options.taskId);
48386
+ if (!task) {
48387
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
48388
+ fix: `Use 'cleo find "${options.taskId}"' to search`
48389
+ });
48390
+ }
48391
+ await requireActiveSession("tasks.complete", cwd);
48392
+ const enforcement = await loadCompletionEnforcement(cwd);
48393
+ if (task.status === "done") {
48394
+ throw new CleoError(17 /* TASK_COMPLETED */, `Task ${options.taskId} is already completed`);
48395
+ }
48396
+ if (task.depends?.length) {
48397
+ const deps = await acc.loadTasks(task.depends);
48398
+ const incompleteDeps = deps.filter((d) => d.status !== "done" && d.status !== "cancelled").map((d) => d.id);
48399
+ if (incompleteDeps.length > 0) {
48400
+ throw new CleoError(
48401
+ 5 /* DEPENDENCY_ERROR */,
48402
+ `Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
48403
+ {
48404
+ fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}`
48405
+ }
48406
+ );
48407
+ }
48408
+ }
48409
+ const acceptanceEnforcement = await createAcceptanceEnforcement(cwd);
48410
+ const completionValidation = acceptanceEnforcement.validateCompletion(task);
48411
+ if (!completionValidation.valid) {
48412
+ throw new CleoError(
48413
+ completionValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
48414
+ completionValidation.error,
48415
+ { fix: completionValidation.fix }
48416
+ );
48417
+ }
48418
+ if (enforcement.verificationEnabled && task.type !== "epic") {
48419
+ if (!task.verification) {
48420
+ throw new CleoError(
48421
+ 40 /* VERIFICATION_INIT_FAILED */,
48422
+ `Task ${options.taskId} is missing verification metadata`,
48423
+ {
48424
+ fix: `Initialize verification for ${options.taskId} before completion`
48425
+ }
48426
+ );
48427
+ }
48428
+ if (task.verification.round > enforcement.verificationMaxRounds) {
48429
+ throw new CleoError(
48430
+ 44 /* MAX_ROUNDS_EXCEEDED */,
48431
+ `Task ${options.taskId} exceeded verification max rounds (${enforcement.verificationMaxRounds})`,
48432
+ {
48433
+ fix: `Review failure log and resolve blockers before retrying completion`
48434
+ }
48435
+ );
48436
+ }
48437
+ const missingRequiredGates = enforcement.verificationRequiredGates.filter(
48438
+ (gate) => task.verification?.gates?.[gate] !== true
48439
+ );
48440
+ if (missingRequiredGates.length > 0 || task.verification.passed !== true) {
48441
+ const exitCode = enforcement.lifecycleMode === "strict" ? 80 /* LIFECYCLE_GATE_FAILED */ : 45 /* GATE_DEPENDENCY */;
48442
+ throw new CleoError(
48443
+ exitCode,
48444
+ `Task ${options.taskId} failed verification gates: ${missingRequiredGates.join(", ") || "verification.passed=false"}`,
48445
+ {
48446
+ fix: `Set required verification gates before completion: ${enforcement.verificationRequiredGates.join(", ")}`
48447
+ }
48448
+ );
48449
+ }
48450
+ }
48451
+ const children = await acc.getChildren(options.taskId);
48452
+ const incompleteChildren = children.filter(
48453
+ (c) => c.status !== "done" && c.status !== "cancelled"
48454
+ );
48455
+ if (incompleteChildren.length > 0 && task.type === "epic") {
48456
+ if (!task.noAutoComplete) {
48457
+ throw new CleoError(
48458
+ 16 /* HAS_CHILDREN */,
48459
+ `Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
48460
+ {
48461
+ fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'`
48462
+ }
48463
+ );
48464
+ }
48465
+ }
48466
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
48467
+ const before = { ...task };
48468
+ task.status = "done";
48469
+ task.completedAt = now2;
48470
+ task.updatedAt = now2;
48471
+ if (options.notes) {
48472
+ const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
48473
+ if (!task.notes) task.notes = [];
48474
+ task.notes.push(timestampedNote);
48475
+ }
48476
+ if (options.changeset) {
48477
+ if (!task.notes) task.notes = [];
48478
+ task.notes.push(`Changeset: ${options.changeset}`);
48479
+ }
48480
+ const autoCompleted = [];
48481
+ const autoCompletedTasks = [];
48482
+ if (task.parentId) {
48483
+ const parent = await acc.loadSingleTask(task.parentId);
48484
+ if (parent && parent.type === "epic" && !parent.noAutoComplete) {
48485
+ const siblings = await acc.getChildren(parent.id);
48486
+ const allDone = siblings.every(
48487
+ (c) => c.id === task.id || c.status === "done" || c.status === "cancelled"
48488
+ );
48489
+ if (allDone) {
48490
+ parent.status = "done";
48491
+ parent.completedAt = now2;
48492
+ parent.updatedAt = now2;
48493
+ autoCompleted.push(parent.id);
48494
+ autoCompletedTasks.push(parent);
48495
+ }
48496
+ }
48497
+ }
48498
+ await acc.transaction(async (tx) => {
48499
+ await tx.upsertSingleTask(task);
48500
+ for (const parentTask of autoCompletedTasks) {
48501
+ await tx.upsertSingleTask(parentTask);
48502
+ }
48503
+ await tx.appendLog({
48504
+ id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
48505
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48506
+ action: "task_completed",
48507
+ taskId: options.taskId,
48508
+ actor: "system",
48509
+ details: { title: task.title, previousStatus: before.status },
48510
+ before: null,
48511
+ after: { title: task.title, previousStatus: before.status }
48512
+ });
48513
+ });
48514
+ const dependents = await acc.getDependents(options.taskId);
48515
+ const unblockedTasks = [];
48516
+ for (const dep of dependents) {
48517
+ if (dep.status === "done" || dep.status === "cancelled") continue;
48518
+ if (dep.depends?.length) {
48519
+ const depDeps = await acc.loadTasks(dep.depends);
48520
+ const stillUnresolved = depDeps.filter(
48521
+ (d) => d.id !== options.taskId && d.status !== "done" && d.status !== "cancelled"
48522
+ );
48523
+ if (stillUnresolved.length === 0) {
48524
+ unblockedTasks.push({ id: dep.id, title: dep.title });
48525
+ }
48526
+ } else {
48527
+ unblockedTasks.push({ id: dep.id, title: dep.title });
48528
+ }
48529
+ }
48530
+ Promise.resolve().then(() => (init_auto_extract(), auto_extract_exports)).then(
48531
+ ({ extractTaskCompletionMemory: extractTaskCompletionMemory2 }) => extractTaskCompletionMemory2(cwd ?? process.cwd(), task)
48532
+ ).catch(() => {
48533
+ });
48534
+ return {
48535
+ task,
48536
+ ...autoCompleted.length > 0 && { autoCompleted },
48537
+ ...unblockedTasks.length > 0 && { unblockedTasks }
48538
+ };
48539
+ }
48540
+ var DEFAULT_VERIFICATION_REQUIRED_GATES, VERIFICATION_GATES;
48541
+ var init_complete = __esm({
48542
+ "packages/core/src/tasks/complete.ts"() {
48543
+ "use strict";
48544
+ init_src();
48545
+ init_config();
48546
+ init_errors3();
48547
+ init_session_enforcement();
48548
+ init_data_accessor();
48549
+ init_enforcement();
48550
+ DEFAULT_VERIFICATION_REQUIRED_GATES = [
48551
+ "implemented",
48552
+ "testsPassed",
48553
+ "qaPassed",
48554
+ "securityPassed",
48555
+ "documented"
48556
+ ];
48557
+ VERIFICATION_GATES = /* @__PURE__ */ new Set([
48558
+ "implemented",
48559
+ "testsPassed",
48560
+ "qaPassed",
48561
+ "cleanupDone",
48562
+ "securityPassed",
48563
+ "documented"
48564
+ ]);
48565
+ }
48566
+ });
48567
+
48568
+ // packages/core/src/tasks/update.ts
48569
+ var update_exports = {};
48570
+ __export(update_exports, {
48571
+ updateTask: () => updateTask
48572
+ });
48573
+ function hasNonStatusDoneFields(options) {
48574
+ return NON_STATUS_DONE_FIELDS.some((field) => options[field] !== void 0);
48575
+ }
48576
+ async function updateTask(options, cwd, accessor) {
48577
+ const acc = accessor ?? await getAccessor(cwd);
48578
+ const task = await acc.loadSingleTask(options.taskId);
48579
+ if (!task) {
48580
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${options.taskId}`, {
48581
+ fix: `Use 'cleo find "${options.taskId}"' to search`
48582
+ });
48583
+ }
48584
+ await requireActiveSession("tasks.update", cwd);
48585
+ const changes = [];
48586
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
48587
+ const isStatusOnlyDoneTransition = options.status === "done" && task.status !== "done" && !hasNonStatusDoneFields(options);
48588
+ if (isStatusOnlyDoneTransition) {
48589
+ const result = await completeTask({ taskId: options.taskId }, cwd, accessor);
48590
+ return { task: result.task, changes: ["status"] };
48591
+ }
48592
+ if (options.status === "done" && task.status !== "done") {
48593
+ throw new CleoError(
48594
+ 6 /* VALIDATION_ERROR */,
48595
+ "status=done must use complete flow; do not combine with other update fields",
48596
+ {
48597
+ fix: `Run 'cleo complete ${options.taskId}' first, then apply additional updates with 'cleo update ${options.taskId} ...'`
48598
+ }
48599
+ );
48600
+ }
48601
+ const enforcement = await createAcceptanceEnforcement(cwd);
48602
+ const updateValidation = enforcement.validateUpdate(task, { acceptance: options.acceptance });
48603
+ if (!updateValidation.valid) {
48604
+ throw new CleoError(
48605
+ updateValidation.exitCode ?? 6 /* VALIDATION_ERROR */,
48606
+ updateValidation.error,
48607
+ { fix: updateValidation.fix }
48608
+ );
48609
+ }
48610
+ if (options.title !== void 0) {
48611
+ validateTitle(options.title);
48612
+ task.title = options.title;
48613
+ changes.push("title");
48614
+ }
48615
+ if (options.status !== void 0) {
48616
+ validateStatus(options.status);
48617
+ const oldStatus = task.status;
48618
+ task.status = options.status;
48619
+ changes.push("status");
48620
+ if (options.status === "done" && oldStatus !== "done") {
48621
+ task.completedAt = now2;
48622
+ }
48623
+ if (options.status === "cancelled" && oldStatus !== "cancelled") {
48624
+ task.cancelledAt = now2;
48625
+ }
48626
+ }
48627
+ if (options.priority !== void 0) {
48628
+ const normalizedPriority = normalizePriority(options.priority);
48629
+ task.priority = normalizedPriority;
48630
+ changes.push("priority");
48631
+ }
48632
+ if (options.type !== void 0) {
48633
+ validateTaskType(options.type);
48634
+ task.type = options.type;
48635
+ changes.push("type");
48636
+ }
48637
+ if (options.size !== void 0) {
48638
+ validateSize(options.size);
48639
+ task.size = options.size;
48640
+ changes.push("size");
48641
+ }
48642
+ if (options.phase !== void 0) {
48643
+ task.phase = options.phase;
48644
+ changes.push("phase");
48645
+ }
48646
+ if (options.description !== void 0) {
48647
+ task.description = options.description;
48648
+ changes.push("description");
48649
+ }
48650
+ if (options.labels !== void 0) {
48651
+ if (options.labels.length) validateLabels(options.labels);
48652
+ task.labels = options.labels;
48653
+ changes.push("labels");
48654
+ }
48655
+ if (options.addLabels?.length) {
48656
+ validateLabels(options.addLabels);
48657
+ const existing = new Set(task.labels ?? []);
48658
+ for (const l of options.addLabels) existing.add(l.trim());
48659
+ task.labels = [...existing];
48660
+ changes.push("labels");
48661
+ }
48662
+ if (options.removeLabels?.length) {
48663
+ const toRemove = new Set(options.removeLabels.map((l) => l.trim()));
48664
+ task.labels = (task.labels ?? []).filter((l) => !toRemove.has(l));
48665
+ changes.push("labels");
48666
+ }
48667
+ if (options.depends !== void 0) {
48668
+ task.depends = options.depends;
48669
+ changes.push("depends");
48670
+ }
48671
+ if (options.addDepends?.length) {
48672
+ const existing = new Set(task.depends ?? []);
48673
+ for (const d of options.addDepends) existing.add(d.trim());
48674
+ task.depends = [...existing];
48675
+ changes.push("depends");
48676
+ }
48677
+ if (options.removeDepends?.length) {
48678
+ const toRemove = new Set(options.removeDepends.map((d) => d.trim()));
48679
+ task.depends = (task.depends ?? []).filter((d) => !toRemove.has(d));
48680
+ changes.push("depends");
48681
+ }
48682
+ if (options.notes !== void 0) {
48683
+ const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
48684
+ if (!task.notes) task.notes = [];
48685
+ task.notes.push(timestampedNote);
48686
+ changes.push("notes");
48687
+ }
48688
+ if (options.acceptance !== void 0) {
48689
+ task.acceptance = options.acceptance;
48690
+ changes.push("acceptance");
48691
+ }
48692
+ if (options.files !== void 0) {
48693
+ task.files = options.files;
48694
+ changes.push("files");
48695
+ }
48696
+ if (options.blockedBy !== void 0) {
48697
+ task.blockedBy = options.blockedBy;
48698
+ changes.push("blockedBy");
48699
+ }
48700
+ if (options.noAutoComplete !== void 0) {
48701
+ task.noAutoComplete = options.noAutoComplete;
48702
+ changes.push("noAutoComplete");
48703
+ }
48704
+ if (options.pipelineStage !== void 0) {
48705
+ validatePipelineTransition(task.pipelineStage, options.pipelineStage);
48706
+ if (task.type === "epic" && task.pipelineStage) {
48707
+ await validateEpicStageAdvancement(
48708
+ {
48709
+ epicId: task.id,
48710
+ currentStage: task.pipelineStage,
48711
+ newStage: options.pipelineStage
48712
+ },
48713
+ acc,
48714
+ cwd
48715
+ );
48716
+ }
48717
+ if (task.type !== "epic") {
48718
+ const epicAncestor = task.parentId ? await findEpicAncestor(task.parentId, acc) : null;
48719
+ const directParent = task.parentId ? await acc.loadSingleTask(task.parentId) : null;
48720
+ const epicToCheck = directParent?.type === "epic" ? directParent : epicAncestor;
48721
+ if (epicToCheck) {
48722
+ await validateChildStageCeiling(
48723
+ { childStage: options.pipelineStage, epicId: epicToCheck.id },
48724
+ acc,
48725
+ cwd
48726
+ );
48727
+ }
48728
+ }
48729
+ task.pipelineStage = options.pipelineStage;
48730
+ changes.push("pipelineStage");
48731
+ }
48732
+ if (options.parentId !== void 0) {
48733
+ const newParentId = options.parentId || null;
48734
+ const currentParentId = task.parentId ?? null;
48735
+ if (newParentId !== currentParentId) {
48736
+ const originalType = task.type;
48737
+ if (!newParentId) {
48738
+ task.parentId = null;
48739
+ if (task.type === "subtask") task.type = "task";
48740
+ changes.push("parentId");
48741
+ if (task.type !== originalType) changes.push("type");
48742
+ } else {
48743
+ const newParent = await acc.loadSingleTask(newParentId);
48744
+ if (!newParent) {
48745
+ throw new CleoError(10 /* PARENT_NOT_FOUND */, `Parent task ${newParentId} not found`);
48746
+ }
48747
+ if (newParent.type === "subtask") {
48748
+ throw new CleoError(
48749
+ 13 /* INVALID_PARENT_TYPE */,
48750
+ `Cannot parent under subtask '${newParentId}'`
48751
+ );
48752
+ }
48753
+ const subtree = await acc.getSubtree(options.taskId);
48754
+ if (subtree.some((t) => t.id === newParentId)) {
48755
+ throw new CleoError(
48756
+ 14 /* CIRCULAR_REFERENCE */,
48757
+ `Moving '${options.taskId}' under '${newParentId}' would create a circular reference`
48758
+ );
48759
+ }
48760
+ const ancestors = await acc.getAncestorChain(newParentId);
48761
+ const parentDepth = ancestors.length;
48762
+ const config2 = await loadConfig(cwd);
48763
+ const policy = resolveHierarchyPolicy(config2);
48764
+ if (parentDepth + 1 >= policy.maxDepth) {
48765
+ throw new CleoError(
48766
+ 11 /* DEPTH_EXCEEDED */,
48767
+ `Maximum nesting depth ${policy.maxDepth} would be exceeded`
48768
+ );
48769
+ }
48770
+ task.parentId = newParentId;
48771
+ const newDepth = parentDepth + 1;
48772
+ if (newDepth === 1) task.type = "task";
48773
+ else if (newDepth >= 2) task.type = "subtask";
48774
+ changes.push("parentId");
48775
+ if (task.type !== originalType) changes.push("type");
48776
+ }
48777
+ }
48778
+ }
48779
+ if (changes.length === 0) {
48780
+ throw new CleoError(102 /* NO_CHANGE */, "No changes specified");
48781
+ }
48782
+ task.updatedAt = now2;
48783
+ await acc.transaction(async (tx) => {
48784
+ await tx.upsertSingleTask(task);
48785
+ await tx.appendLog({
48786
+ id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
48787
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48788
+ action: "task_updated",
48789
+ taskId: options.taskId,
48790
+ actor: "system",
48791
+ details: { changes, title: task.title },
48792
+ before: null,
48793
+ after: { changes, title: task.title }
48794
+ });
48795
+ });
48796
+ return { task, changes };
48797
+ }
48798
+ var NON_STATUS_DONE_FIELDS;
48799
+ var init_update2 = __esm({
48800
+ "packages/core/src/tasks/update.ts"() {
48801
+ "use strict";
48802
+ init_src();
48803
+ init_config();
48804
+ init_errors3();
48805
+ init_session_enforcement();
48806
+ init_data_accessor();
48807
+ init_add();
48808
+ init_complete();
48809
+ init_enforcement();
48810
+ init_epic_enforcement();
48811
+ init_hierarchy_policy();
48812
+ init_pipeline_stage();
48813
+ NON_STATUS_DONE_FIELDS = [
48814
+ "title",
48815
+ "priority",
48816
+ "type",
48817
+ "size",
48818
+ "phase",
48819
+ "description",
48820
+ "labels",
48821
+ "addLabels",
48822
+ "removeLabels",
48823
+ "depends",
48824
+ "addDepends",
48825
+ "removeDepends",
48826
+ "notes",
48827
+ "acceptance",
48828
+ "files",
48829
+ "blockedBy",
48830
+ "parentId",
48831
+ "noAutoComplete",
48832
+ "pipelineStage"
48833
+ ];
48834
+ }
48835
+ });
48836
+
48837
+ // packages/core/src/nexus/workspace.ts
48838
+ function checkRateLimit(agentId) {
48839
+ const now2 = Date.now();
48840
+ const entry = rateLimitCounters.get(agentId);
48841
+ if (!entry || now2 - entry.windowStart > RATE_LIMIT_WINDOW_MS) {
48842
+ rateLimitCounters.set(agentId, { count: 1, windowStart: now2 });
48843
+ return;
48844
+ }
48845
+ entry.count++;
48846
+ if (entry.count > RATE_LIMIT_MAX_OPS) {
48847
+ throw new CleoError(
48848
+ 1 /* GENERAL_ERROR */,
48849
+ `Agent '${agentId}' exceeded rate limit: ${RATE_LIMIT_MAX_OPS} routing ops per ${RATE_LIMIT_WINDOW_MS / 1e3}s`
48850
+ );
48851
+ }
48852
+ }
48853
+ async function loadProjectACL(projectPath) {
48854
+ try {
48855
+ const { loadConfig: loadConfig4 } = await Promise.resolve().then(() => (init_config(), config_exports));
48856
+ const config2 = await loadConfig4(projectPath);
48857
+ const agents = config2?.authorizedAgents;
48858
+ if (Array.isArray(agents) && agents.length > 0) {
48859
+ return { authorizedAgents: agents };
48860
+ }
48861
+ } catch {
48862
+ }
48863
+ return DEFAULT_ACL;
48864
+ }
48865
+ function isAuthorized(acl, agentId) {
48866
+ if (acl.authorizedAgents.includes("*")) return true;
48867
+ return acl.authorizedAgents.includes(agentId);
48868
+ }
48869
+ function parseDirective(message) {
48870
+ const content = message.content;
48871
+ const verbMatch = content.match(/^\/(\w+)/);
48872
+ if (!verbMatch) return null;
48873
+ const verb = verbMatch[1];
48874
+ const taskRefs = [];
48875
+ const pattern = new RegExp(TASK_REF_PATTERN.source, "g");
48876
+ for (const m of content.matchAll(pattern)) {
48877
+ taskRefs.push(`T${m[1]}`);
48878
+ }
48879
+ const metaRefs = message.metadata?.taskRefs;
48880
+ if (Array.isArray(metaRefs)) {
48881
+ for (const ref of metaRefs) {
48882
+ if (typeof ref === "string" && !taskRefs.includes(ref)) {
48883
+ taskRefs.push(ref);
48884
+ }
48885
+ }
48886
+ }
48887
+ if (taskRefs.length === 0) return null;
48888
+ return {
48889
+ verb,
48890
+ taskRefs,
48891
+ agentId: message.from,
48892
+ messageId: message.id,
48893
+ timestamp: message.timestamp
48894
+ };
48895
+ }
48896
+ async function routeDirective(directive) {
48897
+ checkRateLimit(directive.agentId);
48898
+ const results = [];
48899
+ const operation = VERB_TO_OPERATION[directive.verb];
48900
+ if (!operation) {
48901
+ return results;
48902
+ }
48903
+ const projects = await nexusList();
48904
+ for (const taskRef of directive.taskRefs) {
48905
+ const result = await routeSingleTask(taskRef, directive, operation, projects);
48906
+ results.push(result);
48907
+ }
48908
+ return results;
48909
+ }
48910
+ async function routeSingleTask(taskId, directive, operation, projects) {
48911
+ let targetProject = null;
48912
+ let targetAccessor = null;
48913
+ for (const project of projects) {
48914
+ try {
48915
+ const acc = await getAccessor(project.path);
48916
+ const { tasks: tasks2 } = await acc.queryTasks({});
48917
+ const task = tasks2.find((t) => t.id === taskId);
48918
+ if (task) {
48919
+ targetProject = project;
48920
+ targetAccessor = acc;
48921
+ break;
48922
+ }
48923
+ } catch {
48924
+ }
48925
+ }
48926
+ if (!targetProject || !targetAccessor) {
48927
+ return {
48928
+ success: false,
48929
+ project: "unknown",
48930
+ projectPath: "",
48931
+ taskId,
48932
+ operation,
48933
+ error: `Task ${taskId} not found in any registered project`
48934
+ };
48935
+ }
48936
+ const acl = await loadProjectACL(targetProject.path);
48937
+ if (!isAuthorized(acl, directive.agentId)) {
48938
+ return {
48939
+ success: false,
48940
+ project: targetProject.name,
48941
+ projectPath: targetProject.path,
48942
+ taskId,
48943
+ operation,
48944
+ error: `Agent '${directive.agentId}' not authorized to mutate project '${targetProject.name}'`
48945
+ };
48946
+ }
48947
+ try {
48948
+ await executeOperation(operation, taskId, targetProject.path, targetAccessor, directive);
48949
+ await logRouteAudit(directive, targetProject.name, taskId, operation, true);
48950
+ return {
48951
+ success: true,
48952
+ project: targetProject.name,
48953
+ projectPath: targetProject.path,
48954
+ taskId,
48955
+ operation
48956
+ };
48957
+ } catch (err) {
48958
+ const errorMsg = err instanceof Error ? err.message : String(err);
48959
+ await logRouteAudit(directive, targetProject.name, taskId, operation, false, errorMsg);
48960
+ return {
48961
+ success: false,
48962
+ project: targetProject.name,
48963
+ projectPath: targetProject.path,
48964
+ taskId,
48965
+ operation,
48966
+ error: errorMsg
48967
+ };
48968
+ }
48969
+ }
48970
+ async function executeOperation(operation, taskId, projectPath, accessor, directive) {
48971
+ switch (operation) {
48972
+ case "tasks.start": {
48973
+ const { startTask: startTask3 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
48974
+ await startTask3(taskId, projectPath, accessor);
48975
+ break;
48976
+ }
48977
+ case "tasks.complete": {
48978
+ const { completeTask: completeTask2 } = await Promise.resolve().then(() => (init_complete(), complete_exports));
48979
+ await completeTask2(
48980
+ { taskId, notes: `Completed via Conduit directive from ${directive.agentId}` },
48981
+ projectPath,
48982
+ accessor
48983
+ );
48984
+ break;
48985
+ }
48986
+ case "tasks.stop": {
48987
+ const { stopTask: stopTask3 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
48988
+ await stopTask3(projectPath, accessor);
48989
+ break;
48990
+ }
48991
+ case "tasks.update": {
48992
+ const { updateTask: updateTask3 } = await Promise.resolve().then(() => (init_update2(), update_exports));
48993
+ await updateTask3(
48994
+ { taskId, notes: `Marked blocked via Conduit directive from ${directive.agentId}` },
48995
+ projectPath,
48996
+ accessor
48997
+ );
48998
+ break;
48999
+ }
49000
+ }
49001
+ }
49002
+ async function logRouteAudit(directive, projectName, taskId, operation, success2, error40) {
49003
+ try {
49004
+ const { getLogger: getLogger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
49005
+ const log11 = getLogger2("nexus.route");
49006
+ const level = success2 ? "info" : "warn";
49007
+ log11[level](
49008
+ {
49009
+ directive: directive.verb,
49010
+ agentId: directive.agentId,
49011
+ messageId: directive.messageId,
49012
+ project: projectName,
49013
+ taskId,
49014
+ operation,
49015
+ success: success2,
49016
+ error: error40
49017
+ },
49018
+ `Conduit directive routed: ${directive.verb} ${taskId} \u2192 ${projectName} (${success2 ? "OK" : "FAILED"})`
49019
+ );
49020
+ } catch {
49021
+ }
49022
+ }
49023
+ async function workspaceStatus() {
49024
+ const projects = await nexusList();
49025
+ const summaries = [];
49026
+ const totals = { pending: 0, active: 0, done: 0, total: 0 };
49027
+ for (const project of projects) {
49028
+ try {
49029
+ const acc = await getAccessor(project.path);
49030
+ const { tasks: tasks2 } = await acc.queryTasks({});
49031
+ const counts2 = {
49032
+ pending: tasks2.filter((t) => t.status === "pending").length,
49033
+ active: tasks2.filter((t) => t.status === "active").length,
49034
+ done: tasks2.filter((t) => t.status === "done").length,
49035
+ total: tasks2.length
49036
+ };
49037
+ summaries.push({
49038
+ name: project.name,
49039
+ path: project.path,
49040
+ counts: counts2,
49041
+ health: project.healthStatus,
49042
+ lastSync: project.lastSync
49043
+ });
49044
+ totals.pending += counts2.pending;
49045
+ totals.active += counts2.active;
49046
+ totals.done += counts2.done;
49047
+ totals.total += counts2.total;
49048
+ } catch {
49049
+ summaries.push({
49050
+ name: project.name,
49051
+ path: project.path,
49052
+ counts: { pending: 0, active: 0, done: 0, total: 0 },
49053
+ health: "unreachable",
49054
+ lastSync: project.lastSync
49055
+ });
49056
+ }
49057
+ }
49058
+ return {
49059
+ projectCount: projects.length,
49060
+ projects: summaries,
49061
+ totals,
49062
+ computedAt: (/* @__PURE__ */ new Date()).toISOString()
49063
+ };
49064
+ }
49065
+ async function workspaceAgents() {
49066
+ const projects = await nexusList();
49067
+ const agents = [];
49068
+ for (const project of projects) {
49069
+ try {
49070
+ const { listAgentInstances: listAgentInstances2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports2));
49071
+ const instances = await listAgentInstances2(void 0, project.path);
49072
+ for (const inst of instances) {
49073
+ agents.push({
49074
+ agentId: inst.id,
49075
+ agentType: inst.agentType,
49076
+ status: inst.status,
49077
+ project: project.name,
49078
+ taskId: inst.taskId ?? null,
49079
+ lastHeartbeat: inst.lastHeartbeat
49080
+ });
49081
+ }
49082
+ } catch {
49083
+ }
49084
+ }
49085
+ return agents;
49086
+ }
49087
+ var RATE_LIMIT_WINDOW_MS, RATE_LIMIT_MAX_OPS, rateLimitCounters, DEFAULT_ACL, TASK_REF_PATTERN, VERB_TO_OPERATION;
49088
+ var init_workspace = __esm({
49089
+ "packages/core/src/nexus/workspace.ts"() {
49090
+ "use strict";
49091
+ init_src();
49092
+ init_errors3();
49093
+ init_data_accessor();
49094
+ init_registry3();
49095
+ RATE_LIMIT_WINDOW_MS = 6e4;
49096
+ RATE_LIMIT_MAX_OPS = 100;
49097
+ rateLimitCounters = /* @__PURE__ */ new Map();
49098
+ DEFAULT_ACL = { authorizedAgents: ["*"] };
49099
+ TASK_REF_PATTERN = /\bT(\d+)\b/g;
49100
+ VERB_TO_OPERATION = {
49101
+ claim: "tasks.start",
49102
+ done: "tasks.complete",
49103
+ complete: "tasks.complete",
49104
+ blocked: "tasks.update",
49105
+ // Update status to blocked
49106
+ start: "tasks.start",
49107
+ stop: "tasks.stop"
49108
+ };
49109
+ }
49110
+ });
49111
+
47932
49112
  // packages/core/src/nexus/index.ts
47933
49113
  var nexus_exports = {};
47934
49114
  __export(nexus_exports, {
@@ -47963,6 +49143,7 @@ __export(nexus_exports, {
47963
49143
  nexusSyncAll: () => nexusSyncAll,
47964
49144
  nexusUnregister: () => nexusUnregister,
47965
49145
  orphanDetection: () => orphanDetection,
49146
+ parseDirective: () => parseDirective,
47966
49147
  parseQuery: () => parseQuery,
47967
49148
  permissionLevel: () => permissionLevel,
47968
49149
  previewTransfer: () => previewTransfer,
@@ -47973,10 +49154,13 @@ __export(nexus_exports, {
47973
49154
  resolveCrossDeps: () => resolveCrossDeps,
47974
49155
  resolveProjectPath: () => resolveProjectPath2,
47975
49156
  resolveTask: () => resolveTask,
49157
+ routeDirective: () => routeDirective,
47976
49158
  searchAcrossProjects: () => searchAcrossProjects,
47977
49159
  setPermission: () => setPermission,
47978
49160
  syncGitignore: () => syncGitignore,
47979
- validateSyntax: () => validateSyntax
49161
+ validateSyntax: () => validateSyntax,
49162
+ workspaceAgents: () => workspaceAgents,
49163
+ workspaceStatus: () => workspaceStatus
47980
49164
  });
47981
49165
  var init_nexus = __esm({
47982
49166
  "packages/core/src/nexus/index.ts"() {
@@ -47989,6 +49173,7 @@ var init_nexus = __esm({
47989
49173
  init_registry3();
47990
49174
  init_sharing();
47991
49175
  init_transfer();
49176
+ init_workspace();
47992
49177
  }
47993
49178
  });
47994
49179
 
@@ -49524,485 +50709,6 @@ var init_pipeline2 = __esm({
49524
50709
  }
49525
50710
  });
49526
50711
 
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
50712
  // packages/core/src/reconciliation/reconciliation-engine.ts
50007
50713
  function buildTaskMap(tasks2) {
50008
50714
  const map2 = /* @__PURE__ */ new Map();
@@ -62387,145 +63093,6 @@ var init_system2 = __esm({
62387
63093
  }
62388
63094
  });
62389
63095
 
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
63096
  // packages/core/src/tasks/archive.ts
62530
63097
  async function archiveTasks(options = {}, cwd, accessor) {
62531
63098
  const acc = accessor ?? await getAccessor(cwd);
@@ -67985,7 +68552,7 @@ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
67985
68552
  await acc.upsertSingleSession(restoredSession);
67986
68553
  try {
67987
68554
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
67988
- await hooks2.dispatch("onSessionStart", projectRoot, {
68555
+ await hooks2.dispatch("SessionStart", projectRoot, {
67989
68556
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
67990
68557
  sessionId: restoredSession.id,
67991
68558
  name: restoredSession.name,
@@ -68028,6 +68595,7 @@ var init_cleo = __esm({
68028
68595
  init_permissions();
68029
68596
  init_registry3();
68030
68597
  init_sharing();
68598
+ init_workspace();
68031
68599
  init_orchestration();
68032
68600
  init_reconciliation();
68033
68601
  init_link_store();
@@ -68266,7 +68834,14 @@ var init_cleo = __esm({
68266
68834
  discover: (p) => discoverRelated(p.query, p.method, p.limit),
68267
68835
  search: (p) => searchAcrossProjects(p.pattern, p.project, p.limit),
68268
68836
  setPermission: (p) => setPermission(p.name, p.level),
68269
- sharingStatus: () => getSharingStatus()
68837
+ sharingStatus: () => getSharingStatus(),
68838
+ route: (message) => {
68839
+ const directive = parseDirective(message);
68840
+ if (!directive) return Promise.resolve([]);
68841
+ return routeDirective(directive);
68842
+ },
68843
+ workspaceStatus: () => workspaceStatus(),
68844
+ workspaceAgents: () => workspaceAgents()
68270
68845
  };
68271
68846
  }
68272
68847
  // === Agents ===
@@ -69575,6 +70150,14 @@ var init_protocol_enforcement = __esm({
69575
70150
  });
69576
70151
 
69577
70152
  // packages/core/src/hooks/types.ts
70153
+ import {
70154
+ buildHookMatrix,
70155
+ CANONICAL_HOOK_EVENTS,
70156
+ HOOK_CATEGORIES,
70157
+ supportsHook,
70158
+ toCanonical,
70159
+ toNative
70160
+ } from "@cleocode/caamp";
69578
70161
  import { getCommonHookEvents, getProvidersByHookEvent } from "@cleocode/caamp";
69579
70162
  function isProviderHookEvent(event) {
69580
70163
  return !INTERNAL_HOOK_EVENT_SET.has(event);
@@ -75385,7 +75968,7 @@ async function runUpgrade(options = {}) {
75385
75968
  return { success: false, upToDate: false, dryRun: isDryRun, actions, applied: 0, errors };
75386
75969
  }
75387
75970
  await forceCheckpointBeforeOperation("storage-migration", options.cwd);
75388
- const { MigrationLogger: MigrationLogger2 } = await Promise.resolve().then(() => (init_logger3(), logger_exports));
75971
+ const { MigrationLogger: MigrationLogger2 } = await Promise.resolve().then(() => (init_logger3(), logger_exports2));
75389
75972
  const {
75390
75973
  createMigrationState: createMigrationState2,
75391
75974
  updateMigrationPhase: updateMigrationPhase2,
@@ -82384,6 +82967,30 @@ var init_registry5 = __esm({
82384
82967
  sessionRequired: false,
82385
82968
  requiredParams: []
82386
82969
  },
82970
+ {
82971
+ gateway: "query",
82972
+ domain: "admin",
82973
+ operation: "hooks.matrix",
82974
+ description: "admin.hooks.matrix (query) \u2014 cross-provider hook support matrix using CAAMP canonical taxonomy",
82975
+ tier: 1,
82976
+ idempotent: true,
82977
+ sessionRequired: false,
82978
+ requiredParams: [],
82979
+ params: [
82980
+ {
82981
+ name: "providerIds",
82982
+ type: "string",
82983
+ required: false,
82984
+ description: "Limit matrix to specific provider IDs (default: all mapped providers)"
82985
+ },
82986
+ {
82987
+ name: "detectProvider",
82988
+ type: "boolean",
82989
+ required: false,
82990
+ description: "Detect current runtime provider (default: true)"
82991
+ }
82992
+ ]
82993
+ },
82387
82994
  {
82388
82995
  gateway: "query",
82389
82996
  domain: "admin",
@@ -83208,6 +83815,114 @@ var init_config_engine = __esm({
83208
83815
  }
83209
83816
  });
83210
83817
 
83818
+ // packages/cleo/src/dispatch/engines/hooks-engine.ts
83819
+ var hooks_engine_exports = {};
83820
+ __export(hooks_engine_exports, {
83821
+ queryCommonHooks: () => queryCommonHooks,
83822
+ queryHookProviders: () => queryHookProviders,
83823
+ systemHooksMatrix: () => systemHooksMatrix
83824
+ });
83825
+ async function queryHookProviders(event) {
83826
+ if (!isProviderHookEvent(event)) {
83827
+ return engineSuccess({
83828
+ event,
83829
+ providers: []
83830
+ });
83831
+ }
83832
+ const { getProvidersByHookEvent: getProvidersByHookEvent2 } = await import("@cleocode/caamp");
83833
+ const providers = getProvidersByHookEvent2(event);
83834
+ return engineSuccess({
83835
+ event,
83836
+ providers: providers.map((p) => ({
83837
+ id: p.id,
83838
+ name: p.name,
83839
+ supportedHooks: p.capabilities?.hooks?.supported ?? []
83840
+ }))
83841
+ });
83842
+ }
83843
+ async function queryCommonHooks(providerIds) {
83844
+ const { getCommonHookEvents: getCommonHookEvents2 } = await import("@cleocode/caamp");
83845
+ const commonEvents = getCommonHookEvents2(providerIds);
83846
+ return engineSuccess({
83847
+ providerIds,
83848
+ commonEvents
83849
+ });
83850
+ }
83851
+ async function systemHooksMatrix(params) {
83852
+ try {
83853
+ const { buildHookMatrix: buildHookMatrix2, getProviderSummary, getHookMappingsVersion, detectAllProviders: detectAllProviders3 } = await import("@cleocode/caamp");
83854
+ const caampVersion = getHookMappingsVersion();
83855
+ const raw = buildHookMatrix2(params?.providerIds);
83856
+ const boolMatrix = {};
83857
+ for (const event of raw.events) {
83858
+ boolMatrix[event] = {};
83859
+ for (const providerId of raw.providers) {
83860
+ const mapping = raw.matrix[event]?.[providerId];
83861
+ boolMatrix[event][providerId] = mapping?.supported ?? false;
83862
+ }
83863
+ }
83864
+ const summary = raw.providers.map((providerId) => {
83865
+ const provSummary = getProviderSummary(providerId);
83866
+ if (provSummary) {
83867
+ return {
83868
+ providerId,
83869
+ supportedCount: provSummary.supportedCount,
83870
+ totalCanonical: provSummary.totalCanonical,
83871
+ coverage: provSummary.coverage,
83872
+ supported: provSummary.supported,
83873
+ unsupported: provSummary.unsupported
83874
+ };
83875
+ }
83876
+ const supported = raw.events.filter((ev) => boolMatrix[ev]?.[providerId] === true);
83877
+ const unsupported = raw.events.filter((ev) => boolMatrix[ev]?.[providerId] !== true);
83878
+ const totalCanonical = raw.events.length;
83879
+ const coverage = totalCanonical > 0 ? Math.round(supported.length / totalCanonical * 100) : 0;
83880
+ return {
83881
+ providerId,
83882
+ supportedCount: supported.length,
83883
+ totalCanonical,
83884
+ coverage,
83885
+ supported,
83886
+ unsupported
83887
+ };
83888
+ });
83889
+ let detectedProvider = null;
83890
+ const shouldDetect = params?.detectProvider !== false;
83891
+ if (shouldDetect) {
83892
+ try {
83893
+ const detectionResults = detectAllProviders3();
83894
+ const detected = detectionResults.find((r) => r.installed && r.projectDetected);
83895
+ if (detected) {
83896
+ detectedProvider = detected.provider.id;
83897
+ } else {
83898
+ const anyInstalled = detectionResults.find((r) => r.installed);
83899
+ if (anyInstalled) {
83900
+ detectedProvider = anyInstalled.provider.id;
83901
+ }
83902
+ }
83903
+ } catch {
83904
+ }
83905
+ }
83906
+ return engineSuccess({
83907
+ caampVersion,
83908
+ events: raw.events,
83909
+ providers: raw.providers,
83910
+ matrix: boolMatrix,
83911
+ summary,
83912
+ detectedProvider
83913
+ });
83914
+ } catch (err) {
83915
+ return engineError("E_GENERAL", err.message);
83916
+ }
83917
+ }
83918
+ var init_hooks_engine = __esm({
83919
+ "packages/cleo/src/dispatch/engines/hooks-engine.ts"() {
83920
+ "use strict";
83921
+ init_internal();
83922
+ init_error();
83923
+ }
83924
+ });
83925
+
83211
83926
  // packages/cleo/src/dispatch/engines/init-engine.ts
83212
83927
  async function initProject2(projectRoot, options) {
83213
83928
  try {
@@ -86573,7 +87288,7 @@ async function dispatchFromCli(gateway, domain2, operation, params, outputOpts)
86573
87288
  const dispatcher = getCliDispatcher();
86574
87289
  const projectRoot = getProjectRoot();
86575
87290
  const dispatchStart = Date.now();
86576
- hooks.dispatch("onPromptSubmit", projectRoot, {
87291
+ hooks.dispatch("PromptSubmit", projectRoot, {
86577
87292
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
86578
87293
  gateway,
86579
87294
  domain: domain2,
@@ -86589,7 +87304,7 @@ async function dispatchFromCli(gateway, domain2, operation, params, outputOpts)
86589
87304
  source: "cli",
86590
87305
  requestId: randomUUID10()
86591
87306
  });
86592
- hooks.dispatch("onResponseComplete", projectRoot, {
87307
+ hooks.dispatch("ResponseComplete", projectRoot, {
86593
87308
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
86594
87309
  gateway,
86595
87310
  domain: domain2,
@@ -86646,7 +87361,7 @@ async function dispatchRaw(gateway, domain2, operation, params) {
86646
87361
  const dispatcher = getCliDispatcher();
86647
87362
  const projectRoot = getProjectRoot();
86648
87363
  const dispatchStart = Date.now();
86649
- hooks.dispatch("onPromptSubmit", projectRoot, {
87364
+ hooks.dispatch("PromptSubmit", projectRoot, {
86650
87365
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
86651
87366
  gateway,
86652
87367
  domain: domain2,
@@ -86662,7 +87377,7 @@ async function dispatchRaw(gateway, domain2, operation, params) {
86662
87377
  source: "cli",
86663
87378
  requestId: randomUUID10()
86664
87379
  });
86665
- hooks.dispatch("onResponseComplete", projectRoot, {
87380
+ hooks.dispatch("ResponseComplete", projectRoot, {
86666
87381
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
86667
87382
  gateway,
86668
87383
  domain: domain2,
@@ -88494,6 +89209,7 @@ var init_engine2 = __esm({
88494
89209
  init_internal();
88495
89210
  init_codebase_map_engine();
88496
89211
  init_config_engine();
89212
+ init_hooks_engine();
88497
89213
  init_init_engine();
88498
89214
  init_lifecycle_engine();
88499
89215
  init_memory_engine();
@@ -89022,6 +89738,13 @@ var init_admin2 = __esm({
89022
89738
  const result = await systemSmoke();
89023
89739
  return wrapResult(result, "query", "admin", operation, startTime);
89024
89740
  }
89741
+ case "hooks.matrix": {
89742
+ const result = await systemHooksMatrix({
89743
+ providerIds: params?.providerIds,
89744
+ detectProvider: params?.detectProvider !== false
89745
+ });
89746
+ return wrapResult(result, "query", "admin", operation, startTime);
89747
+ }
89025
89748
  default:
89026
89749
  return unsupportedOp("query", "admin", operation, startTime);
89027
89750
  }
@@ -89506,7 +90229,8 @@ var init_admin2 = __esm({
89506
90229
  "backup",
89507
90230
  "export",
89508
90231
  "map",
89509
- "smoke"
90232
+ "smoke",
90233
+ "hooks.matrix"
89510
90234
  ],
89511
90235
  mutate: [
89512
90236
  "init",
@@ -93208,46 +93932,6 @@ var init_tasks4 = __esm({
93208
93932
  }
93209
93933
  });
93210
93934
 
93211
- // packages/cleo/src/dispatch/engines/hooks-engine.ts
93212
- var hooks_engine_exports = {};
93213
- __export(hooks_engine_exports, {
93214
- queryCommonHooks: () => queryCommonHooks,
93215
- queryHookProviders: () => queryHookProviders
93216
- });
93217
- async function queryHookProviders(event) {
93218
- if (!isProviderHookEvent(event)) {
93219
- return engineSuccess({
93220
- event,
93221
- providers: []
93222
- });
93223
- }
93224
- const { getProvidersByHookEvent: getProvidersByHookEvent2 } = await import("@cleocode/caamp");
93225
- const providers = getProvidersByHookEvent2(event);
93226
- return engineSuccess({
93227
- event,
93228
- providers: providers.map((p) => ({
93229
- id: p.id,
93230
- name: p.name,
93231
- supportedHooks: p.capabilities?.hooks?.supported ?? []
93232
- }))
93233
- });
93234
- }
93235
- async function queryCommonHooks(providerIds) {
93236
- const { getCommonHookEvents: getCommonHookEvents2 } = await import("@cleocode/caamp");
93237
- const commonEvents = getCommonHookEvents2(providerIds);
93238
- return engineSuccess({
93239
- providerIds,
93240
- commonEvents
93241
- });
93242
- }
93243
- var init_hooks_engine = __esm({
93244
- "packages/cleo/src/dispatch/engines/hooks-engine.ts"() {
93245
- "use strict";
93246
- init_internal();
93247
- init_error();
93248
- }
93249
- });
93250
-
93251
93935
  // packages/cleo/src/dispatch/engines/tools-engine.ts
93252
93936
  import {
93253
93937
  buildInjectionContent,
@@ -98936,7 +99620,7 @@ async function main() {
98936
99620
  };
98937
99621
  }
98938
99622
  }
98939
- hooks.dispatch("onPromptSubmit", process.cwd(), {
99623
+ hooks.dispatch("PromptSubmit", process.cwd(), {
98940
99624
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
98941
99625
  gateway: name2,
98942
99626
  domain: domain2,
@@ -98946,7 +99630,7 @@ async function main() {
98946
99630
  });
98947
99631
  const dispatchStart = Date.now();
98948
99632
  let result = await handleMcpToolCall(name2, domain2, operation, params);
98949
- hooks.dispatch("onResponseComplete", process.cwd(), {
99633
+ hooks.dispatch("ResponseComplete", process.cwd(), {
98950
99634
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
98951
99635
  gateway: name2,
98952
99636
  domain: domain2,
@@ -99014,7 +99698,7 @@ async function main() {
99014
99698
  } catch (error40) {
99015
99699
  reqLog.error({ err: error40 }, "Tool call error");
99016
99700
  const errorMessage = error40 instanceof Error ? error40.message : String(error40);
99017
- hooks.dispatch("onError", process.cwd(), {
99701
+ hooks.dispatch("PostToolUseFailure", process.cwd(), {
99018
99702
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
99019
99703
  errorCode: "E_INTERNAL_ERROR",
99020
99704
  message: errorMessage,