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