@cleocode/cleo 2026.3.72 → 2026.3.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1572 -766
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +1413 -729
- package/dist/mcp/index.js.map +4 -4
- package/package.json +4 -4
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
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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: '
|
|
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
|
|
785
|
-
|
|
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(
|
|
837
|
+
this.handlers.set(resolvedEvent, list);
|
|
788
838
|
return () => {
|
|
789
|
-
const handlers = this.handlers.get(
|
|
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
|
-
*
|
|
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('
|
|
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
|
-
|
|
821
|
-
|
|
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(
|
|
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
|
-
|
|
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: {
|
|
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
|
-
|
|
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("
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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("
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35925
|
-
|
|
35926
|
-
|
|
35927
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
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
|
|
44979
|
-
__export(
|
|
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("
|
|
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(),
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
99701
|
+
hooks.dispatch("PostToolUseFailure", process.cwd(), {
|
|
99018
99702
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
99019
99703
|
errorCode: "E_INTERNAL_ERROR",
|
|
99020
99704
|
message: errorMessage,
|