@cleocode/core 2026.4.2 → 2026.4.3
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/index.js +218 -6
- package/dist/index.js.map +4 -4
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/tasks/update.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/internal.ts +4 -3
- package/src/sessions/index.ts +12 -7
- package/src/tasks/update.ts +8 -0
package/dist/index.js
CHANGED
|
@@ -22134,7 +22134,7 @@ async function endSession(options = {}, cwd, accessor) {
|
|
|
22134
22134
|
session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
22135
22135
|
const duration3 = Math.floor((Date.now() - new Date(session.startedAt).getTime()) / 1e3);
|
|
22136
22136
|
const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
22137
|
-
hooks2.dispatch("SessionEnd", cwd ?? process.cwd(), {
|
|
22137
|
+
await hooks2.dispatch("SessionEnd", cwd ?? process.cwd(), {
|
|
22138
22138
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22139
22139
|
sessionId: session.id,
|
|
22140
22140
|
duration: duration3,
|
|
@@ -22143,7 +22143,7 @@ async function endSession(options = {}, cwd, accessor) {
|
|
|
22143
22143
|
}).catch(() => {
|
|
22144
22144
|
});
|
|
22145
22145
|
const { bridgeSessionToMemory: bridgeSessionToMemory2 } = await Promise.resolve().then(() => (init_session_memory_bridge(), session_memory_bridge_exports));
|
|
22146
|
-
bridgeSessionToMemory2(cwd ?? process.cwd(), {
|
|
22146
|
+
await bridgeSessionToMemory2(cwd ?? process.cwd(), {
|
|
22147
22147
|
sessionId: session.id,
|
|
22148
22148
|
scope: options.sessionId ? session.scope.type : session.scope.epicId ? `epic:${session.scope.epicId}` : session.scope.type,
|
|
22149
22149
|
tasksCompleted: session.tasksCompleted || [],
|
|
@@ -22156,6 +22156,11 @@ async function endSession(options = {}, cwd, accessor) {
|
|
|
22156
22156
|
}
|
|
22157
22157
|
const acc = accessor ?? await getAccessor(cwd);
|
|
22158
22158
|
await acc.upsertSingleSession(session);
|
|
22159
|
+
try {
|
|
22160
|
+
const { refreshMemoryBridge: refreshMemoryBridge2 } = await Promise.resolve().then(() => (init_memory_bridge(), memory_bridge_exports));
|
|
22161
|
+
await refreshMemoryBridge2(cwd ?? process.cwd());
|
|
22162
|
+
} catch {
|
|
22163
|
+
}
|
|
22159
22164
|
return session;
|
|
22160
22165
|
}
|
|
22161
22166
|
async function sessionStatus(cwd, accessor) {
|
|
@@ -26897,6 +26902,208 @@ var init_complete = __esm({
|
|
|
26897
26902
|
}
|
|
26898
26903
|
});
|
|
26899
26904
|
|
|
26905
|
+
// packages/core/src/validation/validation-rules.ts
|
|
26906
|
+
var validation_rules_exports = {};
|
|
26907
|
+
__export(validation_rules_exports, {
|
|
26908
|
+
hasErrors: () => hasErrors,
|
|
26909
|
+
validateHierarchy: () => validateHierarchy,
|
|
26910
|
+
validateIdUniqueness: () => validateIdUniqueness,
|
|
26911
|
+
validateNewTask: () => validateNewTask,
|
|
26912
|
+
validateNoDuplicateDescription: () => validateNoDuplicateDescription,
|
|
26913
|
+
validateStatusTransition: () => validateStatusTransition,
|
|
26914
|
+
validateTimestamps: () => validateTimestamps,
|
|
26915
|
+
validateTitleDescription: () => validateTitleDescription
|
|
26916
|
+
});
|
|
26917
|
+
function validateTitleDescription(title, description) {
|
|
26918
|
+
const violations = [];
|
|
26919
|
+
if (!title || title.trim().length === 0) {
|
|
26920
|
+
violations.push({
|
|
26921
|
+
rule: "title-required",
|
|
26922
|
+
field: "title",
|
|
26923
|
+
message: "Title is required and cannot be empty",
|
|
26924
|
+
severity: "error"
|
|
26925
|
+
});
|
|
26926
|
+
}
|
|
26927
|
+
if (!description || description.trim().length === 0) {
|
|
26928
|
+
violations.push({
|
|
26929
|
+
rule: "description-required",
|
|
26930
|
+
field: "description",
|
|
26931
|
+
message: "Description is required and cannot be empty",
|
|
26932
|
+
severity: "error"
|
|
26933
|
+
});
|
|
26934
|
+
}
|
|
26935
|
+
if (title && description && title.trim().toLowerCase() === description.trim().toLowerCase()) {
|
|
26936
|
+
violations.push({
|
|
26937
|
+
rule: "title-description-different",
|
|
26938
|
+
field: "description",
|
|
26939
|
+
message: "Title and description must be different",
|
|
26940
|
+
severity: "error"
|
|
26941
|
+
});
|
|
26942
|
+
}
|
|
26943
|
+
return violations;
|
|
26944
|
+
}
|
|
26945
|
+
function validateTimestamps(task) {
|
|
26946
|
+
const violations = [];
|
|
26947
|
+
const now = /* @__PURE__ */ new Date();
|
|
26948
|
+
const threshold = new Date(now.getTime() + 5 * 60 * 1e3);
|
|
26949
|
+
const timestampFields = [
|
|
26950
|
+
["createdAt", task.createdAt],
|
|
26951
|
+
["updatedAt", task.updatedAt],
|
|
26952
|
+
["completedAt", task.completedAt],
|
|
26953
|
+
["cancelledAt", task.cancelledAt]
|
|
26954
|
+
];
|
|
26955
|
+
for (const [field, value] of timestampFields) {
|
|
26956
|
+
if (value) {
|
|
26957
|
+
const date6 = new Date(value);
|
|
26958
|
+
if (Number.isNaN(date6.getTime())) {
|
|
26959
|
+
violations.push({
|
|
26960
|
+
rule: "valid-timestamp",
|
|
26961
|
+
field,
|
|
26962
|
+
message: `Invalid timestamp format: ${value}`,
|
|
26963
|
+
severity: "error"
|
|
26964
|
+
});
|
|
26965
|
+
} else if (date6 > threshold) {
|
|
26966
|
+
violations.push({
|
|
26967
|
+
rule: "no-future-timestamps",
|
|
26968
|
+
field,
|
|
26969
|
+
message: `Timestamp ${value} is in the future`,
|
|
26970
|
+
severity: "error"
|
|
26971
|
+
});
|
|
26972
|
+
}
|
|
26973
|
+
}
|
|
26974
|
+
}
|
|
26975
|
+
return violations;
|
|
26976
|
+
}
|
|
26977
|
+
function validateIdUniqueness(taskId, existingIds) {
|
|
26978
|
+
if (existingIds.has(taskId)) {
|
|
26979
|
+
return [
|
|
26980
|
+
{
|
|
26981
|
+
rule: "unique-id",
|
|
26982
|
+
field: "id",
|
|
26983
|
+
message: `Task ID '${taskId}' already exists`,
|
|
26984
|
+
severity: "error"
|
|
26985
|
+
}
|
|
26986
|
+
];
|
|
26987
|
+
}
|
|
26988
|
+
return [];
|
|
26989
|
+
}
|
|
26990
|
+
function validateNoDuplicateDescription(description, existingDescriptions, _excludeTaskId) {
|
|
26991
|
+
const normalizedNew = description.trim().toLowerCase();
|
|
26992
|
+
for (const existing of existingDescriptions) {
|
|
26993
|
+
if (existing.trim().toLowerCase() === normalizedNew) {
|
|
26994
|
+
return [
|
|
26995
|
+
{
|
|
26996
|
+
rule: "no-duplicate-description",
|
|
26997
|
+
field: "description",
|
|
26998
|
+
message: "A task with this exact description already exists",
|
|
26999
|
+
severity: "warning"
|
|
27000
|
+
}
|
|
27001
|
+
];
|
|
27002
|
+
}
|
|
27003
|
+
}
|
|
27004
|
+
return [];
|
|
27005
|
+
}
|
|
27006
|
+
function validateHierarchy(parentId, tasks2, _taskType, limits) {
|
|
27007
|
+
const violations = [];
|
|
27008
|
+
if (!parentId) {
|
|
27009
|
+
return violations;
|
|
27010
|
+
}
|
|
27011
|
+
const maxDepth = limits?.maxDepth ?? 3;
|
|
27012
|
+
const maxSiblings = limits?.maxSiblings ?? 0;
|
|
27013
|
+
const parent = tasks2.find((t) => t.id === parentId);
|
|
27014
|
+
if (!parent) {
|
|
27015
|
+
violations.push({
|
|
27016
|
+
rule: "parent-exists",
|
|
27017
|
+
field: "parentId",
|
|
27018
|
+
message: `Parent task '${parentId}' not found`,
|
|
27019
|
+
severity: "error"
|
|
27020
|
+
});
|
|
27021
|
+
return violations;
|
|
27022
|
+
}
|
|
27023
|
+
let depth = 1;
|
|
27024
|
+
let current = parent;
|
|
27025
|
+
while (current.parentId) {
|
|
27026
|
+
depth++;
|
|
27027
|
+
const nextParent = tasks2.find((t) => t.id === current.parentId);
|
|
27028
|
+
if (!nextParent) break;
|
|
27029
|
+
current = nextParent;
|
|
27030
|
+
}
|
|
27031
|
+
if (depth > maxDepth - 1) {
|
|
27032
|
+
violations.push({
|
|
27033
|
+
rule: "max-depth",
|
|
27034
|
+
field: "parentId",
|
|
27035
|
+
message: `Maximum hierarchy depth of ${maxDepth} exceeded (epic -> task -> subtask)`,
|
|
27036
|
+
severity: "error"
|
|
27037
|
+
});
|
|
27038
|
+
}
|
|
27039
|
+
const siblingCount = tasks2.filter((t) => t.parentId === parentId).length;
|
|
27040
|
+
if (maxSiblings > 0 && siblingCount >= maxSiblings) {
|
|
27041
|
+
violations.push({
|
|
27042
|
+
rule: "max-siblings",
|
|
27043
|
+
field: "parentId",
|
|
27044
|
+
message: `Parent '${parentId}' already has ${siblingCount} children (max ${maxSiblings})`,
|
|
27045
|
+
severity: "error"
|
|
27046
|
+
});
|
|
27047
|
+
}
|
|
27048
|
+
return violations;
|
|
27049
|
+
}
|
|
27050
|
+
function validateStatusTransition(currentStatus, newStatus) {
|
|
27051
|
+
const validTransitions = {
|
|
27052
|
+
pending: ["active", "blocked", "done", "cancelled"],
|
|
27053
|
+
active: ["pending", "blocked", "done", "cancelled"],
|
|
27054
|
+
blocked: ["pending", "active", "done", "cancelled"],
|
|
27055
|
+
done: ["pending", "active"],
|
|
27056
|
+
// restore (alias: reopen)
|
|
27057
|
+
cancelled: ["pending"]
|
|
27058
|
+
// restore (alias: uncancel)
|
|
27059
|
+
};
|
|
27060
|
+
const allowed = validTransitions[currentStatus];
|
|
27061
|
+
if (!allowed) {
|
|
27062
|
+
return [
|
|
27063
|
+
{
|
|
27064
|
+
rule: "valid-status-transition",
|
|
27065
|
+
field: "status",
|
|
27066
|
+
message: `Unknown current status: '${currentStatus}'`,
|
|
27067
|
+
severity: "error"
|
|
27068
|
+
}
|
|
27069
|
+
];
|
|
27070
|
+
}
|
|
27071
|
+
if (!allowed.includes(newStatus)) {
|
|
27072
|
+
return [
|
|
27073
|
+
{
|
|
27074
|
+
rule: "valid-status-transition",
|
|
27075
|
+
field: "status",
|
|
27076
|
+
message: `Cannot transition from '${currentStatus}' to '${newStatus}'. Valid: ${allowed.join(", ")}`,
|
|
27077
|
+
severity: "error"
|
|
27078
|
+
}
|
|
27079
|
+
];
|
|
27080
|
+
}
|
|
27081
|
+
return [];
|
|
27082
|
+
}
|
|
27083
|
+
function validateNewTask(task, existingIds, existingDescriptions, existingTasks, limits) {
|
|
27084
|
+
const violations = [];
|
|
27085
|
+
violations.push(...validateTitleDescription(task.title, task.description));
|
|
27086
|
+
violations.push(...validateTimestamps(task));
|
|
27087
|
+
if (task.id) {
|
|
27088
|
+
violations.push(...validateIdUniqueness(task.id, existingIds));
|
|
27089
|
+
}
|
|
27090
|
+
if (task.description) {
|
|
27091
|
+
violations.push(...validateNoDuplicateDescription(task.description, existingDescriptions));
|
|
27092
|
+
}
|
|
27093
|
+
if (task.parentId) {
|
|
27094
|
+
violations.push(...validateHierarchy(task.parentId, existingTasks, task.type, limits));
|
|
27095
|
+
}
|
|
27096
|
+
return violations;
|
|
27097
|
+
}
|
|
27098
|
+
function hasErrors(violations) {
|
|
27099
|
+
return violations.some((v) => v.severity === "error");
|
|
27100
|
+
}
|
|
27101
|
+
var init_validation_rules = __esm({
|
|
27102
|
+
"packages/core/src/validation/validation-rules.ts"() {
|
|
27103
|
+
"use strict";
|
|
27104
|
+
}
|
|
27105
|
+
});
|
|
27106
|
+
|
|
26900
27107
|
// packages/core/src/tasks/update.ts
|
|
26901
27108
|
var update_exports = {};
|
|
26902
27109
|
__export(update_exports, {
|
|
@@ -26946,6 +27153,11 @@ async function updateTask(options, cwd, accessor) {
|
|
|
26946
27153
|
}
|
|
26947
27154
|
if (options.status !== void 0) {
|
|
26948
27155
|
validateStatus(options.status);
|
|
27156
|
+
const { validateStatusTransition: validateStatusTransition3 } = await Promise.resolve().then(() => (init_validation_rules(), validation_rules_exports));
|
|
27157
|
+
const transitionViolations = validateStatusTransition3(task.status, options.status);
|
|
27158
|
+
if (transitionViolations.length > 0) {
|
|
27159
|
+
throw new CleoError(6 /* VALIDATION_ERROR */, transitionViolations[0].message);
|
|
27160
|
+
}
|
|
26949
27161
|
const oldStatus = task.status;
|
|
26950
27162
|
task.status = options.status;
|
|
26951
27163
|
changes.push("status");
|
|
@@ -65261,7 +65473,7 @@ __export(validation_exports, {
|
|
|
65261
65473
|
validatePhaseTimestamps: () => validatePhaseTimestamps,
|
|
65262
65474
|
validateSessionNote: () => validateSessionNote,
|
|
65263
65475
|
validateSingleActivePhase: () => validateSingleActivePhase,
|
|
65264
|
-
validateStatusTransition: () =>
|
|
65476
|
+
validateStatusTransition: () => validateStatusTransition2,
|
|
65265
65477
|
validateTask: () => validateTask,
|
|
65266
65478
|
validateTitle: () => validateTitle2
|
|
65267
65479
|
});
|
|
@@ -66031,9 +66243,9 @@ function detectDrift(mode = "full", projectRoot = ".") {
|
|
|
66031
66243
|
}
|
|
66032
66244
|
}
|
|
66033
66245
|
}
|
|
66034
|
-
const
|
|
66246
|
+
const hasErrors2 = issues.some((i) => i.severity === "error");
|
|
66035
66247
|
const hasWarnings = issues.some((i) => i.severity === "warning");
|
|
66036
|
-
const exitCode =
|
|
66248
|
+
const exitCode = hasErrors2 ? 2 : hasWarnings ? 1 : 0;
|
|
66037
66249
|
return { mode, issues, exitCode };
|
|
66038
66250
|
}
|
|
66039
66251
|
function shouldRunDriftDetection(enabled = true, autoCheck = false, command, criticalCommands = []) {
|
|
@@ -66529,7 +66741,7 @@ var STATUS_TRANSITIONS = {
|
|
|
66529
66741
|
cancelled: ["pending"],
|
|
66530
66742
|
archived: []
|
|
66531
66743
|
};
|
|
66532
|
-
function
|
|
66744
|
+
function validateStatusTransition2(oldStatus, newStatus) {
|
|
66533
66745
|
if (oldStatus === newStatus) {
|
|
66534
66746
|
return { valid: true, errors: [], warnings: [] };
|
|
66535
66747
|
}
|